From 002ec5da20092006388b9f44fc6bbaa19bfe3833 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 23 Jan 2026 14:48:03 -0800 Subject: [PATCH] Help text --- README.md | 61 +++++++++++++++++++++++++---- flake.nix | 112 ++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 138 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index f00482d..7b47ddd 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,14 @@ Helpers for defining commands for Nix shells under Flakes. }; outputs = {self, flake-utils, command-utils}: - flake-utils.lib.eachDefaultSystem (system: + flake-utils.lib.eachDefaultSystem (system: let # Cargo just for example cargo = "${pkgs.cargo}/bin/cargo"; - + cmd = command-utils.cmd.${system}; + withHelp = command-utils.withHelp.${system}; + command_menu = command-utils.commands.${system} { hello = cmd "Print a hello world message" "echo 'Hello, world!'"; @@ -39,7 +41,7 @@ Helpers for defining commands for Nix shells under Flakes. command_menu # ... ]; - + #... }; } @@ -55,12 +57,57 @@ $ menu | |__| (_) | | | | | | | | | | | (_| | | | | (_| \__ \ \____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|___/ - -bench | Run benchmarks, including test utils -bench:host | Run host Criterion benchmarks -hello | Print a hello world message +┌────────────┬───────────────────────────────────┐ +│ Command │ Description │ +╞════════════╪═══════════════════════════════════╡ +│ bench │ Run benchmarks, including test utils │ +├────────────┼───────────────────────────────────┤ +│ bench:host │ Run host Criterion benchmarks │ +├────────────┼───────────────────────────────────┤ +│ hello │ Print a hello world message │ +└────────────┴───────────────────────────────────┘ $ hello +⚙️ Running hello... Hello, world! ``` +## Adding Help Text + +Use `withHelp` to add detailed help text accessible via `--help` or `-h`: + +``` nix +command_menu = command-utils.commands.${system} { + hello = cmd "Print a hello world message" "echo 'Hello, world!'"; + + bench = withHelp '' + Usage: bench [OPTIONS] + + Options: + --release Run in release mode + --save FILE Save results to FILE + + Examples: + bench --release + bench --save baseline.json + '' (cmd "Run benchmarks" "${cargo} bench"); +}; +``` + +``` console +$ bench --help +bench - Run benchmarks + +Usage: bench [OPTIONS] + +Options: + --release Run in release mode + --save FILE Save results to FILE + +Examples: + bench --release + bench --save baseline.json +``` + +Commands without `withHelp` pass `--help` through to the underlying script. + diff --git a/flake.nix b/flake.nix index 4a8bb6f..1f8c078 100644 --- a/flake.nix +++ b/flake.nix @@ -10,19 +10,33 @@ let pkgs = import nixpkgs { inherit system; }; - cmd = description: script: { inherit description script; }; + cmd = description: script: { inherit description script; help = null; }; - command = { name, script, description ? "" }: - { - inherit name description; + withHelp = help: def: def // { inherit help; }; - package = - pkgs.writeScriptBin name '' - #!${pkgs.bash}/bin/bash - echo "⚙️ Running ${name}..." - ${script} - ''; - }; + command = { name, script, description ? "", help ? null }: + let + helpHandler = + if help != null then '' + if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then + echo "${name} - ${description}" + echo "" + echo "${help}" + exit 0 + fi + '' else ""; + in + { + inherit name description; + + package = + pkgs.writeScriptBin name '' + #!${pkgs.bash}/bin/bash + ${helpHandler} + echo "⚙️ Running ${name}..." + ${script} + ''; + }; commands = defs: let @@ -31,33 +45,75 @@ helper = let - lengths = map builtins.stringLength names; - maxLen = builtins.foldl' (acc: x: if x > acc then x else acc) 0 lengths; - maxPad = + # Calculate max widths + cmdHeader = "Command"; + descHeader = "Description"; + + nameLengths = map builtins.stringLength names; + descLengths = map (n: builtins.stringLength (builtins.getAttr n defs).description) names; + + maxNameLen = builtins.foldl' + (acc: x: if x > acc then x else acc) + (builtins.stringLength cmdHeader) + nameLengths; + + maxDescLen = builtins.foldl' + (acc: x: if x > acc then x else acc) + (builtins.stringLength descHeader) + descLengths; + + # Generate padding strings + repeat = char: len: let go = acc: - if builtins.stringLength acc >= maxLen - then acc - else go (" " + acc); + if builtins.stringLength acc >= len + then builtins.substring 0 len acc + else go (acc + char); in go ""; - folder = acc: name: + pad = str: width: let - nameLen = builtins.stringLength name; - padLen = maxLen - nameLen; - padding = builtins.substring 0 padLen maxPad; + strLen = builtins.stringLength str; + padLen = width - strLen; in - acc + " && echo '${name} ${padding}| ${(builtins.getAttr name defs).description}'"; + str + repeat " " padLen; + + # Table components (double line only for header separator) + hLineDouble = repeat "═"; + hLineSingle = repeat "─"; + + topBorder = "┌─${hLineSingle maxNameLen}─┬─${hLineSingle maxDescLen}─┐"; + headerSep = "╞═${hLineDouble maxNameLen}═╪═${hLineDouble maxDescLen}═╡"; + rowDivider = "├─${hLineSingle maxNameLen}─┼─${hLineSingle maxDescLen}─┤"; + bottomBorder = "└─${hLineSingle maxNameLen}─┴─${hLineSingle maxDescLen}─┘"; + headerRow = "│ ${pad cmdHeader maxNameLen} │ ${pad descHeader maxDescLen} │"; + + # Build data rows + mkRow = name: + let + desc = (builtins.getAttr name defs).description; + in + "│ ${pad name maxNameLen} │ ${pad desc maxDescLen} │"; + + dataRows = map mkRow names; + + # Interleave rows with dividers + interleave = rows: + if builtins.length rows <= 1 + then rows + else [ (builtins.head rows) rowDivider ] ++ interleave (builtins.tail rows); - lines = - builtins.foldl' folder "echo ''" names; + # Combine all lines + allLines = [ topBorder headerRow headerSep ] ++ interleave dataRows ++ [ bottomBorder ]; + echoLines = builtins.concatStringsSep " && " (map (l: "echo '${l}'") allLines); in pkgs.writeScriptBin "menu" '' #!${pkgs.stdenv.shell} ${pkgs.figlet}/bin/figlet "Commands" | ${pkgs.lolcat}/bin/lolcat - ${toString lines} + echo "" + ${echoLines} ''; mapper = name: @@ -67,8 +123,8 @@ task = command { inherit name; - description = element.description; - script = element.script; + inherit (element) description script; + help = element.help or null; }; in task.package; @@ -79,7 +135,7 @@ in [ helper ] ++ packages; in - { inherit cmd command commands; } + { inherit cmd withHelp command commands; } ); } -- 2.51.2