mirror of
https://github.com/devmobasa/wayscriber.git
synced 2026-11-01 07:36:42 +01:00
135 lines
4.0 KiB
C#
135 lines
4.0 KiB
C#
using System.Globalization;
|
|
|
|
namespace Wayscriber.Tools;
|
|
|
|
internal enum SideEffect
|
|
{
|
|
ReadOnly,
|
|
FixtureMutating,
|
|
MachineMutating,
|
|
RemoteMutating,
|
|
ForegroundSensitive,
|
|
}
|
|
|
|
internal sealed record ToolCommand(
|
|
string Area,
|
|
string Name,
|
|
string Description,
|
|
SideEffect SideEffect,
|
|
Func<ToolContext, string[], Task<int>> Handler );
|
|
|
|
internal sealed record ToolContext(
|
|
string RepositoryRoot,
|
|
TextWriter Output,
|
|
TextWriter Error,
|
|
IProcessRunner Processes,
|
|
CancellationToken CancellationToken,
|
|
Func<string, string?>? EnvironmentReader = null )
|
|
{
|
|
public string Path( params string[] parts ) =>
|
|
System.IO.Path.GetFullPath( System.IO.Path.Combine( [RepositoryRoot, .. parts] ) );
|
|
|
|
public string? Environment( string name )
|
|
{
|
|
var value = EnvironmentReader is null
|
|
? System.Environment.GetEnvironmentVariable( name )
|
|
: EnvironmentReader( name );
|
|
|
|
return string.IsNullOrWhiteSpace( value ) ? null : value;
|
|
}
|
|
}
|
|
|
|
internal sealed class ToolException( string message, int exitCode = ExitCodes.Failure ) : Exception( message )
|
|
{
|
|
public int ExitCode { get; } = exitCode;
|
|
}
|
|
|
|
internal static class RepositoryLocator
|
|
{
|
|
public static string Locate( )
|
|
{
|
|
var entryDirectory = AppContext.GetData( "EntryPointFileDirectoryPath" ) as string;
|
|
if ( string.IsNullOrWhiteSpace( entryDirectory ) )
|
|
{
|
|
var entryPath = AppContext.GetData( "EntryPointFilePath" ) as string;
|
|
entryDirectory = entryPath is null ? null : System.IO.Path.GetDirectoryName( entryPath );
|
|
}
|
|
|
|
if ( entryDirectory is null )
|
|
{
|
|
throw new ToolException( "Cannot determine the C# entry-point directory." );
|
|
}
|
|
|
|
var root = System.IO.Path.GetFullPath( System.IO.Path.Combine( entryDirectory, ".." ) );
|
|
if ( !File.Exists( System.IO.Path.Combine( root, RepositoryPaths.CargoManifest ) ) ||
|
|
!File.Exists( System.IO.Path.Combine( root, RepositoryPaths.ToolsDirectory, RepositoryNames.ToolEntryFile ) ) )
|
|
{
|
|
throw new ToolException( $"The entry point is not inside a Wayscriber repository: {entryDirectory}" );
|
|
}
|
|
|
|
return root;
|
|
}
|
|
}
|
|
|
|
internal sealed class Arguments( IEnumerable<string> values )
|
|
{
|
|
private const int OptionTokenCount = 2;
|
|
private readonly List<string> _values = [.. values];
|
|
|
|
public IReadOnlyList<string> Remaining => _values;
|
|
|
|
public bool TakeFlag( string name ) => _values.Remove( name );
|
|
|
|
public string? TakeOption( string name, bool required = false )
|
|
{
|
|
var index = _values.IndexOf( name );
|
|
if ( index < 0 )
|
|
{
|
|
if ( required )
|
|
{
|
|
throw new ToolException( $"{name} requires a value.", ExitCodes.InvalidArguments );
|
|
}
|
|
return null;
|
|
}
|
|
if ( index + 1 >= _values.Count )
|
|
{
|
|
throw new ToolException( $"{name} requires a value.", ExitCodes.InvalidArguments );
|
|
}
|
|
|
|
var result = _values[index + 1];
|
|
_values.RemoveRange( index, OptionTokenCount );
|
|
return result;
|
|
}
|
|
|
|
public string SinglePositional( string usage )
|
|
{
|
|
if ( _values.Count != 1 )
|
|
{
|
|
throw new ToolException( usage, ExitCodes.InvalidArguments );
|
|
}
|
|
return _values[0];
|
|
}
|
|
|
|
public void RequireEmpty( string usage )
|
|
{
|
|
if ( _values.Count != 0 )
|
|
{
|
|
throw new ToolException( $"{usage}\nUnexpected argument: {_values[0]}", ExitCodes.InvalidArguments );
|
|
}
|
|
}
|
|
|
|
public int TakeIntOption( string name, int fallback )
|
|
{
|
|
var text = TakeOption( name );
|
|
if ( text is null )
|
|
{
|
|
return fallback;
|
|
}
|
|
if ( !int.TryParse( text, NumberStyles.None, CultureInfo.InvariantCulture, out var value ) )
|
|
{
|
|
throw new ToolException( $"{name} must be an integer.", ExitCodes.InvalidArguments );
|
|
}
|
|
return value;
|
|
}
|
|
}
|