-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b0cfe7c
commit e407f56
Showing
10 changed files
with
437 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,11 @@ | ||
<Project> | ||
<PropertyGroup> | ||
<UseArtifactsOutput>true</UseArtifactsOutput> | ||
</PropertyGroup> | ||
|
||
|
||
<PropertyGroup Label="Assembly Info"> | ||
<Company>iterate GmbH</Company> | ||
<PackageLicenseFile>$(MSBuildThisFileDirectory)LICENSE.md</PackageLicenseFile> | ||
<Version>0.1.0</Version> | ||
</PropertyGroup> | ||
</Project> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
namespace CloudFiles.Troubleshooter; | ||
|
||
public abstract class CommandSettings | ||
{ | ||
private readonly OptionParser _optionParser = new(); | ||
|
||
public bool Confirm { get; private set; } = false; | ||
|
||
public bool WhatIf { get; private set; } = false; | ||
|
||
public CommandSettings() | ||
{ | ||
Register(nameof(Confirm), ParseConfirm); | ||
Register(nameof(WhatIf), ParseWhatIf); | ||
} | ||
|
||
public void Parse(ReadOnlySpan<string> parse) | ||
{ | ||
var remaining = _optionParser.Parse(parse); | ||
ConsumeRemaining(remaining); | ||
} | ||
|
||
protected void Register(string propertyName, OptionHandler handler) | ||
{ | ||
ArgumentNullException.ThrowIfNull(handler); | ||
ArgumentException.ThrowIfNullOrWhiteSpace(propertyName); | ||
|
||
if (!_optionParser.AddOption(propertyName, handler)) | ||
{ | ||
throw new ArgumentException("Key already exists", nameof(propertyName)); | ||
} | ||
} | ||
|
||
protected virtual void ConsumeRemaining(List<string> args) | ||
{ } | ||
|
||
private OptionResult ParseConfirm(OptionType optionType, string? value) | ||
{ | ||
if (optionType != OptionType.Switch) | ||
{ | ||
// OptionType is KeyValue, thus `--Confirm:<Value>`. | ||
ArgumentException.ThrowIfNullOrWhiteSpace(value); | ||
// Let fail parsing of bool, if invalid value provided. | ||
Confirm = bool.Parse(value); | ||
} | ||
else | ||
{ | ||
Confirm = true; | ||
} | ||
|
||
return OptionResult.Continue; | ||
} | ||
|
||
private OptionResult ParseWhatIf(OptionType optionType, string? value) | ||
{ | ||
if (optionType != OptionType.Switch) | ||
{ | ||
// OptionType is KeyValue, thus `--WhatIf:<Value>`. | ||
ArgumentException.ThrowIfNullOrWhiteSpace(value); | ||
// Let parsing of bool fail if bad value provided. | ||
WhatIf = bool.Parse(value); | ||
} | ||
else | ||
{ | ||
WhatIf = true; | ||
} | ||
|
||
return OptionResult.Continue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// <auto-generated /> | ||
#pragma warning disable IDE0079 // Dont remove next line | ||
#pragma warning disable IDE0005, CS8019 // Dont remove global usings | ||
|
||
global using static CloudFiles.Troubleshooter.Consts; | ||
global using static Spectre.Console.AnsiConsole; | ||
global using static Windows.Win32.PInvoke; | ||
|
||
using System.Diagnostics; | ||
|
||
using Windows.Win32.Storage.CloudFilters; | ||
|
||
namespace CloudFiles.Troubleshooter | ||
{ | ||
delegate int CommandHandler(ReadOnlySpan<string> args); | ||
|
||
static unsafe class Consts | ||
{ | ||
public static CF_CALLBACK_REGISTRATION[] NoneCallbackRegistration = [ | ||
new() { Type = CF_CALLBACK_TYPE.CF_CALLBACK_TYPE_NONE, Callback = null } | ||
]; | ||
|
||
public static string UsageFormat = $"Usage: {Process.GetCurrentProcess().ProcessName} {{0}} {{1}}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
CF_SYNC_ROOT_BASIC_INFO | ||
CF_SYNC_ROOT_PROVIDER_INFO | ||
CF_SYNC_ROOT_STANDARD_INFO | ||
CfConnectSyncRoot | ||
CfDisconnectSyncRoot | ||
CfUnregisterSyncRoot | ||
CfUpdatePlaceholder | ||
CreateFile | ||
FILE_ACCESS_RIGHTS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
using System.Buffers; | ||
|
||
namespace CloudFiles.Troubleshooter | ||
{ | ||
internal class OptionParser | ||
{ | ||
private static readonly SearchValues<char> ValueSeparators = SearchValues.Create([':', '=']); | ||
private readonly Dictionary<string, OptionHandler> _handler = new(StringComparer.OrdinalIgnoreCase); | ||
|
||
public bool AddOption(string name, OptionHandler handler) | ||
{ | ||
return _handler.TryAdd(name, handler); | ||
} | ||
|
||
public List<string> Parse(ReadOnlySpan<string> args) | ||
{ | ||
List<string> remaining = []; | ||
|
||
string lastKey = default!; | ||
OptionHandler? optionHandler = default; | ||
var type = OptionType.Argument; | ||
foreach (ref readonly var option in args) | ||
{ | ||
if (type == OptionType.Switch) | ||
{ | ||
type = OptionType.KeyValue; | ||
if (optionHandler!(OptionType.KeyValue, option) != OptionResult.Continue) | ||
{ | ||
throw new ArgumentException($"Bad value \"{option}\" supplied", lastKey); | ||
} | ||
} | ||
else if (option.StartsWith('-')) | ||
{ | ||
type = OptionType.Switch; | ||
var optionExpression = option.AsSpan().TrimStart('-'); | ||
var valueSeparator = optionExpression.IndexOfAny(ValueSeparators); | ||
string? value = null; | ||
if (valueSeparator != -1) | ||
{ | ||
type = OptionType.KeyValue; | ||
value = optionExpression[(valueSeparator + 1)..].ToString(); | ||
optionExpression = optionExpression[..valueSeparator]; | ||
} | ||
|
||
lastKey = optionExpression.ToString(); | ||
if ((optionHandler = FindOptionHandler(lastKey)) is null) | ||
{ | ||
throw new ArgumentException("Unknown option", lastKey); | ||
} | ||
else if (optionHandler(type, value) != OptionResult.NeedMore) | ||
{ | ||
type = OptionType.Argument; | ||
} | ||
} | ||
else | ||
{ | ||
remaining.Add(option); | ||
} | ||
} | ||
|
||
return remaining; | ||
} | ||
|
||
private OptionHandler? FindOptionHandler(string key) | ||
{ | ||
return _handler.TryGetValue(key, out var handler) | ||
? handler | ||
: FindSlow(key); | ||
|
||
OptionHandler? FindSlow(ReadOnlySpan<char> key) | ||
{ | ||
List<KeyValuePair<string, OptionHandler>> keys = [.. _handler]; | ||
for (int i = 0; i < key.Length; i++) | ||
{ | ||
ref readonly char symbol = ref key[i]; | ||
for (int j = keys.Count - 1; j >= 0; j--) | ||
{ | ||
var handler = keys[j]; | ||
// remove all keys, that are smaller than input key | ||
// and remove all keys that don't match ignore-case | ||
if (handler.Key.Length < key.Length || (char.ToUpperInvariant(symbol) != char.ToUpperInvariant(handler.Key[i]))) | ||
{ | ||
keys.RemoveAt(j); | ||
} | ||
else if (keys.Count == 1) | ||
{ | ||
return handler.Value; | ||
} | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} | ||
|
||
public delegate OptionResult OptionHandler(OptionType optionType, string? value); | ||
|
||
public enum OptionResult | ||
{ | ||
Continue, | ||
NeedMore, | ||
Unknown | ||
} | ||
|
||
public enum OptionType | ||
{ | ||
Argument, | ||
Switch, | ||
KeyValue | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,41 @@ | ||
// See https://aka.ms/new-console-template for more information | ||
Console.WriteLine("Hello, World!"); | ||
using CloudFiles.Troubleshooter; | ||
|
||
if (args.Length == 0) | ||
{ | ||
goto help; | ||
} | ||
|
||
if (FindCommand(args) is { } handler) | ||
{ | ||
return handler(args.AsSpan(1)); | ||
} | ||
|
||
help: | ||
PrintHelp(); | ||
return 0; | ||
|
||
static void PrintHelp() | ||
{ | ||
WriteLine($$""" | ||
cloudfiles-troubleshooter Copyright (C) 2024 iterate GmbH | ||
This program comes with ABSOLUTELY NO WARRANTY | ||
This is free software, and you are welcome to redistribute it | ||
under certain conditions | ||
{{string.Format(UsageFormat, "<Command>", "[Options]")}} | ||
Commands: | ||
prune Nukes a broken sync root from the filesystem. | ||
No, or any other unsupported command will print this Help. | ||
"""); | ||
} | ||
|
||
static CommandHandler? FindCommand(ReadOnlySpan<string> args) | ||
{ | ||
return args[0].ToUpperInvariant() switch | ||
{ | ||
"PRUNE" => PruneCommand.Run, | ||
_ => null | ||
}; | ||
} |
Oops, something went wrong.