Split types in dedicated files

This commit is contained in:
Marcello 2022-06-10 16:30:57 +02:00
parent ab99b102a0
commit 5c24b9ff09
4 changed files with 178 additions and 177 deletions

View file

@ -1,7 +1,4 @@
using System.ComponentModel; using Cocona;
using System.Diagnostics;
using System.Text;
using Cocona;
using Spectre.Console; using Spectre.Console;
var app = CoconaLiteApp.Create(); var app = CoconaLiteApp.Create();
@ -54,181 +51,8 @@ static async Task RootCommand(
return; return;
} }
var prompt = PromptConstructor.GetScriptPrompt(files, brief); var prompt = PromptConstructor.GetScriptPrompt(files, brief);
var scripts = AnsiConsole.Prompt(prompt); var scripts = AnsiConsole.Prompt(prompt);
await ScriptExecutor.ExecAsync(scripts, elevated); await ScriptExecutor.ExecAsync(scripts, elevated);
}
static class PromptConstructor
{
const int ScriptListSize = 15;
private static Style SelectionHighlight => new(decoration: Decoration.Bold | Decoration.Underline);
private static string FileStyle(FileInfo info, bool brief)
{
var builder = new StringBuilder();
if (!brief)
{
builder.Append($"[blue]{info.DirectoryName}{Path.DirectorySeparatorChar}[/]");
}
builder
.Append($"[orangered1]{Path.GetFileNameWithoutExtension(info.Name)}[/]")
.Append($"[greenyellow]{info.Extension}[/]");
return builder.ToString();
}
private static string DirectoryStyle(DirectoryInfo info) => $"[blue]{info}[/]";
public static MultiSelectionPrompt<FileInfo> GetScriptPrompt(FileInfo[] files, bool brief)
{
var prompt = new MultiSelectionPrompt<FileInfo>()
.Title("Select a script the scripts to execute:")
.NotRequired()
.PageSize(ScriptListSize)
.InstructionsText("[grey](Press [blue]<space>[/] to toggle a script, [green]<enter>[/] to accept)[/]")
.MoreChoicesText("[grey]Move up and down to reveal more options[/]")
.UseConverter(x => FileStyle(x, brief))
.HighlightStyle(SelectionHighlight)
.AddChoices(files);
return prompt;
}
public static SelectionPrompt<DirectoryInfo> GetDirectoryPrompt(DirectoryInfo[] directories)
{
var prompt = new SelectionPrompt<DirectoryInfo>()
.Title("Select a directory:")
.PageSize(ScriptListSize)
.MoreChoicesText("[grey]Move up and down to reveal more options[/]")
.UseConverter(DirectoryStyle)
.HighlightStyle(SelectionHighlight)
.AddChoices(directories);
return prompt;
}
}
static class ScriptExecutor
{
public static async Task ExecAsync(List<FileInfo> files, bool elevated)
{
await Parallel.ForEachAsync(files, (x, ct) => ExecAsync(x, elevated, ct));
}
public static async ValueTask ExecAsync(FileInfo file, bool elevated, CancellationToken cancellationToken = default)
{
var process = GetExecutableProcessInfo(file, elevated);
if (process is null) return;
try
{
await (Process.Start(process)?.WaitForExitAsync(cancellationToken) ?? Task.CompletedTask);
}
catch (Exception ex) when (ex is Win32Exception or InvalidOperationException or PlatformNotSupportedException)
{
AnsiConsole.Markup($"[red]{ex.Message}[/]");
}
}
private static ProcessStartInfo? GetExecutableProcessInfo(FileInfo file, bool elevated)
{
return file.Extension switch
{
".bat" or ".cmd" => new ProcessStartInfo
{
FileName = "cmd",
Arguments = $"/Q /C .\\{file.Name}",
Verb = elevated ? "runas /user:Administrator" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".ps1" => new ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = $"-ExecutionPolicy Bypass -File .\\{file.Name}",
Verb = elevated ? "runas /user:Administrator" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".sh" => new ProcessStartInfo
{
FileName = "bash",
Arguments = $"-c ./{file.Name}",
Verb = elevated ? "sudo" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".zsh" => new ProcessStartInfo
{
FileName = "zsh",
Arguments = $"-c ./{file.Name}",
Verb = elevated ? "sudo" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".fish" => new ProcessStartInfo
{
FileName = "fish",
Arguments = $"-c ./{file.Name}",
Verb = elevated ? "sudo" : string.Empty,
WorkingDirectory = file.DirectoryName
},
_ => null
};
}
}
readonly struct ScriptFinder
{
static readonly string[] DefaultExtensions = new[] { ".ps1", ".*sh", ".bat", ".cmd" };
public string[] Extensions { get; }
public string RootDirectory { get; }
public int Depth { get; }
private readonly EnumerationOptions _options;
public ScriptFinder(string? extensions, string directory, int depth)
{
Extensions = extensions?.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.ToHashSet()
.Select(x => $".{x.TrimStart('.')}")
.ToArray() ?? DefaultExtensions;
Depth = depth;
RootDirectory = directory;
_options = new EnumerationOptions
{
IgnoreInaccessible = true,
RecurseSubdirectories = Depth > 0,
MaxRecursionDepth = Depth,
};
}
private IEnumerable<FileInfo> GetScriptFilesWithExtension(string extension)
{
try
{
var filenames = Directory.GetFiles(RootDirectory, $"*{extension}", _options);
return filenames.Select(x => new FileInfo(x));
}
catch (UnauthorizedAccessException)
{
return Enumerable.Empty<FileInfo>();
}
}
public FileInfo[] GetScripts() =>
Extensions.Select(GetScriptFilesWithExtension).SelectMany(x => x).ToArray();
public IDictionary<DirectoryInfo, FileInfo[]> GetScriptsByDirectory() =>
Extensions
.Select(GetScriptFilesWithExtension)
.SelectMany(x => x)
.GroupBy(x => x.DirectoryName!)
.OrderBy(x => x.Key)
.ToDictionary(x => new DirectoryInfo(x.Key), x => x.ToArray());
} }

55
src/PromptConstructor.cs Normal file
View file

@ -0,0 +1,55 @@
using System.Text;
using Spectre.Console;
static class PromptConstructor
{
const int ScriptListSize = 15;
private static Style SelectionHighlight => new(decoration: Decoration.Bold | Decoration.Underline);
private static string FileStyle(FileInfo info, bool brief)
{
var builder = new StringBuilder();
if (!brief)
{
builder.Append($"[blue]{info.DirectoryName}{Path.DirectorySeparatorChar}[/]");
}
builder
.Append($"[orangered1]{Path.GetFileNameWithoutExtension(info.Name)}[/]")
.Append($"[greenyellow]{info.Extension}[/]");
return builder.ToString();
}
private static string DirectoryStyle(DirectoryInfo info) => $"[blue]{info}[/]";
public static MultiSelectionPrompt<FileInfo> GetScriptPrompt(FileInfo[] files, bool brief)
{
var prompt = new MultiSelectionPrompt<FileInfo>()
.Title("Select a script the scripts to execute:")
.NotRequired()
.PageSize(ScriptListSize)
.InstructionsText("[grey](Press [blue]<space>[/] to toggle a script, [green]<enter>[/] to accept)[/]")
.MoreChoicesText("[grey]Move up and down to reveal more options[/]")
.UseConverter(x => FileStyle(x, brief))
.HighlightStyle(SelectionHighlight)
.AddChoices(files);
return prompt;
}
public static SelectionPrompt<DirectoryInfo> GetDirectoryPrompt(DirectoryInfo[] directories)
{
var prompt = new SelectionPrompt<DirectoryInfo>()
.Title("Select a directory:")
.PageSize(ScriptListSize)
.MoreChoicesText("[grey]Move up and down to reveal more options[/]")
.UseConverter(DirectoryStyle)
.HighlightStyle(SelectionHighlight)
.AddChoices(directories);
return prompt;
}
}

70
src/ScriptExecutor.cs Normal file
View file

@ -0,0 +1,70 @@
using System.ComponentModel;
using System.Diagnostics;
using Spectre.Console;
static class ScriptExecutor
{
public static async Task ExecAsync(List<FileInfo> files, bool elevated)
{
await Parallel.ForEachAsync(files, (x, ct) => ExecAsync(x, elevated, ct));
}
public static async ValueTask ExecAsync(FileInfo file, bool elevated, CancellationToken cancellationToken = default)
{
var process = GetExecutableProcessInfo(file, elevated);
if (process is null) return;
try
{
await (Process.Start(process)?.WaitForExitAsync(cancellationToken) ?? Task.CompletedTask);
}
catch (Exception ex) when (ex is Win32Exception or InvalidOperationException or PlatformNotSupportedException)
{
AnsiConsole.Markup($"[red]{ex.Message}[/]");
}
}
private static ProcessStartInfo? GetExecutableProcessInfo(FileInfo file, bool elevated)
{
return file.Extension switch
{
".bat" or ".cmd" => new ProcessStartInfo
{
FileName = "cmd",
Arguments = $"/Q /C .\\{file.Name}",
Verb = elevated ? "runas /user:Administrator" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".ps1" => new ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = $"-ExecutionPolicy Bypass -File .\\{file.Name}",
Verb = elevated ? "runas /user:Administrator" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".sh" => new ProcessStartInfo
{
FileName = "bash",
Arguments = $"-c ./{file.Name}",
Verb = elevated ? "sudo" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".zsh" => new ProcessStartInfo
{
FileName = "zsh",
Arguments = $"-c ./{file.Name}",
Verb = elevated ? "sudo" : string.Empty,
WorkingDirectory = file.DirectoryName
},
".fish" => new ProcessStartInfo
{
FileName = "fish",
Arguments = $"-c ./{file.Name}",
Verb = elevated ? "sudo" : string.Empty,
WorkingDirectory = file.DirectoryName
},
_ => null
};
}
}

52
src/ScriptFinder.cs Normal file
View file

@ -0,0 +1,52 @@
using Spectre.Console;
readonly struct ScriptFinder
{
static readonly string[] DefaultExtensions = new[] { ".ps1", ".*sh", ".bat", ".cmd" };
public string[] Extensions { get; }
public string RootDirectory { get; }
public int Depth { get; }
private readonly EnumerationOptions _options;
public ScriptFinder(string? extensions, string directory, int depth)
{
Extensions = extensions?.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.ToHashSet()
.Select(x => $".{x.TrimStart('.')}")
.ToArray() ?? DefaultExtensions;
Depth = depth;
RootDirectory = directory;
_options = new EnumerationOptions
{
IgnoreInaccessible = true,
RecurseSubdirectories = Depth > 0,
MaxRecursionDepth = Depth,
};
}
private IEnumerable<FileInfo> GetScriptFilesWithExtension(string extension)
{
try
{
var filenames = Directory.GetFiles(RootDirectory, $"*{extension}", _options);
return filenames.Select(x => new FileInfo(x));
}
catch (UnauthorizedAccessException)
{
return Enumerable.Empty<FileInfo>();
}
}
public FileInfo[] GetScripts() =>
Extensions.Select(GetScriptFilesWithExtension).SelectMany(x => x).ToArray();
public IDictionary<DirectoryInfo, FileInfo[]> GetScriptsByDirectory() =>
Extensions
.Select(GetScriptFilesWithExtension)
.SelectMany(x => x)
.GroupBy(x => x.DirectoryName!)
.OrderBy(x => x.Key)
.ToDictionary(x => new DirectoryInfo(x.Key), x => x.ToArray());
}