refactor: Rewrite everything

Rewrite the library from almost scratch to vastly improve performance
and slightly improve any maintainability

Signed-off-by: Sam Therapy <sam@samtherapy.net>
This commit is contained in:
Sam Therapy 2023-09-16 17:40:16 +02:00
parent fc51fed0dc
commit a61acec08a
Signed by: sam
GPG Key ID: 4D8B07C18F31ACBD
23 changed files with 435 additions and 286 deletions

View File

@ -31,7 +31,7 @@ jobs:
- name: Test
run: dotnet test --no-restore --verbosity normal
publish:
publish-preview:
needs: build
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
runs-on: ubuntu-latest
@ -58,3 +58,33 @@ jobs:
run: dotnet build --configuration Release --no-restore -p:SymbolPackageFormat=symbols.nupkg
- name: Publish the package
run: dotnet nuget push src/Xdg.Directories/bin/Release/*.symbols.nupkg -s https://nuget.pkg.github.com/xdg-net/index.json -k ${{ secrets.GITHUB_TOKEN }}
publish-release:
needs: build
if: ${{ github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/') }}
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v3
- uses: actions/cache@v3
with:
path: ~/.nuget/packages
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget
- name: Install dependencies
run: dotnet restore
- name: Build the package
run: dotnet build --configuration Release --no-restore -p:SymbolPackageFormat=snupkg
- name: Publish the package
run: dotnet nuget push src/Xdg.Directories/bin/Release/*.snupkg -s https://api.nuget.org/v3/index.json -k ${{ secrets.NUGET_KEY }}
env:
NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }}

View File

@ -33,7 +33,7 @@ Inspiration is taken from the [Go implementation](https://github.com/adrg/xdg) f
<details open>
<summary>User Directory</summary>
User directories on Windows use [Known Folders](https://learn.microsoft.com/en-us/windows/win32/shell/known-folders) as a fallback.
User directories on Windows use [Known Folders](https://learn.microsoft.com/en-us/windows/win32/shell/known-folders).
| Environment Variable | Windows | macOS | Linux/FreeBSD |
| --- | --- | --- | --- |
| `XDG_DESKTOP_DIR` | `Desktop` | `$HOME/Desktop` | `$HOME/Desktop` |

View File

@ -1,4 +1,3 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33627.172

View File

@ -1,16 +1,18 @@
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Running
open BenchmarkDotNet.Jobs
open Xdg.Directories;
open BenchmarkDotNet.Configs
open Xdg.Directories
[<MemoryDiagnoser>]
//[<SimpleJob(RuntimeMoniker.Mono)>]
[<SimpleJob(RuntimeMoniker.Net462)>]
[<SimpleJob(RuntimeMoniker.Net70, baseline = true)>]
[<SimpleJob(RuntimeMoniker.NativeAot70)>]
#if WINDOWS
[<SimpleJob(RuntimeMoniker.Net481)>]
#endif
[<SimpleJob(RuntimeMoniker.Net70)>]
// [<SimpleJob(RuntimeMoniker.NativeAot70)>]
type Benchmarks() =
// Base Directory
[<Benchmark>]
[<Benchmark(Baseline=true)>]
member _.DataHome () = BaseDirectory.DataHome
[<Benchmark>]
member _.ConfigHome () = BaseDirectory.ConfigHome
@ -18,7 +20,7 @@ type Benchmarks() =
member _.StateHome () = BaseDirectory.StateHome
[<Benchmark>]
member _.BinHome () = BaseDirectory.BinHome
[<Benchmark>]
[<Benchmark>]
member _.DataDirs () = BaseDirectory.DataDirs
[<Benchmark>]
member _.ConfigDirs () = BaseDirectory.ConfigDirs
@ -53,4 +55,5 @@ type Benchmarks() =
[<Benchmark>]
member _.Fonts () = Other.Fonts
BenchmarkRunner.Run<Benchmarks>() |> ignore
let config = DefaultConfig.Instance.WithOptions(ConfigOptions.DisableOptimizationsValidator)
BenchmarkRunner.Run<Benchmarks>(config) |> ignore

View File

@ -2,29 +2,42 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;net48</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>True</RunAnalyzersDuringLiveAnalysis>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<AnalysisLevel>latest-all</AnalysisLevel>
<TargetFrameworks>net7.0;net462</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>True</RunAnalyzersDuringLiveAnalysis>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<AnalysisLevel>latest-all</AnalysisLevel>
<IsWindows
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">
true
</IsWindows>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0|AnyCPU'">
<PropertyGroup Condition="'$(IsWindows)'=='true'">
<DefineConstants>Windows</DefineConstants>
</PropertyGroup>
<PropertyGroup
Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0|AnyCPU'">
<WarningLevel>5</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0|AnyCPU'">
<PropertyGroup
Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0|AnyCPU'">
<WarningLevel>5</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net462|AnyCPU'">
<PropertyGroup
Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net462|AnyCPU'">
<WarningLevel>5</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net462|AnyCPU'">
<PropertyGroup
Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net462|AnyCPU'">
<WarningLevel>5</WarningLevel>
</PropertyGroup>
@ -40,4 +53,4 @@
<ProjectReference Include="..\Xdg.Directories\Xdg.Directories.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@ -1,5 +1,4 @@
#if NET7_0_OR_GREATER
#pragma warning disable CA1031 // General exceptions needed for handling errors
using System.Runtime.InteropServices;
namespace Xdg.Directories.FFI;
@ -87,4 +86,4 @@ internal static partial class Exports
}
}
}
#endif
#endif

View File

@ -2,7 +2,7 @@ PREFIX ?= /usr/local
LIB ?= lib64
DOTNET ?= dotnet
NETVERSION ?= net7.0
PUBFLAGS ?= --framework $(NETVERSION) -c Release -p PublishAot=true
PUBFLAGS ?= --framework $(NETVERSION) -c Release
ifeq ($(OS),Windows_NT)
OSFLAG += win

View File

@ -14,10 +14,13 @@ internal static partial class Exports
/// </remarks>
/// <param name="p">The .NET Pointer to free</param>
[UnmanagedCallersOnly(EntryPoint = "xdg_free")]
public static void Free(IntPtr p)
=> Marshal.FreeCoTaskMem(p);
public static void Free(IntPtr p) => Marshal.FreeCoTaskMem(p);
internal static IntPtr StringToPtr(string? str)
=> Marshal.StringToCoTaskMemUTF8(str);
/// <summary>
/// Converts a .NET string to a C string
/// </summary>
/// <param name="str">The .NET string</param>
/// <returns>A pointer to a C string</returns>
internal static IntPtr StringToPtr(string? str) => Marshal.StringToCoTaskMemUTF8(str);
}
#endif
#endif

View File

@ -1,8 +1,8 @@
#if NET7_0_OR_GREATER
#pragma warning disable CA1031 // General exceptions needed for handling errors
using System.Runtime.InteropServices;
namespace Xdg.Directories.FFI;
internal static partial class Exports
{
[UnmanagedCallersOnly(EntryPoint = "xdg_user_home")]
@ -18,4 +18,4 @@ internal static partial class Exports
}
}
}
#endif
#endif

View File

@ -1,8 +1,8 @@
#if NET7_0_OR_GREATER
#pragma warning disable CA1031 // General exceptions needed for handling errors
using System.Runtime.InteropServices;
namespace Xdg.Directories.FFI;
internal static partial class Exports
{
[UnmanagedCallersOnly(EntryPoint = "xdg_desktop_dir")]
@ -109,4 +109,4 @@ internal static partial class Exports
}
}
}
#endif
#endif

View File

@ -4,7 +4,7 @@ includedir=${prefix}/include
libdir=${exec_prefix}/lib
Name: xdg
Description: xdg directory library
Description: a dotnet-ffi xdg directory library
Version: 1.0.0
Libs: -L${libdir} -lxdg
Cflags: -I${includedir}

View File

@ -7,116 +7,142 @@ public static class BaseDirectory
public static string DataHome
{
get =>
Helpers.OS(
"XDG_DATA_HOME",
GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
$"{Other.Home}/Library/Application Support",
$"{Other.Home}/.local/share"
)!;
GetEnvironmentVariable("XDG_DATA_HOME")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Library", "Application Support"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, ".local", "share"),
_ => string.Empty
};
}
/// <include file='docs/BaseDirectory.xml' path='docs/ConfigHome'/>
public static string ConfigHome
{
get =>
Helpers.OS(
"XDG_CONFIG_HOME",
GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
$"{Other.Home}/Library/Application Support",
$"{Other.Home}/.config"
)!;
GetEnvironmentVariable("XDG_CONFIG_HOME")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Library", "Application Support"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, ".config"),
_ => string.Empty
};
}
/// <include file='docs/BaseDirectory.xml' path='docs/StateHome'/>
public static string StateHome
{
get =>
Helpers.OS(
"XDG_STATE_HOME",
GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
$"{Other.Home}/Library/Application Support",
$"{Other.Home}/.local/state"
)!;
GetEnvironmentVariable("XDG_STATE_HOME")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Library", "Application Support"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, ".local", "state"),
_ => string.Empty
};
}
/// <include file='docs/BaseDirectory.xml' path='docs/BinHome'/>
public static string? BinHome
public static string BinHome
{
get => Helpers.OS("XDG_BIN_HOME", null, null, $"{Other.Home}/.local/bin")!;
get =>
GetEnvironmentVariable("XDG_BIN_HOME")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => string.Empty,
Helpers.OS.MacOS => string.Empty,
Helpers.OS.UnixLike => Path.Combine(Other.Home, ".local", "bin"),
_ => string.Empty
};
}
/// <include file='docs/BaseDirectory.xml' path='docs/DataDirs'/>
public static IList<string> DataDirs
{
get =>
Helpers.OS(
"XDG_DATA_DIRS",
new string[]
{
Helpers.AorB(
GetFolderPath(SpecialFolder.ApplicationData),
GetEnvironmentVariable("APPDATA")!
),
GetEnvironmentVariable("PROGRAMDATA")
?? GetFolderPath(SpecialFolder.CommonApplicationData)
},
new string[] { "/Library/Application Support" },
new string[] { "/usr/local/share", "/usr/share" }
)!;
GetEnvironmentVariable("XDG_DATA_DIRS")?.Split(':')
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> new string[]
{
GetEnvironmentVariable("APPDATA")
?? GetFolderPath(SpecialFolder.ApplicationData),
GetEnvironmentVariable("PROGRAMDATA")
?? GetFolderPath(SpecialFolder.CommonApplicationData)
},
Helpers.OS.MacOS => new string[] { "/Library/Application Support" },
Helpers.OS.UnixLike => new string[] {"/usr/local/share", "/usr/share"},
_ => Array.Empty<string>()
};
}
/// <include file='docs/BaseDirectory.xml' path='docs/ConfigDirs'/>
public static IList<string> ConfigDirs
{
get =>
Helpers.OS(
"XDG_CONFIG_DIRS",
new string[]
{
GetEnvironmentVariable("PROGRAMDATA")
?? GetFolderPath(SpecialFolder.CommonApplicationData),
Helpers.AorB(
GetFolderPath(SpecialFolder.ApplicationData),
GetEnvironmentVariable("APPDATA")!
)
},
new string[]
{
$"{Other.Home}/Library/Preferences",
"/Library/Application Support",
"/Library/Preferences"
},
new string[] { "/etc/xdg" }
)!;
GetEnvironmentVariable("XDG_CONFIG_DIRS")?.Split(':')
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> new string[]
{
GetEnvironmentVariable("ProgramData")
?? GetFolderPath(SpecialFolder.CommonApplicationData),
GetEnvironmentVariable("APPDATA")
?? GetFolderPath(SpecialFolder.ApplicationData)
},
Helpers.OS.MacOS
=> new string[]
{
Path.Combine(Other.Home, "Library", "Preferences"),
"/Library/Application Support",
"/Library/Preferences"
},
Helpers.OS.UnixLike => new string[] { "/etc/xdg" },
_ => Array.Empty<string>()
};
}
/// <include file='docs/BaseDirectory.xml' path='docs/CacheHome'/>
public static string CacheHome
{
get =>
Helpers.OS(
"XDG_CACHE_HOME",
GetEnvironmentVariable("LOCALAPPDATA") is not null
? $"{GetEnvironmentVariable("LOCALAPPDATA")}\\cache"
: $"{GetFolderPath(SpecialFolder.LocalApplicationData)}\\cache",
$"{Other.Home}/Library/Caches",
$"{Other.Home}/.cache"
)!;
GetEnvironmentVariable("XDG_CACHE_HOME")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> GetEnvironmentVariable("LOCALAPPDATA") is not null
? Path.Combine(GetEnvironmentVariable("LOCALAPPDATA")!, "cache")
: Path.Combine(GetFolderPath(SpecialFolder.LocalApplicationData), "cache"),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Library", "Caches"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, ".cache"),
_ => string.Empty
};
}
/// <include file='docs/BaseDirectory.xml' path='docs/RuntimeDir'/>
public static string RuntimeDir
{
get =>
Helpers.OS(
"XDG_RUNTIME_DIR",
GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
$"{Other.Home}/Library/Application Support",
$"/run/user/{GetEnvironmentVariable("UID")}"
)!;
GetEnvironmentVariable("XDG_RUNTIME_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> GetEnvironmentVariable("LOCALAPPDATA")
?? GetFolderPath(SpecialFolder.LocalApplicationData),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Library", "Application Support"),
Helpers.OS.UnixLike => Path.Combine("/run", "user", GetEnvironmentVariable("UID") ?? "0"),
_ => string.Empty
};
}
}

View File

@ -4,67 +4,45 @@ namespace Xdg.Directories;
static internal class Helpers
{
/// <summary>
/// Return a specified value depending on the running operating system
/// The current operating system
/// </summary>
/// <param name="Env">Environment variable to test initially</param>
/// <param name="Windows">Windows directory</param>
/// <param name="MacOS">macOS Directory</param>
/// <param name="Unix">Linux/BSD Directory</param>
/// <returns><c>Env</c> if set, otherwise variable depending on operating system</returns>
/// <exception cref="NotSupportedException">If running on another operating system</exception>
internal static string? OS(string Env, string? Windows, string? MacOS, string? Unix)
internal enum OS : byte
{
string? env = GetEnvironmentVariable(Env);
if (env is not null)
return env;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Windows;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return MacOS;
#if NETSTANDARD
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
#elif NET
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
#endif
return Unix;
else
throw new NotSupportedException("Platforms not supported by .NET Standard are not supported.");
Windows,
MacOS,
/// <summary>
/// Linux, FreeBSD, etc.
/// </summary>
UnixLike,
/// <summary>
/// Anything not supported by .NET
/// </summary>
Other
}
/// <summary>
/// Return a specified value depending on the running operating system
/// </summary>
/// <param name="Env">Environment variable to test initially</param>
/// <param name="Windows">Windows directories</param>
/// <param name="MacOS">macOS Directories</param>
/// <param name="Unix">Linux/BSD Directories</param>
/// <returns><c>Env</c> if set, otherwise variable depending on operating system</returns>
/// <exception cref="NotSupportedException">If running on another operating system</exception>
internal static IList<string>? OS(string Env, IList<string>? Windows, IList<string>? MacOS, IList<string>? Unix)
/// Get the current operating system
/// </summary>
/// <returns>The current operating system</returns>
internal static OS GetCurrentOperatingSystem()
{
string? env = GetEnvironmentVariable(Env);
if (env is not null)
return env.Split(':')!;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Windows;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return MacOS;
// OperatingSystem.Is is faster but not supported by .NET Standard
#if NETSTANDARD
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return OS.Windows;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return OS.MacOS;
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return OS.UnixLike;
#elif NET
if (OperatingSystem.IsWindows())
return OS.Windows;
else if (OperatingSystem.IsMacOS())
return OS.MacOS;
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
return OS.UnixLike;
#endif
return Unix;
else
throw new NotSupportedException("Platforms not supported by .NET Standard are not supported.");
return OS.Other;
}
/// <summary>
/// Returns B if and only if A is empty.
/// </summary>
/// <param name="A">Value to test</param>
/// <param name="B">Alternative if A is null or empty</param>
/// <returns>B if A is null or empty, A if not</returns>
internal static string AorB(string A, string B)
=> A is { Length: 0 } ? B : A;
}

View File

@ -8,14 +8,14 @@ public static class Other
{
get
{
// Unix ?? Windows
string? home =
GetEnvironmentVariable("HOME")
?? GetEnvironmentVariable("USERPROFILE");
if (home is not null)
return home;
else
return GetFolderPath(SpecialFolder.UserProfile);
string? homeEnv = Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetEnvironmentVariable("USERPROFILE"),
Helpers.OS.MacOS => GetEnvironmentVariable("HOME"),
Helpers.OS.UnixLike => GetEnvironmentVariable("HOME"),
_ => null
};
return homeEnv ?? GetFolderPath(SpecialFolder.UserProfile);
}
}
@ -23,56 +23,63 @@ public static class Other
public static IList<string> Applications
{
get =>
Helpers.OS(
"",
new string[]
{
GetFolderPath(SpecialFolder.Programs),
GetFolderPath(SpecialFolder.CommonPrograms)
},
new string[] { "/Applications" },
new string[]
{
$"{BaseDirectory.DataHome}/applications",
$"{Home}/.local/share/applications",
"/usr/local/share/applications",
"/usr/share/applications",
// TODO: Add $XDG_DATA_DIRS/applications
}
)!;
Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> new string[]
{
GetFolderPath(SpecialFolder.Programs),
GetFolderPath(SpecialFolder.CommonPrograms)
},
Helpers.OS.MacOS => new string[] { "/Applications" },
Helpers.OS.UnixLike
=> new string[]
{
$"{BaseDirectory.DataHome}/applications",
$"{Home}/.local/share/applications",
"/usr/local/share/applications",
"/usr/share/applications",
// TODO: Add $XDG_DATA_DIRS/applications
},
_ => Array.Empty<string>()
};
}
/// <include file='docs/Other.xml' path='docs/Fonts'/>
public static IList<string> Fonts
{
get =>
Helpers.OS(
"",
new string[]
{
GetEnvironmentVariable("SystemRoot") is not null
? $"{GetEnvironmentVariable("SystemRoot")}\\Fonts"
: GetFolderPath(SpecialFolder.Fonts),
GetEnvironmentVariable("LOCALAPPDATA") is not null
? $"{GetEnvironmentVariable("LOCALAPPDATA")}\\Microsoft\\Windows\\Fonts"
: $"{GetFolderPath(SpecialFolder.LocalApplicationData)}\\Microsoft\\Windows\\Fonts"
},
new string[]
{
$"{Home}/Library/Fonts",
"/Library/Fonts",
"/System/Library/Fonts",
"/Network/Library/Fonts"
},
new string[]
{
$"{BaseDirectory.DataHome}/fonts",
$"{Home}/.fonts",
$"{Home}/.local/share/fonts",
"/usr/local/share/fonts",
"/usr/share/fonts",
// TODO: Add $XDG_DATA_DIRS/fonts
}
)!;
Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows
=> new string[]
{
GetEnvironmentVariable("SystemRoot") is not null
? $"{GetEnvironmentVariable("SystemRoot")}\\Fonts"
: GetFolderPath(SpecialFolder.Fonts),
GetEnvironmentVariable("LOCALAPPDATA") is not null
? $"{GetEnvironmentVariable("LOCALAPPDATA")}\\Microsoft\\Windows\\Fonts"
: $"{GetFolderPath(SpecialFolder.LocalApplicationData)}\\Microsoft\\Windows\\Fonts"
},
Helpers.OS.MacOS
=> new string[]
{
$"{Home}/Library/Fonts",
"/Library/Fonts",
"/System/Library/Fonts",
"/Network/Library/Fonts"
},
Helpers.OS.UnixLike
=> new string[]
{
Path.Combine(BaseDirectory.DataHome, "fonts"),
Path.Combine(Home, ".fonts"),
Path.Combine(Home, ".local", "share", "fonts"),
"/usr/local/share/fonts",
"/usr/share/fonts",
// TODO: Add $XDG_DATA_DIRS/fonts
},
_ => Array.Empty<string>()
};
}
}

View File

@ -7,95 +7,112 @@ public static class UserDirectory
public static string DesktopDir
{
get =>
Helpers.OS(
"XDG_DESKTOP_DIR",
GetFolderPath(SpecialFolder.Desktop),
$"{Other.Home}/Desktop",
$"{Other.Home}/Desktop"
)!;
GetEnvironmentVariable("XDG_DESKTOP_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetFolderPath(SpecialFolder.Desktop),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Desktop"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, "Desktop"),
_ => string.Empty
};
}
/// <include file='docs/UserDirectory.xml' path='docs/DownloadDir'/>
public static string? DownloadDir
public static string DownloadDir
{
get =>
Helpers.OS(
"XDG_DOWNLOAD_DIR",
null, // TODO: Actually Implement?
$"{Other.Home}/Downloads",
$"{Other.Home}/Downloads"
)!;
GetEnvironmentVariable("XDG_DOWNLOAD_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
// TODO: Implement this ourselves because Microsoft doesn't.
Helpers.OS.Windows => string.Empty,
Helpers.OS.MacOS => Path.Combine(Other.Home, "Downloads"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, "Downloads"),
_ => string.Empty
};
}
/// <include file='docs/UserDirectory.xml' path='docs/DocumentsDir'/>
public static string DocumentsDir
{
get =>
Helpers.OS(
"XDG_DOCUMENTS_DIR",
GetFolderPath(SpecialFolder.MyDocuments),
$"{Other.Home}/Documents",
$"{Other.Home}/Documents"
)!;
GetEnvironmentVariable("XDG_DOCUMENTS_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetFolderPath(SpecialFolder.MyDocuments),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Documents"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, "Documents"),
_ => string.Empty
};
}
/// <include file='docs/UserDirectory.xml' path='docs/MusicDir'/>
public static string MusicDir
{
get =>
Helpers.OS(
"XDG_MUSIC_DIR",
GetFolderPath(SpecialFolder.MyMusic),
$"{Other.Home}/Music",
$"{Other.Home}/Music"
)!;
GetEnvironmentVariable("XDG_MUSIC_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetFolderPath(SpecialFolder.MyMusic),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Music"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, "Music"),
_ => string.Empty
};
}
/// <include file='docs/UserDirectory.xml' path='docs/PicturesDir'/>
public static string PicturesDir
{
get =>
Helpers.OS(
"XDG_PICTURES_DIR",
GetFolderPath(SpecialFolder.MyPictures),
$"{Other.Home}/Pictures",
$"{Other.Home}/Pictures"
)!;
GetEnvironmentVariable("XDG_PICTURES_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetFolderPath(SpecialFolder.MyPictures),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Pictures"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, "Pictures"),
_ => string.Empty
};
}
/// <include file='docs/UserDirectory.xml' path='docs/VideosDir'/>
public static string VideosDir
{
get =>
Helpers.OS(
"XDG_VIDEOS_DIR",
GetFolderPath(SpecialFolder.MyVideos),
$"{Other.Home}/Movies",
$"{Other.Home}/Videos"
)!;
GetEnvironmentVariable("XDG_VIDEOS_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetFolderPath(SpecialFolder.MyVideos),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Movies"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, "Videos"),
_ => string.Empty
};
}
/// <include file='docs/UserDirectory.xml' path='docs/TemplatesDir'/>
public static string TemplatesDir
{
get =>
Helpers.OS(
"XDG_TEMPLATES_DIR",
GetFolderPath(SpecialFolder.Templates),
$"{Other.Home}/Templates",
$"{Other.Home}/Templates"
)!;
GetEnvironmentVariable("XDG_TEMPLATES_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetFolderPath(SpecialFolder.Templates),
Helpers.OS.MacOS => Path.Combine(Other.Home, "Templates"),
Helpers.OS.UnixLike => Path.Combine(Other.Home, "Templates"),
_ => string.Empty
};
}
/// <include file='docs/UserDirectory.xml' path='docs/PublicDir'/>
public static string PublicDir
{
get =>
Helpers.OS(
"XDG_PUBLICSHARE_DIR",
GetEnvironmentVariable("PUBLIC"),
$"{Other.Home}/Public",
$"{Other.Home}/Public"
)!;
GetEnvironmentVariable("XDG_PUBLICSHARE_DIR")
?? Helpers.GetCurrentOperatingSystem() switch
{
Helpers.OS.Windows => GetEnvironmentVariable("PUBLIC") ?? string.Empty,
Helpers.OS.MacOS => Path.Combine(Other.Home, "Public"),
Helpers.OS.UnixLike => $"{Other.Home}/Public",
_ => string.Empty
};
}
}

View File

@ -1 +1 @@
global using static System.Environment;
global using static System.Environment;

View File

@ -24,7 +24,7 @@
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<IncludeSymbols>True</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageTags>xdg;base-directories;base-directory;xdg-base-directory;directory;freedesktop;cross-platform</PackageTags>
@ -74,4 +74,4 @@
<ItemGroup>
<InternalsVisibleTo Include="Xdg.Testing" />
</ItemGroup>
</Project>
</Project>

View File

@ -155,7 +155,7 @@
<item>
<term>Windows</term>
<description>
<c>null</c>
<c>""</c>
<br />
Windows does not support this by default.
</description>
@ -163,7 +163,7 @@
<item>
<term>macOS</term>
<description>
<c>null</c>
<c>""</c>
<br />
macOS does not support this by default.
</description>

View File

@ -12,42 +12,57 @@ public class BaseDirectory_Test
public void DataHome_Windows_Default()
{
Helper.Prepare("XDG_DATA_HOME", null, "Windows");
Assert.AreEqual(BaseDirectory.DataHome, GetFolderPath(SpecialFolder.LocalApplicationData));
Assert.AreEqual(
BaseDirectory.DataHome,
GetFolderPath(SpecialFolder.LocalApplicationData)
);
}
[TestMethod, TestCategory("ConfigHome")]
public void ConfigHome_Windows_Default()
{
Helper.Prepare("XDG_CONFIG_HOME", null, "Windows");
Assert.AreEqual(BaseDirectory.ConfigHome, GetFolderPath(SpecialFolder.LocalApplicationData));
Assert.AreEqual(
BaseDirectory.ConfigHome,
GetFolderPath(SpecialFolder.LocalApplicationData)
);
}
[TestMethod, TestCategory("StateHome")]
public void StateHome_Windows_Default()
{
Helper.Prepare("XDG_STATE_HOME", null, "Windows");
Assert.AreEqual(GetFolderPath(SpecialFolder.LocalApplicationData), BaseDirectory.StateHome);
Assert.AreEqual(
GetFolderPath(SpecialFolder.LocalApplicationData),
BaseDirectory.StateHome
);
}
[TestMethod, TestCategory("BinHome")]
public void BinHome_Windows_Default()
{
Helper.Prepare("XDG_BIN_HOME", null, "Windows");
Assert.IsNull(BaseDirectory.BinHome);
Assert.AreEqual(string.Empty, BaseDirectory.BinHome);
}
[TestMethod, TestCategory("CacheHome")]
public void CacheHome_Windows_Default()
{
Helper.Prepare("XDG_CACHE_HOME", null, "Windows");
Assert.AreEqual($"{GetFolderPath(SpecialFolder.LocalApplicationData)}\\cache", BaseDirectory.CacheHome);
Assert.AreEqual(
$"{GetFolderPath(SpecialFolder.LocalApplicationData)}\\cache",
BaseDirectory.CacheHome
);
}
[TestMethod, TestCategory("RuntimeDir")]
public void RuntimeDir_Windows_Default()
{
Helper.Prepare("XDG_RUNTIME_DIR", null, "Windows");
Assert.AreEqual(GetFolderPath(SpecialFolder.LocalApplicationData), BaseDirectory.RuntimeDir);
Assert.AreEqual(
GetFolderPath(SpecialFolder.LocalApplicationData),
BaseDirectory.RuntimeDir
);
}
[TestMethod, TestCategory("DataDirs")]
@ -55,7 +70,11 @@ public class BaseDirectory_Test
{
Helper.Prepare("XDG_DATA_DIRS", null, "Windows");
CollectionAssert.AreEqual(
new string[] { GetEnvironmentVariable("APPDATA")!, GetEnvironmentVariable("PROGRAMDATA")! },
new string[]
{
GetEnvironmentVariable("APPDATA")!,
GetEnvironmentVariable("PROGRAMDATA")!
},
(System.Collections.ICollection)BaseDirectory.DataDirs
);
}
@ -65,7 +84,11 @@ public class BaseDirectory_Test
{
Helper.Prepare("XDG_CONFIG_DIRS", null, "Windows");
CollectionAssert.AreEqual(
new string[] { GetEnvironmentVariable("PROGRAMDATA")!, GetEnvironmentVariable("APPDATA")! },
new string[]
{
GetEnvironmentVariable("PROGRAMDATA")!,
GetEnvironmentVariable("APPDATA")!
},
(System.Collections.ICollection)BaseDirectory.ConfigDirs
);
}
@ -131,7 +154,12 @@ public class BaseDirectory_Test
{
Helper.Prepare("XDG_CONFIG_DIRS", null, "MacOS");
CollectionAssert.AreEqual(
new string[] { $"{Other.Home}/Library/Preferences", "/Library/Application Support", "/Library/Preferences" },
new string[]
{
$"{Other.Home}/Library/Preferences",
"/Library/Application Support",
"/Library/Preferences"
},
(System.Collections.ICollection)BaseDirectory.ConfigDirs
);
}
@ -179,7 +207,7 @@ public class BaseDirectory_Test
public void RuntimeDir_Linux_Default()
{
Helper.Prepare("XDG_RUNTIME_DIR", null, "Linux");
Assert.AreEqual($"/run/user/{GetEnvironmentVariable("UID")}", BaseDirectory.RuntimeDir);
Assert.AreEqual($"/run/user/{GetEnvironmentVariable("UID") ?? "0"}", BaseDirectory.RuntimeDir);
}
[TestMethod, TestCategory("DataDirs")]
@ -268,4 +296,4 @@ public class BaseDirectory_Test
);
}
}
}
}

View File

@ -1,6 +1,7 @@
using System.Runtime.InteropServices;
namespace Xdg.Testing.Directories;
internal static class Helper
{
public static void Prepare(string Env, string? EnvValue, string OS)

View File

@ -26,14 +26,28 @@ public class Other_Test
public void Applications_Windows_Default()
{
Helper.Prepare("DISCARD", null, "Windows");
CollectionAssert.AreEquivalent(new string[] { GetFolderPath(SpecialFolder.Programs), GetFolderPath(SpecialFolder.CommonPrograms) }, (System.Collections.ICollection)Other.Applications);
CollectionAssert.AreEquivalent(
new string[]
{
GetFolderPath(SpecialFolder.Programs),
GetFolderPath(SpecialFolder.CommonPrograms)
},
(System.Collections.ICollection)Other.Applications
);
}
[TestMethod, TestCategory("Fonts")]
public void Fonts_Windows_Default()
{
Helper.Prepare("DISCARD", null, "Windows");
CollectionAssert.AreEquivalent(new string[] { GetFolderPath(SpecialFolder.Fonts), $"{GetFolderPath(SpecialFolder.LocalApplicationData)}\\Microsoft\\Windows\\Fonts" }, (System.Collections.ICollection)Other.Fonts);
CollectionAssert.AreEquivalent(
new string[]
{
GetFolderPath(SpecialFolder.Fonts),
$"{GetFolderPath(SpecialFolder.LocalApplicationData)}\\Microsoft\\Windows\\Fonts"
},
(System.Collections.ICollection)Other.Fonts
);
}
}
@ -58,14 +72,26 @@ public class Other_Test
public void Applications_MacOS_Default()
{
Helper.Prepare("DISCARD", null, "macOS");
CollectionAssert.AreEquivalent(new string[] { "/Applications" }, (System.Collections.ICollection)Other.Applications);
CollectionAssert.AreEquivalent(
new string[] { "/Applications" },
(System.Collections.ICollection)Other.Applications
);
}
[TestMethod, TestCategory("Fonts")]
public void Fonts_MacOS_Default()
{
Helper.Prepare("DISCARD", null, "macOS");
CollectionAssert.AreEquivalent(new string[] { $"{Other.Home}/Library/Fonts", "/Library/Fonts", "/System/Library/Fonts", "/Network/Library/Fonts" }, (System.Collections.ICollection)Other.Fonts);
CollectionAssert.AreEquivalent(
new string[]
{
$"{Other.Home}/Library/Fonts",
"/Library/Fonts",
"/System/Library/Fonts",
"/Network/Library/Fonts"
},
(System.Collections.ICollection)Other.Fonts
);
}
}
@ -76,7 +102,7 @@ public class Other_Test
public void Home_Linux_Default()
{
Helper.Prepare("DISCARD", null, "Linux");
Assert.AreEqual(GetEnvironmentVariable("HOME"), Other.Home);
Assert.AreEqual(GetFolderPath(SpecialFolder.UserProfile), Other.Home);
}
[TestMethod, TestCategory("Home")]
@ -90,14 +116,33 @@ public class Other_Test
public void Applications_Linux_Default()
{
Helper.Prepare("DISCARD", null, "Linux");
CollectionAssert.AreEquivalent(new string[] { $"{BaseDirectory.DataHome}/applications", $"{Other.Home}/.local/share/applications", "/usr/local/share/applications", "/usr/share/applications", }, (System.Collections.ICollection)Other.Applications);
CollectionAssert.AreEquivalent(
new string[]
{
$"{BaseDirectory.DataHome}/applications",
$"{Other.Home}/.local/share/applications",
"/usr/local/share/applications",
"/usr/share/applications",
},
(System.Collections.ICollection)Other.Applications
);
}
[TestMethod, TestCategory("Fonts")]
public void Fonts_Linux_Default()
{
Helper.Prepare("DISCARD", null, "Linux");
CollectionAssert.AreEquivalent(new string[] { $"{BaseDirectory.DataHome}/fonts", $"{Other.Home}/.fonts", $"{Other.Home}/.local/share/fonts", "/usr/local/share/fonts", "/usr/share/fonts", }, (System.Collections.ICollection)Other.Fonts);
CollectionAssert.AreEquivalent(
new string[]
{
$"{BaseDirectory.DataHome}/fonts",
$"{Other.Home}/.fonts",
$"{Other.Home}/.local/share/fonts",
"/usr/local/share/fonts",
"/usr/share/fonts",
},
(System.Collections.ICollection)Other.Fonts
);
}
}
}

View File

@ -19,7 +19,7 @@ public class UserDirectory_Test
public void DownloadDir_Windows_Default()
{
Helper.Prepare("XDG_DOWNLOAD_DIR", null, "Windows");
Assert.IsNull(UserDirectory.DownloadDir);
Assert.AreEqual(string.Empty, UserDirectory.DownloadDir);
}
[TestMethod, TestCategory("DocumentsDir")]
@ -244,4 +244,4 @@ public class UserDirectory_Test
Assert.AreEqual("/", UserDirectory.PublicDir);
}
}
}
}

View File

@ -1,2 +1,2 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;
global using static System.Environment;
global using static System.Environment;