diff --git a/CliWrap.Magic/FormattedToString.cs b/CliWrap.Magic/FormattedToString.cs new file mode 100644 index 00000000..93951a45 --- /dev/null +++ b/CliWrap.Magic/FormattedToString.cs @@ -0,0 +1,23 @@ +using System; +using System.Globalization; + +namespace CliWrap.Magic; + +public readonly partial struct FormattedToString +{ + public string Value { get; } + + public FormattedToString(string value) => Value = value; +} + +public partial struct FormattedToString +{ + public static implicit operator FormattedToString(string value) => new(value); + + public static implicit operator FormattedToString(bool value) => new(value.ToString()); + + public static implicit operator FormattedToString(IFormattable value) => + new(value.ToString(null, CultureInfo.InvariantCulture)); + + public static implicit operator string(FormattedToString value) => value.Value; +} \ No newline at end of file diff --git a/CliWrap.Magic/Readme.md b/CliWrap.Magic/Readme.md index 144f2926..fc8cfd44 100644 --- a/CliWrap.Magic/Readme.md +++ b/CliWrap.Magic/Readme.md @@ -11,14 +11,60 @@ ## Usage -**CliWrap.Magic** provides a static `Shell` class that contains a various methods for creating and executing commands. -The recommended way to use it is by statically importing the class: +### Quick overview + +Add `using static CliWrap.Magic.Shell;` to your file and start writing scripts like this: ```csharp -using CliWrap.Magic; using static CliWrap.Magic.Shell; -// ... +// Create commands using the _() method, execute them simply by awaiting. +// Check for exit code directly in if statements. +if (!await _("git")) +{ + WriteErrorLine("Git is not installed"); + Exit(1); + return; +} + +// Executing a command returns an object which has implicit conversions to: +// - int (exit code) +// - bool (exit code == 0) +// - string (standard output) +string version = await _("git", "--version"); // git version 2.43.0.windows.1 +WriteLine($"Git version: {version}"); + +// Just like with regular CliWrap, arguments are automatically +// escaped to form a well-formed command line string. +// Non-string arguments of many different types can also be passed directly. +await _("git", "clone", "https://github.com/Tyrrrz/CliWrap", "--depth", 0); + +// Resolve environment variables easily with the Environment() method. +var commit = Environment("HEAD_SHA"); + +// Prompt the user for additional input with the Prompt() method. +if (string.IsNullOrWhiteSpace(commit)) + commit = Prompt("Enter commit hash"); + +// Just like with regular CliWrap, arguments are automatically +// escaped to form a well-formed command line string. +await _("git", "checkout", commit); + +// Set environment variables using the Environment() method. +// This returns an object that you can dispose to restore the original value. +using (Environment("HEAD_SHA", "deadbeef")) + await _("/bin/sh", "-c", "echo $HEAD_SHA"); // deadbeef + +// Same with the WorkingDirectory() method. +using (WorkingDirectory("/tmp/my-script/")) + // Get the current working directory using the same method. + var cwd = WorkingDirectory(); + +// Magic also supports CliWrap's piping syntax. +var commits = new List(); // this will contain commit hashes +await ( + _("git", "log", "--pretty=format:%H") | commits.Add +); ``` ### Executing commands @@ -37,6 +83,4 @@ Piping works the same way as it does in regular **CliWrap**: ```csharp await ("standard input" | _("dotnet", "run")); -``` - -### Tools \ No newline at end of file +``` \ No newline at end of file diff --git a/CliWrap.Magic/Shell.cs b/CliWrap.Magic/Shell.cs index b7520f8f..3f0a5e48 100644 --- a/CliWrap.Magic/Shell.cs +++ b/CliWrap.Magic/Shell.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using CliWrap.Magic.Utils; namespace CliWrap.Magic; @@ -48,6 +49,18 @@ public static Command _(string targetFilePath, IEnumerable arguments) => /// public static Command _(string targetFilePath, params string[] arguments) => _(targetFilePath, (IEnumerable)arguments); + + /// + /// Creates a new command with the specified target file path and command-line arguments. + /// + public static Command _(string targetFilePath, IEnumerable arguments) => + _(targetFilePath).WithArguments(a => a.Add(arguments.Select(x => x.Value))); + + /// + /// Creates a new command with the specified target file path and command-line arguments. + /// + public static Command _(string targetFilePath, params FormattedToString[] arguments) => + _(targetFilePath, (IEnumerable)arguments); /// /// Gets the current working directory. @@ -87,4 +100,40 @@ public static IDisposable Environment(string name, string? value) return Disposable.Create(() => System.Environment.SetEnvironmentVariable(name, lastValue)); } + + /// + /// Terminates the current process with the specified exit code. + /// + public static void Exit(int exitCode = 0) => System.Environment.Exit(exitCode); + + /// + /// Prompt the user for input, with an optional message. + /// + public static string? Prompt(string? message = null) + { + if (!string.IsNullOrWhiteSpace(message)) + Console.Write(message); + + return Console.ReadLine(); + } + + /// + /// Writes the specified text to the standard output stream. + /// + public static void Write(string text) => Console.Write(text); + + /// + /// Writes the specified text to the standard output stream, followed by a line terminator. + /// + public static void WriteLine(string text) => Write(text + System.Environment.NewLine); + + /// + /// Writes the specified text to the standard error stream. + /// + public static void WriteError(string text) => Console.Error.Write(text); + + /// + /// Writes the specified text to the standard error stream, followed by a line terminator. + /// + public static void WriteErrorLine(string text) => WriteError(text + System.Environment.NewLine); }