aggressively trim SysTools (mostly replaced by CliWrap)
This commit is contained in:
parent
e70ab41bc0
commit
19633ec54f
|
@ -1,50 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Process;
|
||||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class RemoteCommandToProcess
|
||||
{
|
||||
public static ProcessResult ExecuteBlocking(this RemoteCommand command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return command
|
||||
.ToLocalCommand()
|
||||
.ExecuteBlocking(variables, logToConsole);
|
||||
}
|
||||
|
||||
public static AsyncProcess ToAsyncProcess(this RemoteCommand command, Dictionary<String, String> variables = null)
|
||||
{
|
||||
return command
|
||||
.ToLocalCommand()
|
||||
.ToAsyncProcess(variables);
|
||||
}
|
||||
|
||||
public static SyncProcess ToSyncProcess(this RemoteCommand command, Dictionary<String, String> variables = null)
|
||||
{
|
||||
return command
|
||||
.ToLocalCommand()
|
||||
.ToSyncProcess(variables);
|
||||
}
|
||||
|
||||
private static SysCommand ToLocalCommand(this RemoteCommand command)
|
||||
{
|
||||
var ssh = "ssh"
|
||||
.Opt("o", "ConnectTimeout=5") // TODO
|
||||
.Opt("o", "PasswordAuthentication=no")
|
||||
.Opt("o", "StrictHostKeyChecking=no")
|
||||
.Opt("o", "UserKnownHostsFile=/dev/null")
|
||||
.Opt("o", "LogLevel=ERROR");
|
||||
|
||||
var host = command.Host;
|
||||
|
||||
if (host.KeyFile.HasValue)
|
||||
ssh = ssh.Opt("i", host.KeyFile.Value);
|
||||
|
||||
if (host.Port != 22)
|
||||
ssh = ssh.Opt("p", host.Port);
|
||||
|
||||
return ssh
|
||||
.Arg($"{host.User}@{host.Address}")
|
||||
.Arg(command.Path + command.Args);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class RemotePathToRemoteCommand
|
||||
{
|
||||
public static RemoteCommand ToCommand(this RemotePath remotePath) => new RemoteCommand(remotePath);
|
||||
|
||||
public static RemoteCommand Opt1(this RemotePath remotePath, String option)
|
||||
{
|
||||
return remotePath
|
||||
.ToCommand()
|
||||
.Opt1(option);
|
||||
}
|
||||
|
||||
public static RemoteCommand Opt1(this RemotePath remotePath, String option, Object value, String separator = " ")
|
||||
{
|
||||
return remotePath
|
||||
.ToCommand()
|
||||
.Opt1(option, value, separator);
|
||||
}
|
||||
|
||||
public static RemoteCommand Opt2(this RemotePath remotePath, String option)
|
||||
{
|
||||
return remotePath
|
||||
.ToCommand()
|
||||
.Opt2(option);
|
||||
}
|
||||
|
||||
public static RemoteCommand Opt2(this RemotePath remotePath, String option, Object value, String separator = "=")
|
||||
{
|
||||
return remotePath
|
||||
.ToCommand()
|
||||
.Opt2(option, value, separator);
|
||||
}
|
||||
|
||||
public static RemoteCommand Opt(this RemotePath remotePath, String option)
|
||||
{
|
||||
return remotePath
|
||||
.ToCommand()
|
||||
.Opt(option);
|
||||
}
|
||||
|
||||
public static RemoteCommand Opt(this RemotePath remotePath, String option, Object value)
|
||||
{
|
||||
return remotePath
|
||||
.ToCommand()
|
||||
.Opt(option, value);
|
||||
}
|
||||
|
||||
public static RemoteCommand Arg(this RemotePath remotePath, Object argument)
|
||||
{
|
||||
return remotePath
|
||||
.ToCommand()
|
||||
.Arg(argument);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class SshHostToRemoteCommand
|
||||
{
|
||||
public static RemoteCommand Command(this SshHost host, SysPath path)
|
||||
{
|
||||
return new RemoteCommand(host, path);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class SshHostToRemotePath
|
||||
{
|
||||
public static RemotePath WithPath(this SshHost host, SysPath path)
|
||||
{
|
||||
return new RemotePath(host, path);
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class StringToCommand
|
||||
{
|
||||
public static SysCommand ToCommand(this String cmd) => new SysCommand(cmd);
|
||||
|
||||
public static SysCommand Opt1(this String cmd, String option)
|
||||
{
|
||||
return cmd
|
||||
.ToCommand()
|
||||
.Opt1(option);
|
||||
}
|
||||
|
||||
public static SysCommand Opt1(this String cmd, String option, Object value, String separator = " ")
|
||||
{
|
||||
return cmd
|
||||
.ToCommand()
|
||||
.Opt1(option, value, separator);
|
||||
}
|
||||
|
||||
public static SysCommand Opt2(this String cmd, String option)
|
||||
{
|
||||
return cmd
|
||||
.ToCommand()
|
||||
.Opt2(option);
|
||||
}
|
||||
|
||||
public static SysCommand Opt2(this String cmd, String option, Object value, String separator = "=")
|
||||
{
|
||||
return cmd
|
||||
.ToCommand()
|
||||
.Opt2(option, value, separator);
|
||||
}
|
||||
|
||||
public static SysCommand Opt(this String cmd, String option)
|
||||
{
|
||||
return cmd
|
||||
.ToCommand()
|
||||
.Opt(option);
|
||||
}
|
||||
|
||||
public static SysCommand Opt(this String cmd, String option, Object value)
|
||||
{
|
||||
return cmd
|
||||
.ToCommand()
|
||||
.Opt(option, value);
|
||||
}
|
||||
|
||||
public static SysCommand Arg(this String cmd, Object argument)
|
||||
{
|
||||
return cmd
|
||||
.ToCommand()
|
||||
.Arg(argument);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Process;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
using Env = Dictionary<String, String>;
|
||||
|
||||
|
||||
public static class StringToProcess
|
||||
{
|
||||
|
||||
public static ProcessResult ExecuteBlocking(this String command, Env variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return SysCommandToProcess.ExecuteBlocking(command, variables, logToConsole);
|
||||
}
|
||||
|
||||
public static AsyncProcess ToAsyncProcess(this String command, Env variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return new AsyncProcess(command, variables, logToConsole);
|
||||
}
|
||||
|
||||
public static SyncProcess ToSyncProcess(this String command, Env variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return new SyncProcess(command, variables, logToConsole);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class StringToRemotePath
|
||||
{
|
||||
public static RemotePath At(this String path, SshHost host) => new RemotePath(host, path);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class StringToSysPath
|
||||
{
|
||||
public static SysPath ToPath(this String path) => new SysPath(path);
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using InnovEnergy.Lib.SysTools.Process;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class SysCommandToProcess
|
||||
{
|
||||
public static ProcessResult ExecuteBlocking(this SysCommand command, Dictionary<String, String> variables = null, Boolean logToConsole =false)
|
||||
{
|
||||
var process = command.ToAsyncProcess(variables, logToConsole);
|
||||
|
||||
var stdOut = new List<String>();
|
||||
var stdErr = new List<String>();
|
||||
var output = new StringBuilder();
|
||||
|
||||
process.StandardOut.Subscribe(o =>
|
||||
{
|
||||
stdOut.Add(o);
|
||||
output.AppendLine(o);
|
||||
});
|
||||
|
||||
process.StandardErr.Subscribe(e =>
|
||||
{
|
||||
stdErr.Add(e);
|
||||
output.AppendLine(e);
|
||||
});
|
||||
|
||||
process.Run();
|
||||
|
||||
|
||||
var exitCode = process.ExitCode.Wait();
|
||||
|
||||
process.Terminate();
|
||||
return new ProcessResult(exitCode, stdOut, stdErr, output.ToString());
|
||||
}
|
||||
|
||||
public static AsyncProcess ToAsyncProcess(this SysCommand command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return new AsyncProcess(command, variables, logToConsole);
|
||||
}
|
||||
|
||||
public static SyncProcess ToSyncProcess(this SysCommand command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return new SyncProcess(command, variables, logToConsole);
|
||||
}
|
||||
|
||||
public static System.Diagnostics.Process ToConsoleProcess(this SysCommand command, Dictionary<String, String> variables = null)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
RedirectStandardOutput = false,
|
||||
RedirectStandardError = false,
|
||||
RedirectStandardInput = false,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
Arguments = command.Args,
|
||||
FileName = command.Path,
|
||||
};
|
||||
|
||||
if (variables != null)
|
||||
foreach (var kv in variables)
|
||||
startInfo.EnvironmentVariables[kv.Key] = kv.Value;
|
||||
|
||||
return new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = startInfo,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class SysCommandToRemoteCommand
|
||||
{
|
||||
public static RemoteCommand At(this SysCommand command, SshHost host)
|
||||
{
|
||||
var (path, args) = command;
|
||||
return new RemoteCommand(host, path, args);
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Process;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class SysPathToProcess
|
||||
{
|
||||
public static ProcessResult ExecuteBlocking(this SysPath command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return SysCommandToProcess.ExecuteBlocking(command, variables, logToConsole);
|
||||
}
|
||||
|
||||
public static AsyncProcess ToAsyncProcess(this SysPath command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return new AsyncProcess(command, variables, logToConsole);
|
||||
}
|
||||
|
||||
public static SyncProcess ToSyncProcess(this SysPath command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
||||
{
|
||||
return new SyncProcess(command, variables, logToConsole);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class SysPathToRemotePath
|
||||
{
|
||||
public static RemotePath At(this SysPath path, SshHost host)
|
||||
{
|
||||
return new RemotePath(host, path);
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Edges;
|
||||
|
||||
public static class SysPathToSysCommand
|
||||
{
|
||||
public static SysCommand ToCommand(this SysPath path) => new SysCommand(path);
|
||||
|
||||
public static SysCommand Opt1(this SysPath path, String option)
|
||||
{
|
||||
return path
|
||||
.ToCommand()
|
||||
.Opt1(option);
|
||||
}
|
||||
|
||||
public static SysCommand Opt1(this SysPath path, String option, Object value, String separator = " ")
|
||||
{
|
||||
return path
|
||||
.ToCommand()
|
||||
.Opt1(option, value, separator);
|
||||
}
|
||||
|
||||
public static SysCommand Opt2(this SysPath path, String option)
|
||||
{
|
||||
return path
|
||||
.ToCommand()
|
||||
.Opt2(option);
|
||||
}
|
||||
|
||||
public static SysCommand Opt2(this SysPath path, String option, Object value, String separator = "=")
|
||||
{
|
||||
return path
|
||||
.ToCommand()
|
||||
.Opt2(option, value, separator);
|
||||
}
|
||||
|
||||
public static SysCommand Opt(this SysPath path, String option)
|
||||
{
|
||||
return path
|
||||
.ToCommand()
|
||||
.Opt(option);
|
||||
}
|
||||
|
||||
public static SysCommand Opt(this SysPath path, String option, Object value)
|
||||
{
|
||||
return path
|
||||
.ToCommand()
|
||||
.Opt(option, value);
|
||||
}
|
||||
|
||||
public static SysCommand Arg(this SysPath path, Object argument)
|
||||
{
|
||||
return path
|
||||
.ToCommand()
|
||||
.Arg(argument);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ using InnovEnergy.Lib.Utils;
|
|||
|
||||
namespace InnovEnergy.Lib.SysTools;
|
||||
|
||||
[Obsolete("Needs rework before use")]
|
||||
public static class FileIo
|
||||
{
|
||||
public static Boolean Exists (this SysPath path) => path.FileExists() || path.DirectoryExists();
|
||||
|
@ -92,7 +91,7 @@ public static class FileIo
|
|||
|
||||
SysPath Target(SysPath path) => targetDir.Append(path.RelativeTo(sourceDir));
|
||||
|
||||
sourceDir.Traverse(Directories)
|
||||
sourceDir.TraverseDepthFirstPreOrder(Directories)
|
||||
.Do(d => Target(d).CreateDirectory())
|
||||
.SelectMany(Files)
|
||||
.ForEach(f => f.CopyFileTo(Target(f)));
|
||||
|
@ -116,7 +115,7 @@ public static class FileIo
|
|||
|
||||
public static IEnumerable<SysPath> DescendantDirectories(this SysPath path)
|
||||
{
|
||||
return path.Traverse(Directories);
|
||||
return path.TraverseDepthFirstPreOrder(Directories);
|
||||
}
|
||||
|
||||
public static IEnumerable<SysPath> DescendantFiles(this SysPath path)
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
using static System.ConsoleColor;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Process;
|
||||
|
||||
using Env = Dictionary<String, String>;
|
||||
|
||||
[Obsolete("Use CliWrap instead")]
|
||||
public class AsyncProcess
|
||||
{
|
||||
private readonly Subject<String> _StandardIn;
|
||||
private readonly Subject<String> _StandardOut;
|
||||
private readonly Subject<String> _StandardErr;
|
||||
private readonly ReplaySubject<Int32> _ExitCode;
|
||||
|
||||
public SysCommand Command { get; }
|
||||
public Env Environment { get; }
|
||||
|
||||
public IObserver<String> StandardIn => _StandardIn;
|
||||
public IObservable<String> StandardOut => _StandardOut;
|
||||
public IObservable<String> StandardErr => _StandardErr;
|
||||
public IObservable<Int32> ExitCode => _ExitCode;
|
||||
|
||||
public Boolean HasStarted => Process != null;
|
||||
public Boolean HasFinished => HasStarted && Process.HasExited;
|
||||
|
||||
private System.Diagnostics.Process Process { get; set; }
|
||||
|
||||
public AsyncProcess(SysCommand command, Env environment = null, Boolean logToConsole = false)
|
||||
{
|
||||
Command = command;
|
||||
Environment = environment;
|
||||
|
||||
_StandardIn = new Subject<String>();
|
||||
_StandardOut = new Subject<String>();
|
||||
_StandardErr = new Subject<String>();
|
||||
_ExitCode = new ReplaySubject<Int32>();
|
||||
|
||||
if (logToConsole)
|
||||
LogToConsole();
|
||||
}
|
||||
|
||||
private void LogToConsole()
|
||||
{
|
||||
_StandardOut.Subscribe(d => d.WriteLine());
|
||||
_StandardErr.Subscribe(d => d.WriteLine(Red));
|
||||
_ExitCode.Subscribe(ec =>
|
||||
{
|
||||
"ExitCode: ".Write(Cyan);
|
||||
ec.WriteLine(ec == 0 ? Green : Red);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public Int32 WaitForExit()
|
||||
{
|
||||
EnsureStarted();
|
||||
|
||||
return ExitCode.Wait();
|
||||
|
||||
//Process.WaitForExit(); // TODO
|
||||
//return Process.ExitCode;
|
||||
}
|
||||
|
||||
private void EnsureNotStarted()
|
||||
{
|
||||
if (HasStarted)
|
||||
throw new Exception("Process has already started");
|
||||
}
|
||||
|
||||
private void EnsureStarted()
|
||||
{
|
||||
if (!HasStarted)
|
||||
throw new Exception("Process has not yet started");
|
||||
}
|
||||
|
||||
|
||||
public AsyncProcess Run()
|
||||
{
|
||||
EnsureNotStarted();
|
||||
Process = InitProcess();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private System.Diagnostics.Process InitProcess()
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardInput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
Arguments = Command.Args,
|
||||
FileName = Command.Path,
|
||||
};
|
||||
|
||||
foreach (var ev in Environment.EmptyIfNull())
|
||||
startInfo.EnvironmentVariables[ev.Key] = ev.Value;
|
||||
|
||||
var proc = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = startInfo,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
proc.Start(); // must start BEFORE we access Streams
|
||||
|
||||
Observable.Repeat(proc.StandardOutput, TaskPoolScheduler.Default)
|
||||
.Select(s => s.ReadLine())
|
||||
.TakeWhile(l => l != null)
|
||||
.Subscribe(_StandardOut);
|
||||
|
||||
Observable.Repeat(proc.StandardError, TaskPoolScheduler.Default)
|
||||
.Select(s => s.ReadLine())
|
||||
.TakeWhile(l => l != null)
|
||||
.Subscribe(_StandardErr);
|
||||
|
||||
_StandardIn.Subscribe(onNext: proc.StandardInput.WriteLine,
|
||||
onError: _ => proc.StandardInput.Close(),
|
||||
onCompleted: proc.StandardInput.Close);
|
||||
|
||||
Observable.FromEventPattern(e => proc.Exited += e, e => proc.Exited -= e)
|
||||
.Take(1)
|
||||
.Do(_=>Console.WriteLine("Exited"))
|
||||
.OfType<Object>()
|
||||
.Concat(StandardOut.IgnoreElements()) // make sure std streams finish first
|
||||
.Concat(StandardErr.IgnoreElements())
|
||||
.Select(_ => Process.ExitCode)
|
||||
.Subscribe(_ExitCode);
|
||||
|
||||
_ExitCode.Subscribe(_ =>
|
||||
{
|
||||
proc.StandardInput.Close();
|
||||
proc.StandardOutput.Close();
|
||||
proc.StandardError.Close();
|
||||
});
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
public AsyncProcess Terminate()
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
Process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsyncProcess Kill()
|
||||
{
|
||||
EnsureStarted();
|
||||
|
||||
try
|
||||
{
|
||||
Process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public IDisposable ThrowOnStdErr()
|
||||
{
|
||||
return StandardErr.Subscribe(e => throw new Exception(e));
|
||||
}
|
||||
|
||||
public override String ToString() => Command.ToString(); // TODO: env
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Process;
|
||||
|
||||
[Obsolete("Use CliWrap instead")]
|
||||
public readonly struct ProcessResult
|
||||
{
|
||||
public ProcessResult(Int32 exitCode,
|
||||
IReadOnlyList<String> stdOut,
|
||||
IReadOnlyList<String> stdErr,
|
||||
String output)
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
StdOut = stdOut;
|
||||
StdErr = stdErr;
|
||||
Output = output;
|
||||
}
|
||||
|
||||
public Int32 ExitCode { get; }
|
||||
public String Output { get; }
|
||||
|
||||
public IReadOnlyList<String> StdOut { get; }
|
||||
public IReadOnlyList<String> StdErr { get; }
|
||||
|
||||
public ProcessResult ThrowOnError()
|
||||
{
|
||||
if (ExitCode != 0)
|
||||
{
|
||||
if (StdErr.Count ==0)
|
||||
throw new Exception(nameof(AsyncProcess) + " exited with exit code " + ExitCode);
|
||||
|
||||
throw new Exception(StdErr.Aggregate((a, b) => a.NewLine() + b));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessResult OnError(Action<ProcessResult> action)
|
||||
{
|
||||
if (Failed) action(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessResult OnSuccess(Action<ProcessResult> action)
|
||||
{
|
||||
if (Succeeded) action(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessResult Dump()
|
||||
{
|
||||
Output.Write();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean Succeeded => ExitCode == 0;
|
||||
public Boolean Failed => ExitCode != 0;
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
return Output.NewLine() + "ExitCode: " + ExitCode;
|
||||
}
|
||||
}
|
|
@ -1,284 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
using static System.ConsoleColor;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Process;
|
||||
|
||||
using Env = Dictionary<String, String>;
|
||||
|
||||
[Obsolete("Use CliWrap instead")]
|
||||
public class SyncProcess
|
||||
{
|
||||
public SysCommand Command { get; }
|
||||
public Env Environment { get; }
|
||||
public Boolean LogToConsole { get; }
|
||||
|
||||
public Boolean HasFinished => Process.HasExited;
|
||||
|
||||
private Task<(DateTime timeStamp, StdOutput output)> _ReadStdOut;
|
||||
private Task<(DateTime timeStamp, StdError error )> _ReadStdErr;
|
||||
|
||||
private System.Diagnostics.Process Process { get; }
|
||||
|
||||
public SyncProcess(SysCommand command, Env environment = null, Boolean logToConsole = false)
|
||||
{
|
||||
Command = command;
|
||||
Environment = environment;
|
||||
LogToConsole = logToConsole;
|
||||
Process = InitProcess();
|
||||
|
||||
_ReadStdOut = ReadNextStdOut();
|
||||
_ReadStdErr = ReadNextStdErr();
|
||||
}
|
||||
|
||||
private Task<(DateTime timeStamp, StdOutput output)> ReadNextStdOut()
|
||||
{
|
||||
Debug.Assert(_ReadStdOut == null || _ReadStdOut.IsCompleted);
|
||||
|
||||
return Process
|
||||
.StandardOutput
|
||||
.ReadLineAsync()
|
||||
.ContinueWith(t => (DateTime.Now, SyncProcessOutput.StdOutput(t.Result)));
|
||||
|
||||
}
|
||||
|
||||
private Task<(DateTime timeStamp, StdError error)> ReadNextStdErr()
|
||||
{
|
||||
Debug.Assert(_ReadStdErr == null || _ReadStdErr.IsCompleted);
|
||||
|
||||
return Process
|
||||
.StandardError
|
||||
.ReadLineAsync()
|
||||
.ContinueWith(t => (DateTime.Now, SyncProcessOutput.StdError(t.Result)));
|
||||
}
|
||||
|
||||
|
||||
private System.Diagnostics.Process InitProcess()
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardInput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
Arguments = Command.Args.Trim(),
|
||||
FileName = Command.Path,
|
||||
};
|
||||
|
||||
foreach (var ev in Environment.EmptyIfNull())
|
||||
startInfo.EnvironmentVariables[ev.Key] = ev.Value;
|
||||
|
||||
var proc = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = startInfo,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
proc.Start(); // must start BEFORE we access Streams
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
public Match ReadStdOutUntil(Regex rx)
|
||||
{
|
||||
foreach (var l in ReadStdOut())
|
||||
{
|
||||
|
||||
var match = rx.Match(l);
|
||||
if (match.Success)
|
||||
return match;
|
||||
}
|
||||
|
||||
throw new Exception("no matching output");
|
||||
}
|
||||
|
||||
public String ReadStdOutUntil(String line)
|
||||
{
|
||||
return ReadStdOutUntil(l => l == line);
|
||||
}
|
||||
|
||||
|
||||
public String ReadStdOutUntil(Func<String, Boolean> predicate)
|
||||
{
|
||||
foreach (var l in ReadStdOut())
|
||||
{
|
||||
if (predicate(l))
|
||||
return l;
|
||||
}
|
||||
|
||||
throw new Exception("no matching output");
|
||||
}
|
||||
|
||||
public IEnumerable<String> ReadStdOut(TimeSpan timeOut = default) => ReadMany(timeOut).ThrownOnStdErr();
|
||||
|
||||
public IEnumerable<SyncProcessOutput> ReadMany(TimeSpan timeOut = default)
|
||||
{
|
||||
SyncProcessOutput output;
|
||||
|
||||
do
|
||||
{
|
||||
output = ReadNext(timeOut);
|
||||
yield return output;
|
||||
}
|
||||
while (!(output is ExitCode) && !(output is TimeoutError));
|
||||
}
|
||||
|
||||
public SyncProcessOutput ReadNext(TimeSpan timeOut = default)
|
||||
{
|
||||
var wait = timeOut == default
|
||||
? Task.WaitAny(_ReadStdOut, _ReadStdErr)
|
||||
: Task.WaitAny(_ReadStdOut, _ReadStdErr, Task.Delay(timeOut));
|
||||
|
||||
if (wait == 2)
|
||||
{
|
||||
if (LogToConsole)
|
||||
"Timeout".WriteLine(Red);
|
||||
|
||||
return SyncProcessOutput.TimeoutError(timeOut);
|
||||
}
|
||||
|
||||
if (_ReadStdOut.IsCompleted && _ReadStdErr.IsCompleted)
|
||||
{
|
||||
var (outTimeStamp, output) = _ReadStdOut.Result;
|
||||
var (errTimeStamp, error) = _ReadStdErr.Result;
|
||||
|
||||
if (outTimeStamp < errTimeStamp && output != null)
|
||||
{
|
||||
_ReadStdOut = ReadNextStdOut();
|
||||
|
||||
if (LogToConsole)
|
||||
output.Data.WriteLine();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
if (error != null)
|
||||
{
|
||||
_ReadStdErr = ReadNextStdErr();
|
||||
|
||||
if (LogToConsole)
|
||||
error.Data.WriteLine(Red);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Process.WaitForExit();
|
||||
|
||||
var exitCode = Process.ExitCode;
|
||||
|
||||
if (LogToConsole)
|
||||
{
|
||||
"ExitCode: ".Write(Cyan);
|
||||
exitCode.WriteLine(exitCode == 0 ? Green : Red);
|
||||
}
|
||||
|
||||
return SyncProcessOutput.ExitCode(exitCode);
|
||||
}
|
||||
|
||||
if (_ReadStdOut.IsCompleted)
|
||||
{
|
||||
var (_, output) = _ReadStdOut.Result;
|
||||
|
||||
if (output != null)
|
||||
{
|
||||
_ReadStdOut = ReadNextStdOut();
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_ReadStdErr.IsCompleted)
|
||||
{
|
||||
var (_, error) = _ReadStdErr.Result;
|
||||
|
||||
if (error != null)
|
||||
{
|
||||
_ReadStdErr = ReadNextStdErr();
|
||||
return error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw new Exception("This should not happen");
|
||||
}
|
||||
|
||||
public SyncProcess Terminate()
|
||||
{
|
||||
Process.Kill();
|
||||
return this;
|
||||
}
|
||||
|
||||
public SyncProcess Kill()
|
||||
{
|
||||
Process.Kill();
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Write(String line)
|
||||
{
|
||||
Process.StandardInput.WriteLine(line);
|
||||
}
|
||||
|
||||
public override String ToString() => Command.ToString(); // TODO: env
|
||||
}
|
||||
|
||||
|
||||
public abstract class SyncProcessOutput
|
||||
{
|
||||
public static StdOutput StdOutput (String data ) => new StdOutput(data);
|
||||
public static StdError StdError (String data ) => new StdError(data);
|
||||
public static ExitCode ExitCode (Int32 data ) => new ExitCode(data);
|
||||
public static TimeoutError TimeoutError (TimeSpan timeout) => new TimeoutError(timeout);
|
||||
}
|
||||
|
||||
public class ExitCode : SyncProcessOutput
|
||||
{
|
||||
public ExitCode(Int32 data) { Data = data;}
|
||||
public Int32 Data { get; }
|
||||
|
||||
public override String ToString() => Data.ToString();
|
||||
}
|
||||
|
||||
public class StdError : SyncProcessOutput
|
||||
{
|
||||
public StdError(String data) { Data = data;}
|
||||
public String Data { get; }
|
||||
|
||||
public override String ToString() => Data;
|
||||
}
|
||||
|
||||
public class StdOutput : SyncProcessOutput
|
||||
{
|
||||
public StdOutput(String data) { Data = data; }
|
||||
public String Data { get; }
|
||||
|
||||
public override String ToString() => Data;
|
||||
}
|
||||
|
||||
public class TimeoutError : SyncProcessOutput
|
||||
{
|
||||
public TimeoutError(TimeSpan timeout) { Timeout = timeout; }
|
||||
public TimeSpan Timeout { get; }
|
||||
|
||||
public override String ToString() => Timeout.ToString();
|
||||
}
|
||||
|
||||
|
||||
public static class ProcessOutputExtensions
|
||||
{
|
||||
public static IEnumerable<String> ThrownOnStdErr(this IEnumerable<SyncProcessOutput> po)
|
||||
{
|
||||
foreach (var p in po)
|
||||
{
|
||||
if (p is StdOutput o)
|
||||
yield return o.Data;
|
||||
|
||||
if (p is StdError e)
|
||||
throw new Exception(e.Data);
|
||||
|
||||
// Ignore exit code
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
[Obsolete("Use CliWrap instead")]
|
||||
public readonly struct RemoteCommand
|
||||
{
|
||||
public SshHost Host { get; }
|
||||
public SysPath Path { get; }
|
||||
public String Args { get; }
|
||||
|
||||
public RemoteCommand(RemotePath remotePath, String args = "")
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
Args = args;
|
||||
Host = host;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public RemoteCommand(SshHost host, SysPath path, String args = "")
|
||||
{
|
||||
Args = args;
|
||||
Host = host;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public static RemoteCommand FromRemotePath(RemotePath remotePath) => new RemoteCommand(remotePath);
|
||||
|
||||
|
||||
// TODO
|
||||
public RemoteCommand Opt1(String option)
|
||||
{
|
||||
return new RemoteCommand(Host, Path, $"{Args} -{option}");
|
||||
}
|
||||
|
||||
public RemoteCommand Opt1(String option, Object value, String separator = " ")
|
||||
{
|
||||
return new RemoteCommand(Host, Path, $"{Args} -{option}{separator}{Quote(value)}");
|
||||
}
|
||||
|
||||
public RemoteCommand Opt2(String option)
|
||||
{
|
||||
return new RemoteCommand(Host, Path, $"{Args} --{option}");
|
||||
}
|
||||
|
||||
public RemoteCommand Opt2(String option, Object value, String separator = "=")
|
||||
{
|
||||
return new RemoteCommand(Host, Path, $"{Args} --{option}{separator}{Quote(value)}");
|
||||
}
|
||||
|
||||
public RemoteCommand Opt(String option)
|
||||
{
|
||||
return option.Length == 1
|
||||
? Opt1(option)
|
||||
: Opt2(option);
|
||||
}
|
||||
|
||||
public RemoteCommand Opt(String option, Object value)
|
||||
{
|
||||
return option.Length == 1
|
||||
? Opt1(option, value)
|
||||
: Opt2(option, value);
|
||||
}
|
||||
|
||||
public RemoteCommand Arg(Object argument)
|
||||
{
|
||||
return new RemoteCommand(Host, Path, Args + " " + Quote(argument));
|
||||
}
|
||||
|
||||
private static String Quote(Object argument)
|
||||
{
|
||||
var arg = argument.ToString();
|
||||
if (arg.Contains(" "))
|
||||
arg = arg.Quote(); // TODO: single quote?
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
#region equality
|
||||
|
||||
public Boolean Equals(RemoteCommand other)
|
||||
{
|
||||
return Host.Equals(other.Host) && Path.Equals(other.Path) && Args == other.Args;
|
||||
}
|
||||
|
||||
public override Boolean Equals(Object obj)
|
||||
{
|
||||
return obj is RemoteCommand other && Equals(other);
|
||||
}
|
||||
|
||||
public override Int32 GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = Host.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Path.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ (Args != null ? Args.GetHashCode() : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion equality
|
||||
|
||||
|
||||
public static implicit operator RemoteCommand(RemotePath remotePath) => new RemoteCommand(remotePath);
|
||||
|
||||
public void Deconstruct(out SshHost host, out SysPath path, out String args)
|
||||
{
|
||||
host = Host;
|
||||
path = Path;
|
||||
args = Args;
|
||||
}
|
||||
|
||||
public override String ToString() => $"{Host}:{Path}{Args}";
|
||||
}
|
|
@ -1,299 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Edges;
|
||||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
[Obsolete("Needs rework before use")]
|
||||
public static class RemoteFileIo
|
||||
{
|
||||
|
||||
public static Boolean Exists(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
return "test"
|
||||
.Opt("e")
|
||||
.Arg(path)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ExitCode == 0;
|
||||
}
|
||||
|
||||
// TODO: how to find out if ssh has failed or remote command?
|
||||
|
||||
public static Boolean DirectoryExists(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
return "test"
|
||||
.Opt("d")
|
||||
.Arg(path)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ExitCode == 0;
|
||||
}
|
||||
|
||||
public static Boolean FileExists(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
return "test"
|
||||
.Opt("f")
|
||||
.Arg(path)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ExitCode == 0;
|
||||
}
|
||||
|
||||
public static Boolean DirectoryExists(this RemotePath remotePath, String directoryName)
|
||||
{
|
||||
return DirectoryExists(remotePath / directoryName);
|
||||
}
|
||||
|
||||
public static Boolean FileExists(this RemotePath remotePath, String fileName)
|
||||
{
|
||||
return DirectoryExists(remotePath / fileName);
|
||||
}
|
||||
|
||||
public static IEnumerable<RemotePath> Directories(this RemotePath rp)
|
||||
{
|
||||
var (host, path) = rp;
|
||||
|
||||
return "find"
|
||||
.Arg(path)
|
||||
.Opt1("mindepth", 1)
|
||||
.Opt1("maxdepth", 1)
|
||||
.Opt1("type", "d")
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError()
|
||||
.StdOut
|
||||
.Select(p => new RemotePath(host, p));
|
||||
}
|
||||
|
||||
public static IEnumerable<RemotePath> Files(this RemotePath rp)
|
||||
{
|
||||
var (host, path) = rp;
|
||||
|
||||
return "find"
|
||||
.Arg(path)
|
||||
.Opt1("mindepth", 1)
|
||||
.Opt1("maxdepth", 1)
|
||||
.Opt1("type", "f")
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError()
|
||||
.StdOut
|
||||
.Select(p => new RemotePath(host, p));
|
||||
}
|
||||
|
||||
public static RemotePath CreateDirectory(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
"mkdir".Arg(path)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError();
|
||||
|
||||
return remotePath;
|
||||
}
|
||||
|
||||
public static RemotePath MoveFileTo(this RemotePath sourcePath, RemotePath targetPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static RemotePath MoveDirectory(this RemotePath sourcePath, RemotePath targetPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static RemotePath CreateDirectory(this RemotePath path, String dirName)
|
||||
{
|
||||
return CreateDirectory(path / dirName);
|
||||
}
|
||||
|
||||
public static RemotePath DeleteFile(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath; // TODO: test file
|
||||
|
||||
"rm".Arg(path)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError();
|
||||
|
||||
return remotePath;
|
||||
}
|
||||
|
||||
|
||||
public static RemotePath DeleteFile(this RemotePath path, String fileName)
|
||||
{
|
||||
return DeleteFile(path / fileName);
|
||||
}
|
||||
|
||||
public static RemotePath DeleteDirectory(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath; // TODO: test dir
|
||||
|
||||
"rm".Opt("r")
|
||||
.Arg(path)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError();
|
||||
|
||||
return remotePath;
|
||||
}
|
||||
|
||||
public static RemotePath DeleteDirectory(this RemotePath path, String directoryName)
|
||||
{
|
||||
return DeleteDirectory(path / directoryName);
|
||||
}
|
||||
|
||||
public static RemotePath CopyDirectoryTo(this RemotePath sourceDir, RemotePath targetDir)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public static RemotePath CopyFileTo(this RemotePath srcFile, RemotePath targetFile)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static RemotePath CopyFileToDirectory(this RemotePath srcFile, RemotePath targetDir)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static IEnumerable<RemotePath> DescendantDirectories(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
return "find"
|
||||
.Arg(path)
|
||||
.Opt1("type", "d")
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError()
|
||||
.StdOut
|
||||
.Select(d => new RemotePath(host, d));
|
||||
}
|
||||
|
||||
public static IEnumerable<RemotePath> DescendantFiles(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
return "find"
|
||||
.Arg(path)
|
||||
.Opt1("type", "f")
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError()
|
||||
.StdOut
|
||||
.Select(d => new RemotePath(host, d));
|
||||
}
|
||||
|
||||
public static IEnumerable<RemotePath> Descendants(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
return "find"
|
||||
.Arg(path)
|
||||
.Opt1("mindepth", 1)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError()
|
||||
.StdOut
|
||||
.Select(l => new RemotePath(host, l));
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<Byte> ReadBytes(this RemotePath path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public static IReadOnlyList<String> ReadLines(this RemotePath remotePath)
|
||||
{
|
||||
var (host, path) = remotePath;
|
||||
|
||||
return "cat"
|
||||
.Arg(path)
|
||||
.At(host)
|
||||
.ExecuteBlocking()
|
||||
.ThrowOnError()
|
||||
.StdOut;
|
||||
}
|
||||
|
||||
// public static IEnumerable<String> ReadLines(this RemotePath path, Encoding encoding)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
//public static String ReadText(this RemotePath path) => path.ReadText(Encoding.UTF8);
|
||||
|
||||
public static String ReadText(this RemotePath rp)
|
||||
{
|
||||
var lines = ReadLines(rp);
|
||||
|
||||
if (lines.Count == 0) return "";
|
||||
if (lines.Count == 1) return lines[0];
|
||||
|
||||
return lines.Aggregate((a, b) => a.NewLine() + b);
|
||||
}
|
||||
|
||||
public static RemotePath WriteText(this RemotePath rp, String text)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public static RemotePath AppendLines(this RemotePath filePath, params String[] lines)
|
||||
{
|
||||
return AppendLines(filePath, (IEnumerable<String>) lines);
|
||||
}
|
||||
|
||||
public static RemotePath AppendLines(this RemotePath rp, IEnumerable<String> lines)
|
||||
{
|
||||
return WriteLines(rp, lines, append: true);
|
||||
}
|
||||
|
||||
public static RemotePath WriteLines(this RemotePath filePath, params String[] lines)
|
||||
{
|
||||
return WriteLines(filePath, (IEnumerable<String>) lines);
|
||||
}
|
||||
|
||||
public static RemotePath WriteLines(this RemotePath rp, IEnumerable<String> lines)
|
||||
{
|
||||
return WriteLines(rp, lines, append: false);
|
||||
}
|
||||
|
||||
private static RemotePath WriteLines(this RemotePath rp, IEnumerable<String> lines, Boolean append)
|
||||
{
|
||||
var (host, path) = rp;
|
||||
|
||||
var proc = "cat"
|
||||
.Arg(append ? ">>" : ">")
|
||||
.Arg(path)
|
||||
.At(host).ToAsyncProcess();
|
||||
|
||||
foreach (var line in lines)
|
||||
proc.StandardIn.OnNext(line);
|
||||
|
||||
proc.StandardIn.OnCompleted(); // TODO: OnCompleted or not?
|
||||
|
||||
// TODO: error handling
|
||||
|
||||
var exitCode = proc.WaitForExit();
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
public static RemotePath AppendText(this RemotePath filePath, String text)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
[Obsolete]
|
||||
public readonly struct RemotePath
|
||||
{
|
||||
public SysPath Path { get; }
|
||||
public SshHost Host { get; }
|
||||
|
||||
public RemotePath Parent => Path.Parent.At(Host);
|
||||
public String Tail => Path.Tail;
|
||||
public String Head => Path.Head;
|
||||
public String Extension => Path.Extension;
|
||||
|
||||
public RemotePath(SshHost host, SysPath path)
|
||||
{
|
||||
Host = host;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public RemotePath At(SshHost host)
|
||||
{
|
||||
return new RemotePath(host, Path);
|
||||
}
|
||||
|
||||
public RemotePath ToRelative() => Path.ToRelative().At(Host);
|
||||
public RemotePath ToAbsolute() => Path.ToAbsolute().At(Host);
|
||||
public RemotePath ToEnvRef() => Path.ToEnvRef().At(Host);
|
||||
|
||||
public RemotePath RelativeTo(RemotePath referencePath)
|
||||
{
|
||||
if (!Host.Equals(referencePath.Host))
|
||||
throw new ArgumentException(nameof(referencePath));
|
||||
|
||||
return Path.RelativeTo(referencePath.Path).At(Host);
|
||||
}
|
||||
|
||||
public RemotePath RelativeTo(SysPath referencePath)
|
||||
{
|
||||
return Path.RelativeTo(referencePath).At(Host);
|
||||
}
|
||||
|
||||
public Boolean IsSubPathOf(RemotePath referencePath)
|
||||
{
|
||||
var (host, path) = referencePath;
|
||||
return Host.Equals(host) && Path.IsSubPathOf(path);
|
||||
}
|
||||
|
||||
public RemotePath Append(SysPath right)
|
||||
{
|
||||
return Path.Append(right).At(Host);
|
||||
}
|
||||
|
||||
#region equality
|
||||
|
||||
public Boolean Equals(RemotePath other)
|
||||
{
|
||||
var (host, path) = other;
|
||||
return Path.Equals(path) && Host.Equals(host);
|
||||
}
|
||||
|
||||
public override Boolean Equals(Object obj)
|
||||
{
|
||||
return obj is RemotePath other && Equals(other);
|
||||
}
|
||||
|
||||
public override Int32 GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Path.GetHashCode() * 397) ^ Host.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion equality
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#region operators
|
||||
|
||||
public static RemotePath operator /(RemotePath left, SysPath right) => left.Append(right);
|
||||
public static RemotePath operator /(RemotePath left, String right) => left.Append(right);
|
||||
|
||||
public static Boolean operator ==(RemotePath left, RemotePath right) => left.Equals(right);
|
||||
public static Boolean operator !=(RemotePath left, RemotePath right) => left.Equals(right);
|
||||
|
||||
public static implicit operator String(RemotePath remotePath) => remotePath.ToString();
|
||||
|
||||
#endregion
|
||||
|
||||
public void Deconstruct(out SshHost host, out SysPath path)
|
||||
{
|
||||
path = Path;
|
||||
host = Host;
|
||||
}
|
||||
|
||||
public override String ToString() => $"{Host}:{Path}";
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Edges;
|
||||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools.Remote;
|
||||
|
||||
[Obsolete("Needs rework before use")]
|
||||
public readonly struct SshHost
|
||||
{
|
||||
public const Int32 DefaultPort = 22;
|
||||
|
||||
public String User { get; }
|
||||
public String Address { get; }
|
||||
public Int32 Port { get; }
|
||||
public SysPath? KeyFile { get; }
|
||||
|
||||
public SshHost(String user, String address, Int32 port = DefaultPort)
|
||||
{
|
||||
User = user;
|
||||
Address = address;
|
||||
KeyFile = null;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public SshHost(String user, String address, SysPath? keyFile, Int32 port = DefaultPort)
|
||||
{
|
||||
User = user;
|
||||
Address = address;
|
||||
KeyFile = keyFile;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public static SshHost Parse(String userAtAddressColonPort)
|
||||
{
|
||||
var addressPort = userAtAddressColonPort.AfterFirst('@').Split(':');
|
||||
|
||||
var user = userAtAddressColonPort.UntilFirst('@');
|
||||
var address = addressPort[0];
|
||||
var port = addressPort.Length < 2 ? DefaultPort : Int32.Parse(addressPort[1]);
|
||||
|
||||
return new SshHost(user, address, null, port);
|
||||
}
|
||||
|
||||
public static SshHost Parse(String userAtAddressColonPort, SysPath keyFile)
|
||||
{
|
||||
var addressPort = userAtAddressColonPort.AfterFirst('@').Split(':');
|
||||
|
||||
var user = userAtAddressColonPort.UntilFirst('@');
|
||||
var address = addressPort[0];
|
||||
var port = addressPort.Length < 2 ? DefaultPort : Int32.Parse(addressPort[1]);
|
||||
|
||||
return new SshHost(user, address, keyFile, port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static SshHost Localhost { get; } = new SshHost(Environment.UserName, "127.0.0.1");
|
||||
|
||||
public Boolean IsLocalhost => Equals(Localhost);
|
||||
|
||||
public override String ToString() => $"{User}@{Address}";
|
||||
|
||||
#region equality
|
||||
|
||||
public Boolean Equals(SshHost other)
|
||||
{
|
||||
return User == other.User &&
|
||||
Address == other.Address &&
|
||||
Port == other.Port &&
|
||||
Nullable.Equals(KeyFile, other.KeyFile);
|
||||
}
|
||||
|
||||
public override Boolean Equals(Object obj)
|
||||
{
|
||||
return obj is SshHost other && Equals(other);
|
||||
}
|
||||
|
||||
public override Int32 GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = (User != null ? User.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (Address != null ? Address.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ Port;
|
||||
hashCode = (hashCode * 397) ^ KeyFile.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static RemotePath operator / (SshHost host, SysPath path) => host.WithPath(path);
|
||||
public static RemotePath operator / (SshHost host, String path)
|
||||
{
|
||||
return host.WithPath(path.ToPath().ToAbsolute());
|
||||
}
|
||||
|
||||
public void Deconstruct(out String user, out String address, out SysPath? keyFile)
|
||||
{
|
||||
user = User;
|
||||
address = Address;
|
||||
keyFile = KeyFile;
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools;
|
||||
|
||||
[Obsolete("Use CliWrap instead")]
|
||||
public readonly struct SysCommand
|
||||
{
|
||||
public SysPath Path { get; }
|
||||
public String Args { get; }
|
||||
|
||||
public SysCommand(SysPath path, String args = "")
|
||||
{
|
||||
Path = path;
|
||||
Args = args;
|
||||
}
|
||||
|
||||
public static SysCommand FromPath(SysPath path) => new SysCommand(path);
|
||||
public static SysCommand FromString(String cmd) => new SysCommand(cmd);
|
||||
|
||||
public SysCommand Opt1(String option)
|
||||
{
|
||||
return new SysCommand(Path, $"{Args} -{option}");
|
||||
}
|
||||
|
||||
public SysCommand Opt1(String option, Object value, String separator = " ")
|
||||
{
|
||||
return new SysCommand(Path, $"{Args} -{option}{separator}{QuoteArg(value)}");
|
||||
}
|
||||
|
||||
public SysCommand Opt2(String option)
|
||||
{
|
||||
return new SysCommand(Path, $"{Args} --{option}");
|
||||
}
|
||||
|
||||
public SysCommand Opt2(String option, Object value, String separator = "=")
|
||||
{
|
||||
return new SysCommand(Path, $"{Args} --{option}{separator}{QuoteArg(value)}");
|
||||
}
|
||||
|
||||
public SysCommand Opt(String option)
|
||||
{
|
||||
return option.Length == 1
|
||||
? Opt1(option)
|
||||
: Opt2(option);
|
||||
}
|
||||
|
||||
public SysCommand Opt(String option, Object value)
|
||||
{
|
||||
return option.Length == 1
|
||||
? Opt1(option, value)
|
||||
: Opt2(option, value);
|
||||
}
|
||||
|
||||
public SysCommand Arg(Object argument)
|
||||
{
|
||||
return new SysCommand(Path, $"{Args} {QuoteArg(argument)}");
|
||||
}
|
||||
|
||||
private static String QuoteArg(Object argument)
|
||||
{
|
||||
var arg = argument.ToString();
|
||||
if (arg.Contains(" "))
|
||||
arg = arg.Quote(); // TODO: single quote?
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
public static implicit operator SysCommand(String cmd) => FromString(cmd);
|
||||
public static implicit operator SysCommand(SysPath path) => FromPath(path);
|
||||
|
||||
public static SysCommand operator %(SysCommand cmd, String argument) => cmd.Arg(argument);
|
||||
public static SysCommand operator -(SysCommand cmd, String option ) => cmd.Opt(option);
|
||||
public static SysCommand operator -(SysCommand cmd, (String opt, Object value) option ) => cmd.Opt(option.opt, option.value);
|
||||
|
||||
#region equality
|
||||
|
||||
public Boolean Equals(SysCommand other)
|
||||
{
|
||||
return Path.Equals(other.Path) && Args == other.Args;
|
||||
}
|
||||
|
||||
public override Boolean Equals(Object obj)
|
||||
{
|
||||
return obj is SysCommand other && Equals(other);
|
||||
}
|
||||
|
||||
public override Int32 GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Path.GetHashCode() * 397) ^ (Args != null ? Args.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion equality
|
||||
|
||||
public void Deconstruct(out SysPath path, out String args)
|
||||
{
|
||||
path = Path;
|
||||
args = Args;
|
||||
}
|
||||
|
||||
public override String ToString() => $"{Path}{Args}";
|
||||
}
|
|
@ -6,22 +6,9 @@ namespace InnovEnergy.Lib.SysTools;
|
|||
[Obsolete]
|
||||
public static class SysDirs
|
||||
{
|
||||
|
||||
// TODO conditional compilation
|
||||
|
||||
/*
|
||||
public static SysPath ProgramDirectory => ExecutingAssemblyDirectory.Parent.Head == "bin" && (ExecutingAssemblyDirectory.Head == "Debug" || ExecutingAssemblyDirectory.Head == "Release")
|
||||
? ExecutingAssemblyDirectory.Parent.Parent
|
||||
: ExecutingAssemblyDirectory;
|
||||
|
||||
public static SysPath ExecutingAssemblyDirectory => ExecutingAssembly.Parent;
|
||||
public static SysPath ExecutingAssembly => Assembly.GetExecutingAssembly().Location.Apply(SysPath.FromUri);
|
||||
*/
|
||||
public static SysPath CurrentDirectory => Environment.CurrentDirectory;
|
||||
public static SysPath TempDirectory => System.IO.Path.GetTempPath();
|
||||
public static SysPath TempDirectory => Path.GetTempPath();
|
||||
|
||||
public static SysPath UserHomeDirectory { get; } = GetFolderPath(UserProfile);
|
||||
public static SysPath UserDesktopDirectory { get; } = GetFolderPath(DesktopDirectory);
|
||||
|
||||
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
using System.Text;
|
||||
using InnovEnergy.Lib.SysTools.Remote;
|
||||
using InnovEnergy.Lib.SysTools.Utils;
|
||||
using InnovEnergy.Lib.Utils;
|
||||
using static System.IO.Path;
|
||||
|
||||
namespace InnovEnergy.Lib.SysTools;
|
||||
|
||||
[Obsolete("Needs rework before use")]
|
||||
|
||||
public readonly struct SysPath
|
||||
{
|
||||
private readonly String _Path;
|
||||
|
@ -197,11 +196,10 @@ public readonly struct SysPath
|
|||
return $"{_Path}/{right}";
|
||||
}
|
||||
|
||||
public RemotePath At(SshHost host) => new RemotePath(host, this);
|
||||
|
||||
#region overrides
|
||||
|
||||
public override Boolean Equals(Object obj) => _Path.Equals(obj?.ToString());
|
||||
public override Boolean Equals(Object? obj) => _Path.Equals(obj?.ToString());
|
||||
public override Int32 GetHashCode() => _Path.GetHashCode();
|
||||
public override String ToString() => _Path;
|
||||
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
internal static class ConsoleUtils
|
||||
{
|
||||
public static T WriteLine<T>(this T t, ConsoleColor color)
|
||||
{
|
||||
var c = Console.ForegroundColor;
|
||||
|
||||
Console.ForegroundColor = color;
|
||||
Console.WriteLine(t);
|
||||
Console.ForegroundColor = c;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static T WriteLine<T>(this T t)
|
||||
{
|
||||
Console.WriteLine(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
public static T WriteLine<T>(this T t, ConsoleColor color, params String[] more)
|
||||
{
|
||||
var c = Console.ForegroundColor;
|
||||
|
||||
Console.ForegroundColor = color;
|
||||
Console.WriteLine(t + " " + more.JoinWith(" "));
|
||||
Console.ForegroundColor = c;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static T WriteLine<T>(this T t, params String[] more)
|
||||
{
|
||||
Console.WriteLine(t + " " + more.JoinWith(" "));
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
public static T Write<T>(this T t, ConsoleColor color)
|
||||
{
|
||||
var c = Console.ForegroundColor;
|
||||
|
||||
Console.ForegroundColor = color;
|
||||
Console.Write(t);
|
||||
Console.ForegroundColor = c;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static T Write<T>(this T t)
|
||||
{
|
||||
Console.Write(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
public static T Write<T>(this T t, ConsoleColor color, params String[] more)
|
||||
{
|
||||
var c = Console.ForegroundColor;
|
||||
|
||||
Console.ForegroundColor = color;
|
||||
Console.Write(t + " " + more.JoinWith(" "));
|
||||
Console.ForegroundColor = c;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static T Write<T>(this T t, params String[] more)
|
||||
{
|
||||
Console.Write(t + " " + more.JoinWith(" "));
|
||||
return t;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
internal static class EnumerableUtils
|
||||
{
|
||||
|
||||
public static IEnumerable<T> Pad<T>(this IEnumerable<T> src, Int32 length, T padding)
|
||||
{
|
||||
using var enumerator = src.GetEnumerator();
|
||||
while (enumerator.MoveNext() && length-- > 0)
|
||||
yield return enumerator.Current;
|
||||
|
||||
while (length-- > 0)
|
||||
yield return padding;
|
||||
}
|
||||
|
||||
public static Dictionary<T, IReadOnlyList<T>> IndexColumn<T>(this IEnumerable<IEnumerable<T>> src, UInt16 index)
|
||||
{
|
||||
var d = new Dictionary<T, IReadOnlyList<T>>();
|
||||
|
||||
foreach (var outer in src)
|
||||
{
|
||||
var inner = outer.ToList();
|
||||
var key = inner[index];
|
||||
d.Add(key, inner);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<(TLeft left, TRight right)> Zip<TLeft, TRight>(IEnumerable<TLeft> left,
|
||||
IEnumerable<TRight> right)
|
||||
{
|
||||
using var l = left.GetEnumerator();
|
||||
using var r = right.GetEnumerator();
|
||||
while (l.MoveNext() && r.MoveNext())
|
||||
yield return (l.Current, r.Current);
|
||||
}
|
||||
|
||||
public static IEnumerator<T> Enumerator<T>(this T t)
|
||||
{
|
||||
yield return t;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Enumerable<T>(this T t)
|
||||
{
|
||||
yield return t;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> src) => src.SelectMany(s => s);
|
||||
|
||||
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
|
||||
{
|
||||
foreach (var e in enumerable)
|
||||
action(e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static IEnumerable<T> WhereNot<T>(this IEnumerable<T> enumerable, Func<T,Boolean> predicate)
|
||||
{
|
||||
return enumerable.Where(e => !predicate(e));
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> enumerable)
|
||||
{
|
||||
return enumerable ?? new T[0];
|
||||
}
|
||||
|
||||
// // https://stackoverflow.com/a/34006336/141397
|
||||
// public static Int32 CombineHashes(this ITuple tupled, Int32 seed = 1009, Int32 factor = 9176)
|
||||
// {
|
||||
// var hash = seed;
|
||||
//
|
||||
// for (var i = 0; i < tupled.Length; i++)
|
||||
// {
|
||||
// unchecked
|
||||
// {
|
||||
// hash = hash * factor + tupled[i].GetHashCode();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return hash;
|
||||
// }
|
||||
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
internal static class StringUtils
|
||||
{
|
||||
|
||||
public static String NewLineBefore(this String s)
|
||||
{
|
||||
return Environment.NewLine + s;
|
||||
}
|
||||
|
||||
public static String AddTitle(this String s, String title)
|
||||
{
|
||||
return title.Underline() + s;
|
||||
}
|
||||
|
||||
public static String Underline(this String s)
|
||||
{
|
||||
return s.NewLine() + "".PadRight(s.Length, '-').NewLine(2);
|
||||
}
|
||||
|
||||
public static Boolean IsNullOrEmpty(this String s)
|
||||
{
|
||||
return String.IsNullOrEmpty(s);
|
||||
}
|
||||
|
||||
public static Boolean IsNullOrWhiteSpace(this String s)
|
||||
{
|
||||
return String.IsNullOrWhiteSpace(s);
|
||||
}
|
||||
|
||||
public static String Format(this Object value, String fmt)
|
||||
{
|
||||
return String.Format(fmt, value);
|
||||
}
|
||||
|
||||
public static String NewLine(this String s)
|
||||
{
|
||||
return s + Environment.NewLine;
|
||||
}
|
||||
|
||||
public static String NewLine(this String s, Int32 number)
|
||||
{
|
||||
return Enumerable
|
||||
.Repeat(Environment.NewLine, number)
|
||||
.Aggregate(s, (a, b) => a + b);
|
||||
}
|
||||
|
||||
public static String AppendSpaces(this String s, Int32 count = 4)
|
||||
{
|
||||
return s.PadRight(s.Length + count);
|
||||
}
|
||||
|
||||
public static String Append(this String s, String other)
|
||||
{
|
||||
return s + other;
|
||||
}
|
||||
|
||||
public static String Concat(this IEnumerable<String> ss)
|
||||
{
|
||||
return String.Join("", ss);
|
||||
}
|
||||
|
||||
public static String SurroundWith(this String s, String before, String after = null)
|
||||
{
|
||||
return before + s + (after ?? before);
|
||||
}
|
||||
|
||||
|
||||
public static String ShellSingleQuote(this String s)
|
||||
{
|
||||
return $"'{s.Replace("'", @"'\''")}'";
|
||||
}
|
||||
|
||||
public static String Quote(this String s)
|
||||
{
|
||||
return s.Replace(@"\", @"\\").Replace("\"", "\\\"").SurroundWith("\"");
|
||||
}
|
||||
|
||||
public static String Join(this IEnumerable<String> strings)
|
||||
{
|
||||
return String.Join("", strings);
|
||||
}
|
||||
|
||||
public static String JoinWith(this IEnumerable<String> strings, String separator)
|
||||
{
|
||||
return String.Join(separator, strings);
|
||||
}
|
||||
|
||||
public static String JoinToLines(this IEnumerable<String> lines)
|
||||
{
|
||||
return String.Join(Environment.NewLine, lines);
|
||||
}
|
||||
|
||||
public static String JoinToLines(params String[] lines)
|
||||
{
|
||||
return String.Join(Environment.NewLine, lines);
|
||||
}
|
||||
|
||||
public static String Indent(this String text)
|
||||
{
|
||||
return " ".SideBySideWith(text, "");
|
||||
}
|
||||
|
||||
public static String Indent(this String text, String indentor)
|
||||
{
|
||||
return indentor.SideBySideWith(text);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<String> SplitLines(this String s)
|
||||
{
|
||||
return s.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
}
|
||||
|
||||
public static String SideBySideWith(this String left, String right, String separator = " ")
|
||||
{
|
||||
var leftLines = left .Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
|
||||
var rightLines = right.Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
|
||||
|
||||
var leftWidth = leftLines.Count != 0
|
||||
? leftLines.Max(l => l.Length)
|
||||
: 0;
|
||||
|
||||
var nLines = Math.Max(leftLines.Count, rightLines.Count);
|
||||
|
||||
var leftPadded = leftLines .Pad(nLines, "").Select(l => l.PadRight(leftWidth));
|
||||
var rightPadded = rightLines.Pad(nLines, "");
|
||||
|
||||
return Enumerable
|
||||
.Zip(leftPadded, rightPadded, (l, r) => String.Join(separator, l, r))
|
||||
.JoinToLines();
|
||||
}
|
||||
|
||||
|
||||
public static String JoinWith<T>(this IEnumerable<T> args, String separator)
|
||||
{
|
||||
return String.Join(separator, args);
|
||||
}
|
||||
|
||||
public static IEnumerable<String> GetLinesStartingWith(this IEnumerable<String> lines, String prefix)
|
||||
{
|
||||
return lines.Where(l => l.StartsWith(prefix));
|
||||
}
|
||||
|
||||
public static String GetLineStartingWith(this IEnumerable<String> lines, String prefix)
|
||||
{
|
||||
return lines
|
||||
.GetLinesStartingWith(prefix)
|
||||
.Single();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static ILookup<String, String> ParseKeyValuesTextFile(this SysPath file)
|
||||
{
|
||||
var lines = file
|
||||
.ReadLines()
|
||||
.Select(l => l.Trim())
|
||||
.Where(l => l.Length > 0 && !l.StartsWith("#") && !l.StartsWith(";"));
|
||||
|
||||
var keyValuePairs = from line in lines
|
||||
let fields = line.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)
|
||||
let key = fields[0]
|
||||
from value in fields.Skip(1)
|
||||
select (key, value);
|
||||
|
||||
return keyValuePairs.ToLookup(e => e.key, e => e.value);
|
||||
}
|
||||
|
||||
public static Boolean EndsWith(this String str, Char c)
|
||||
{
|
||||
if (str.Length <= 0)
|
||||
return false;
|
||||
|
||||
return str[str.Length - 1] == c;
|
||||
}
|
||||
|
||||
public static String CommonPrefix(String str, params String[] more)
|
||||
{
|
||||
var prefixLength = str
|
||||
.TakeWhile((c, i) => more.All(s => i < s.Length && s[i] == c))
|
||||
.Count();
|
||||
|
||||
return str.Substring(0, prefixLength);
|
||||
}
|
||||
|
||||
|
||||
public static String AfterFirst(this String str, Char separator)
|
||||
{
|
||||
return str.Substring(str.IndexOf(separator) + 1);
|
||||
}
|
||||
|
||||
public static String AfterLast(this String str, Char separator)
|
||||
{
|
||||
return str.Substring(str.LastIndexOf(separator) + 1);
|
||||
}
|
||||
|
||||
public static String UntilLast(this String str, Char separator)
|
||||
{
|
||||
return str.Substring(0, str.LastIndexOf(separator));
|
||||
}
|
||||
|
||||
public static String UntilFirst(this String str, Char separator)
|
||||
{
|
||||
return str.Substring(0, str.IndexOf(separator));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
namespace InnovEnergy.Lib.SysTools.Utils;
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
|
||||
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public static DateTime FromUnixTime(UInt64 unixTime)
|
||||
{
|
||||
return Epoch.AddSeconds(unixTime);
|
||||
}
|
||||
|
||||
public static R ValueOrDefault<T, R>(this Dictionary<T, R> dict, T key)
|
||||
{
|
||||
return ValueOrDefault(dict, key, default);
|
||||
}
|
||||
|
||||
public static R ValueOrDefault<T, R>(this Dictionary<T, R> dict, T key, R defaultValue)
|
||||
{
|
||||
return dict.TryGetValue(key, out var value) ? value : defaultValue;
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(String source, String target)
|
||||
{
|
||||
CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target));
|
||||
}
|
||||
|
||||
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
|
||||
{
|
||||
foreach (var file in source.GetFiles())
|
||||
file.CopyTo(Path.Combine(target.FullName, file.Name));
|
||||
|
||||
foreach (var dir in source.GetDirectories())
|
||||
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue