Skip to content

Commit

Permalink
feat: ParseCli and ExecutePipeAsync
Browse files Browse the repository at this point in the history
  • Loading branch information
emako committed Sep 24, 2024
1 parent 35c0eb1 commit 44a550a
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 18 deletions.
48 changes: 36 additions & 12 deletions src/Flucli.Test/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,46 @@ static void Main(string[] args)
{
Task.Run(async () =>
{
StringBuilder stdout = new();
StringBuilder stderr = new();
Console.WriteLine("CASE1");
{
StringBuilder stdout = new();
StringBuilder stderr = new();

var command1 = "cmd"
.WithArguments("/c echo Hello World!");
var command1 = "cmd"
.WithArguments("/c echo Hello World!");

var command2 = "cmd"
.WithArguments("/c findstr o")
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdout, Encoding.UTF8))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(stderr, Encoding.UTF8));
var command2 = "cmd"
.WithArguments("/c findstr o")
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdout, Encoding.UTF8))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(stderr, Encoding.UTF8));

CliResult result = await (command1 | command2).ExecuteAsync();
CliResult result = await (command1 | command2).ExecuteAsync();

Console.WriteLine("STDOUT: " + stdout.ToString());
Console.WriteLine("STDERR: " + stderr.ToString());
Console.WriteLine("ExitCode is " + result.ExitCode);
Console.WriteLine("STDOUT: " + stdout.ToString());
Console.WriteLine("STDERR: " + stderr.ToString());
Console.WriteLine("ExitCode is " + result.ExitCode);
}
Console.WriteLine("---");

Console.WriteLine("CASE2");
{
StringBuilder stdout = new();
StringBuilder stderr = new();

Cli command = "cmd /c echo Follow | cmd /c findstr F | cmd /c findstr l*"
.ParseCli()
.PipeTail // Switch to tail command
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdout, Encoding.UTF8))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(stderr, Encoding.UTF8))
.PipeHeader; // Switch to header command

CliResult result = await command.ExecutePipeAsync();

Console.WriteLine("STDOUT: " + stdout.ToString());
Console.WriteLine("STDERR: " + stderr.ToString());
Console.WriteLine("ExitCode is " + result.ExitCode);
}
Console.WriteLine("---");
});

Console.ReadLine();
Expand Down
74 changes: 69 additions & 5 deletions src/Flucli/Cli.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,44 @@ public class Cli : ICli
public PipeTarget _standardErrorPipe = PipeTarget.Null;
public PipeTarget StandardErrorPipe => _standardErrorPipe;

/// <summary>
/// Only stock the cli parse result from <see cref="CliExtensions.ParseCli"/> here.
/// **No automatic execution of pipe.**
/// **You have to run it by yourself.**
/// </summary>
public Cli PipeTo { get; set; } = null!;

public Cli PipeTail
{
get
{
Cli current = this;
while (current != null && current.PipeTo != null)
{
current = current.PipeTo;
}
return current!;
}
}

/// <summary>
/// Only stock the cli parse result from <see cref="CliExtensions.ParseCli"/> here.
/// </summary>
public Cli PipeFrom { get; set; } = null!;

public Cli PipeHeader
{
get
{
Cli current = this;
while (current != null && current.PipeFrom != null)
{
current = current.PipeFrom;
}
return current!;
}
}

public Cli() : this(default(string)!)
{
}
Expand Down Expand Up @@ -220,15 +258,29 @@ public Cli WithStandardErrorPipe(PipeTarget target)
return this;
}

public Task<CliResult> ExecuteAsync(CancellationToken cancellationToken = default) =>
ExecuteAsync(cancellationToken, CancellationToken.None);
public async Task<CliResult> ExecutePipeAsync(CancellationToken cancellationToken = default)
{
Cli current = this;
Cli compositeCli = current;

while (current.PipeTo != null)
{
compositeCli = compositeCli | current.PipeTo;
current = current.PipeTo;
}

return await compositeCli.ExecuteAsync(cancellationToken);
}

public async Task<CliResult> ExecuteAsync(CancellationToken cancellationToken = default) =>
await ExecuteAsync(cancellationToken, CancellationToken.None);

public Task<CliResult> ExecuteAsync(CancellationToken forcefulCancellationToken, CancellationToken gracefulCancellationToken)
public async Task<CliResult> ExecuteAsync(CancellationToken forcefulCancellationToken, CancellationToken gracefulCancellationToken)
{
ProcessEx process = new(CreateStartInfo());

process.Start();
return ExecuteAsync(process, forcefulCancellationToken, gracefulCancellationToken);
return await ExecuteAsync(process, forcefulCancellationToken, gracefulCancellationToken);
}

private async Task<CliResult> ExecuteAsync(ProcessEx process, CancellationToken forcefulCancellationToken = default, CancellationToken gracefulCancellationToken = default)
Expand Down Expand Up @@ -392,7 +444,19 @@ private ProcessStartInfo CreateStartInfo()
return startInfo;
}

public override string ToString() => $"{FileName} {Arguments}";
/// <summary>
/// Only return the command line string
/// </summary>
/// <returns>cli</returns>
public override string ToString()
{
if (PipeTo != null)
{
return $"{FileName.ToQuoteMarkArguments()} {Arguments} | {PipeTo}";
}

return $"{FileName.ToQuoteMarkArguments()} {Arguments}";
}

public static Cli operator |(Cli source, PipeTarget target)
=> source.WithStandardOutputPipe(target);
Expand Down
35 changes: 34 additions & 1 deletion src/Flucli/CliExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
namespace Flucli;
using Flucli.Utils.Extensions;
using System.Collections.Generic;
using System.Linq;

namespace Flucli;

public static class CliExtensions
{
Expand All @@ -7,8 +11,37 @@ public static Cli CreateCli(this string cli)
return new Cli(cli);
}

public static Cli CreateCli(this (string FileName, IEnumerable<string> Arguments) cli)
{
return new Cli(cli.FileName).WithArguments(cli.Arguments);
}

public static Cli WithArguments(this string cli, string arguments)
{
return new Cli(cli).WithArguments(arguments);
}

public static Cli ParseCli(this string cli)
{
cli = cli.Trim();

if (cli.Contains("|"))
{
IEnumerable<string> clis = cli.Split('|');
IEnumerable<Cli> chain = clis.Select(cli => CreateCli(cli.ToFileNameWithArguments()));

Cli header = chain.First();
_ = chain.Skip(1).Aggregate(header, (prev, next) =>
{
prev.PipeTo = next;
next.PipeFrom = prev;
return next;
});
return header;
}
else
{
return CreateCli(cli.ToFileNameWithArguments());
}
}
}
18 changes: 18 additions & 0 deletions src/Flucli/Utils/Extensions/ArgumentExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ public static string ToQuoteMarkArguments(this string arg, QuoteRepalce quoteTyp
return arg;
}

public static (string, IEnumerable<string>) ToFileNameWithArguments(this string cli)
{
IEnumerable<string> args = cli.ToArguments();

if (args.Count() == 0)
{
return (cli, []);
}
else if (args.Count() == 1)
{
return (args.First(), []);
}
else
{
return (args.First(), args.Skip(1));
}
}

public static SecureString ToSecureString(this string str)
{
SecureString secureString = new();
Expand Down

0 comments on commit 44a550a

Please sign in to comment.