Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/DotNetPathUtils.Tests/PathEnvironmentHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ public async Task EnsureApplicationXdgConfigDirectoryIsInPath_Constructs_Correct
// Arrange
var appName = "MyCoolApp";
var xdgHome = "/home/user/.config";
var expectedPath = Path.Combine(xdgHome, appName);
var expectedAppName = "." + appName.ToCamelCase();
var expectedPath = Path.Combine(xdgHome, expectedAppName);

_service.GetApplicationName().Returns(appName);
_service.GetXdgConfigHome().Returns(xdgHome);
Expand Down
3 changes: 2 additions & 1 deletion src/DotNetPathUtils/DotNetPathUtils.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand All @@ -19,6 +19,7 @@
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
<PackageReference Include="Xdg.Directories" Version="0.1.2" />
</ItemGroup>
<ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions src/DotNetPathUtils/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
internal static class StringExtensions
{
public static string ToCamelCase(this string str)
{
if (string.IsNullOrEmpty(str))
{
return str;
}

return char.ToLowerInvariant(str[0]) + str.Substring(1);
}
}
2 changes: 1 addition & 1 deletion src/DotNetPathUtils/IEnvironmentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public interface IEnvironmentService
void SetEnvironmentVariable(string variable, string? value, EnvironmentVariableTarget target);
string GetFullPath(string path);
void CreateDirectory(string path);
string? GetApplicationName();
string GetApplicationName();
string GetXdgConfigHome();
void BroadcastEnvironmentChange();
bool IsWindows();
Expand Down
43 changes: 33 additions & 10 deletions src/DotNetPathUtils/PathEnvironmentHelper.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,58 @@
using System.Security;
using Microsoft.Extensions.Logging;

namespace DotNetPathUtils;

public class PathEnvironmentHelper
{
private readonly IEnvironmentService _service;
private readonly string _pathVariableName;
private readonly ILogger<PathEnvironmentHelper>? logger;

public PathEnvironmentHelper(IEnvironmentService service)
: this(service, "PATH") { }
public PathEnvironmentHelper(
IEnvironmentService service,
ILogger<PathEnvironmentHelper>? logger = null
)
: this(service, "PATH")
{
this.logger = logger;
}

internal PathEnvironmentHelper(IEnvironmentService service, string pathVariableName)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
_pathVariableName =
pathVariableName ?? throw new ArgumentNullException(nameof(pathVariableName));
if (string.IsNullOrWhiteSpace(pathVariableName))
throw new ArgumentNullException(nameof(pathVariableName));
{
_service = service ?? throw new ArgumentNullException(nameof(service));
_pathVariableName =
pathVariableName ?? throw new ArgumentNullException(nameof(pathVariableName));
}
}

public PathUpdateResult EnsureApplicationXdgConfigDirectoryIsInPath(
EnvironmentVariableTarget target = EnvironmentVariableTarget.User,
string? appName = null
string? appName = null,
PathUtilsOptions? options = null
)
{
appName ??= _service.GetApplicationName();
if (string.IsNullOrWhiteSpace(appName))
string name = appName ?? _service.GetApplicationName();
if (string.IsNullOrWhiteSpace(name))
return PathUpdateResult.Error;

options ??= PathUtilsOptions.Default;

if (options.DirectoryNameCase == DirectoryNameCase.CamelCase)
{
name = name.ToCamelCase();
}

if (options.PrefixWithPeriod && !name!.StartsWith("."))
name = '.' + name;
string configHome = _service.GetXdgConfigHome();
if (string.IsNullOrWhiteSpace(configHome))
return PathUpdateResult.Error;

string appConfigPath = Path.Combine(configHome, appName);
string appConfigPath = Path.Combine(configHome, name);

return EnsureDirectoryIsInPath(appConfigPath, target);
}
Expand All @@ -49,6 +71,7 @@ public PathUpdateResult EnsureDirectoryIsInPath(
}
catch (Exception ex)
{
logger?.DirectoryCreationFailed(directoryPath, ex.Message);
return PathUpdateResult.Error;
}

Expand Down Expand Up @@ -80,6 +103,7 @@ .. currentPathVariable
string normalizedExisting = _service
.GetFullPath(p)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

return normalizedExisting.Equals(
normalizedDirectoryToAdd,
StringComparison.OrdinalIgnoreCase
Expand Down Expand Up @@ -121,7 +145,6 @@ public PathRemoveResult RemoveApplicationXdgConfigDirectoryFromPath(
EnvironmentVariableTarget target = EnvironmentVariableTarget.User
)
{
// This method should also be updated to use the generic RemoveDirectoryFromPath
string? appName = _service.GetApplicationName();
if (string.IsNullOrWhiteSpace(appName))
return PathRemoveResult.Error;
Expand Down
34 changes: 34 additions & 0 deletions src/DotNetPathUtils/PathEnvironmentHelperLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Microsoft.Extensions.Logging;

namespace DotNetPathUtils;

internal static partial class PathEnvironmentHelperLog
{
[LoggerMessage(1000, LogLevel.Information, "Ensuring app config path '{Path}' is in PATH.")]
public static partial void EnsuringAppConfigPath(this ILogger logger, string path);

[LoggerMessage(1001, LogLevel.Warning, "The path '{Path}' already exists in PATH.")]
public static partial void PathAlreadyExists(this ILogger logger, string path);

[LoggerMessage(1002, LogLevel.Information, "Path '{Path}' was added to PATH.")]
public static partial void PathAdded(this ILogger logger, string path);

[LoggerMessage(1003, LogLevel.Error, "Failed to create directory '{Path}': {Message}")]
public static partial void DirectoryCreationFailed(
this ILogger logger,
string path,
string message
);

[LoggerMessage(1004, LogLevel.Error, "Exception setting environment variable: {Message}")]
public static partial void SetEnvVarFailed(this ILogger logger, string message);

[LoggerMessage(1005, LogLevel.Information, "Removing path '{Path}' from PATH.")]
public static partial void RemovingPath(this ILogger logger, string path);

[LoggerMessage(1006, LogLevel.Warning, "Path '{Path}' not found in PATH.")]
public static partial void PathNotFound(this ILogger logger, string path);

[LoggerMessage(1007, LogLevel.Information, "Path '{Path}' was removed from PATH.")]
public static partial void PathRemoved(this ILogger logger, string path);
}
13 changes: 13 additions & 0 deletions src/DotNetPathUtils/PathUtilsOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace DotNetPathUtils;

public record PathUtilsOptions
{
public bool PrefixWithPeriod { get; } = true;
public DirectoryNameCase DirectoryNameCase { get; }
public static readonly PathUtilsOptions Default = new();
}

public enum DirectoryNameCase
{
CamelCase,
}
6 changes: 5 additions & 1 deletion src/DotNetPathUtils/SystemEnvironmentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ EnvironmentVariableTarget target

public void CreateDirectory(string path) => Directory.CreateDirectory(path);

public string? GetApplicationName() => Assembly.GetEntryAssembly()?.GetName().Name;
public string GetApplicationName() =>
Assembly.GetEntryAssembly()?.GetName().Name
?? throw new InvalidOperationException(
"Unable to determine application name. Ensure the entry assembly is set correctly."
);

public string GetXdgConfigHome() => BaseDirectory.ConfigHome;

Expand Down