// Copyright (C) 2025, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Text.RegularExpressions;
namespace ReleaseBuilder;
///
/// The operating systems we can build for
///
public enum OSType
{
///
/// The Windows OS
///
Windows,
///
/// The MacOS
///
MacOS,
///
/// Linux, any variant
///
Linux
}
///
/// The system architecture
///
public enum ArchType
{
///
/// An x86 64-bit architecture
///
x64,
///
/// An x86 32-bit architecture
///
x86,
///
/// The ARM-64 architecture
///
Arm64,
///
/// The ARM v7 architecture
///
Arm7
}
///
/// The different package types
///
public enum PackageType
{
///
/// The basic ZIP package
///
Zip,
///
/// The Windows installer format
///
MSI,
///
/// The debian package format
///
Deb,
///
/// The Redhat package manager format
///
RPM,
///
/// Docker build
///
Docker,
///
/// Apple Disk Image
///
DMG,
///
/// The MacOS pkg format
///
MacPkg,
///
/// The synology Spk format
///
SynologySpk
}
///
/// The interface type
///
public enum InterfaceType
{
///
/// The GUI interface
///
GUI,
///
/// The commandline interface
///
Cli,
///
/// The agent interface
///
Agent
}
///
/// Mapping of a package target
///
/// The operating system
/// The CPU architecture
/// The interface type
/// The installer package
public record PackageTarget(OSType OS, ArchType Arch, InterfaceType Interface, PackageType Package)
{
///
/// Returns a string representation of the OS.
///
/// The operating system
/// The operating system id-string
/// This is using .Net RID: https://learn.microsoft.com/en-us/dotnet/core/rid-catalog
private static string OSToString(OSType os)
=> os switch
{
OSType.Windows => "win",
OSType.Linux => "linux",
OSType.MacOS => "osx",
_ => throw new Exception("Not supported OS")
};
///
/// Returns a string representation of the CPU architecture
///
/// The architecture
/// The CPU architecture id-string
/// This is using .Net RID: https://learn.microsoft.com/en-us/dotnet/core/rid-catalog
private static string ArchToString(ArchType arch)
=> arch switch
{
ArchType.x64 => "x64",
ArchType.x86 => "x86",
ArchType.Arm64 => "arm64",
ArchType.Arm7 => "arm7",
_ => throw new Exception("Not supported arch")
};
///
/// Returns a string representation of the package type
///
/// The package type
/// The package type id-string
private static string PackageToString(PackageType package)
=> package switch
{
PackageType.Zip => "zip",
PackageType.MSI => "msi",
PackageType.Deb => "deb",
PackageType.RPM => "rpm",
PackageType.DMG => "dmg",
PackageType.MacPkg => "pkg",
PackageType.SynologySpk => "spk",
PackageType.Docker => "docker",
_ => throw new Exception("Not supported package type")
};
///
/// Returns a string representation of the interface type
///
/// The interface type
/// The interface type id-string
private static string InterfaceToString(InterfaceType interfaceType)
=> interfaceType switch
{
InterfaceType.GUI => "gui",
InterfaceType.Cli => "cli",
InterfaceType.Agent => "agent",
_ => throw new Exception("Not supported interface type")
};
///
/// Gets the RID string for the operating system
///
public string OSString => OSToString(OS);
///
/// Gets the RID string for the CPU architecture
///
public string ArchString => ArchToString(Arch);
///
/// Gets the id string for the interface
///
public string InterfaceString => InterfaceToString(Interface);
///
/// Gets the id string for the package
///
public string PackageString => PackageToString(Package);
///
/// String map of operating system RIDs
///
private static Dictionary OSTypeParse = Enum.GetValues().ToDictionary(OSToString, x => x, StringComparer.OrdinalIgnoreCase);
///
/// String map of architecture RIDs
///
private static Dictionary ArchTypeParse = Enum.GetValues().ToDictionary(ArchToString, x => x, StringComparer.OrdinalIgnoreCase);
///
/// String map of package type ids
///
private static Dictionary PackageTypeParse = Enum.GetValues().ToDictionary(PackageToString, x => x, StringComparer.OrdinalIgnoreCase);
///
/// String map of interface type ids
///
private static Dictionary InterfaceTypeParse = Enum.GetValues().ToDictionary(InterfaceToString, x => x, StringComparer.OrdinalIgnoreCase);
///
/// The RID string for .Net build commands
///
public string BuildArchString => $"{OSString}-{ArchString}";
///
/// The target string for the builds
///
public string BuildTargetString => $"{BuildArchString}-{InterfaceString}";
///
/// The package string for the updater
///
public string PackageTargetString => $"{BuildTargetString}.{PackageString}";
///
/// Parses a string representation of a package target
///
/// The id to parse
/// The matching the string
public static PackageTarget ParsePackageId(string id)
{
var re = Regex.Match(id, @"(?\w+)-(?\w+)-(?\w+)\.(?\w+)");
if (!re.Success)
throw new Exception($"Invalid package id: {id}");
if (!OSTypeParse.TryGetValue(re.Groups["os"].Value, out var os))
throw new Exception($"Not supported OS type: {re.Groups["os"].Value}");
if (!ArchTypeParse.TryGetValue(re.Groups["arch"].Value, out var arch))
throw new Exception($"Not supported Arch type: {re.Groups["arch"].Value}");
if (!InterfaceTypeParse.TryGetValue(re.Groups["int"].Value, out var interfaceType))
throw new Exception($"Not supported Interface type: {re.Groups["int"].Value}");
if (!PackageTypeParse.TryGetValue(re.Groups["package"].Value, out var package))
throw new Exception($"Not supported Package type: {re.Groups["package"].Value}");
return new PackageTarget(os, arch, interfaceType, package);
}
}