nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at release-25.11 339 lines 8.8 kB view raw
1{ lib }: 2 3{ 4 /** 5 Automatically convert an attribute set to command-line options. 6 7 This helps protect against malformed command lines and also to reduce 8 boilerplate related to command-line construction for simple use cases. 9 10 `toGNUCommandLineShell` returns an escaped shell string. 11 12 # Inputs 13 14 `options` 15 16 : How to format the arguments, see `toGNUCommandLine` 17 18 `attrs` 19 20 : The attributes to transform into arguments. 21 22 # Examples 23 24 :::{.example} 25 ## `lib.cli.toGNUCommandLineShell` usage example 26 27 ```nix 28 cli.toGNUCommandLineShell {} { 29 data = builtins.toJSON { id = 0; }; 30 X = "PUT"; 31 retry = 3; 32 retry-delay = null; 33 url = [ "https://example.com/foo" "https://example.com/bar" ]; 34 silent = false; 35 verbose = true; 36 } 37 => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; 38 ``` 39 40 ::: 41 */ 42 toGNUCommandLineShell = 43 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511) 44 "lib.cli.toGNUCommandLineShell is deprecated, please use lib.cli.toCommandLineShell or lib.cli.toCommandLineShellGNU instead." 45 (options: attrs: lib.escapeShellArgs (lib.cli.toGNUCommandLine options attrs)); 46 47 /** 48 Automatically convert an attribute set to a list of command-line options. 49 50 `toGNUCommandLine` returns a list of string arguments. 51 52 # Inputs 53 54 `options` 55 56 : How to format the arguments, see below. 57 58 `attrs` 59 60 : The attributes to transform into arguments. 61 62 ## Options 63 64 `mkOptionName` 65 66 : How to string-format the option name; 67 By default one character is a short option (`-`), more than one characters a long option (`--`). 68 69 `mkBool` 70 71 : How to format a boolean value to a command list; 72 By default its a flag option (only the option name if true, left out completely if false). 73 74 `mkList` 75 76 : How to format a list value to a command list; 77 By default the option name is repeated for each value and `mkOption` is applied to the values themselves. 78 79 `mkOption` 80 81 : How to format any remaining value to a command list; 82 On the toplevel, booleans and lists are handled by `mkBool` and `mkList`, though they can still appear as values of a list. 83 By default, everything is printed verbatim and complex types are forbidden (lists, attrsets, functions). `null` values are omitted. 84 85 `optionValueSeparator` 86 87 : How to separate an option from its flag; 88 By default, there is no separator, so option `-c` and value `5` would become ["-c" "5"]. 89 This is useful if the command requires equals, for example, `-c=5`. 90 91 # Examples 92 93 :::{.example} 94 ## `lib.cli.toGNUCommandLine` usage example 95 96 ```nix 97 cli.toGNUCommandLine {} { 98 data = builtins.toJSON { id = 0; }; 99 X = "PUT"; 100 retry = 3; 101 retry-delay = null; 102 url = [ "https://example.com/foo" "https://example.com/bar" ]; 103 silent = false; 104 verbose = true; 105 } 106 => [ 107 "-X" "PUT" 108 "--data" "{\"id\":0}" 109 "--retry" "3" 110 "--url" "https://example.com/foo" 111 "--url" "https://example.com/bar" 112 "--verbose" 113 ] 114 ``` 115 116 ::: 117 */ 118 toGNUCommandLine = 119 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511) 120 "lib.cli.toGNUCommandLine is deprecated, please use lib.cli.toCommandLine or lib.cli.toCommandLineShellGNU instead." 121 ( 122 { 123 mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}", 124 125 mkBool ? k: v: lib.optional v (mkOptionName k), 126 127 mkList ? k: v: lib.concatMap (mkOption k) v, 128 129 mkOption ? 130 k: v: 131 if v == null then 132 [ ] 133 else if optionValueSeparator == null then 134 [ 135 (mkOptionName k) 136 (lib.generators.mkValueStringDefault { } v) 137 ] 138 else 139 [ "${mkOptionName k}${optionValueSeparator}${lib.generators.mkValueStringDefault { } v}" ], 140 141 optionValueSeparator ? null, 142 }: 143 options: 144 let 145 render = 146 k: v: 147 if builtins.isBool v then 148 mkBool k v 149 else if builtins.isList v then 150 mkList k v 151 else 152 mkOption k v; 153 154 in 155 builtins.concatLists (lib.mapAttrsToList render options) 156 ); 157 158 /** 159 Converts the given attributes into a single shell-escaped command-line string. 160 Similar to `toCommandLineGNU`, but returns a single escaped string instead of an array of arguments. 161 For further reference see: [`lib.cli.toCommandLineGNU`](#function-library-lib.cli.toCommandLineGNU) 162 */ 163 toCommandLineShellGNU = 164 options: attrs: lib.escapeShellArgs (lib.cli.toCommandLineGNU options attrs); 165 166 /** 167 Converts an attribute set into a list of GNU-style command line options. 168 169 `toCommandLineGNU` returns a list of string arguments. 170 171 # Inputs 172 173 `options` 174 175 : Options, see below. 176 177 `attrs` 178 179 : The attributes to transform into arguments. 180 181 ## Options 182 183 `isLong` 184 185 : A function that determines whether an option is long or short. 186 187 `explicitBool` 188 189 : Whether or not boolean option arguments should be formatted explicitly. 190 191 `formatArg` 192 193 : A function that turns the option argument into a string. 194 195 # Examples 196 197 :::{.example} 198 ## `lib.cli.toCommandLineGNU` usage example 199 200 ```nix 201 lib.cli.toCommandLineGNU {} { 202 v = true; 203 verbose = [true true false null]; 204 i = ".bak"; 205 testsuite = ["unit" "integration"]; 206 e = ["s/a/b/" "s/b/c/"]; 207 n = false; 208 data = builtins.toJSON {id = 0;}; 209 } 210 => [ 211 "--data={\"id\":0}" 212 "-es/a/b/" 213 "-es/b/c/" 214 "-i.bak" 215 "--testsuite=unit" 216 "--testsuite=integration" 217 "-v" 218 "--verbose" 219 "--verbose" 220 ] 221 ``` 222 223 ::: 224 */ 225 toCommandLineGNU = 226 { 227 isLong ? optionName: builtins.stringLength optionName > 1, 228 explicitBool ? false, 229 formatArg ? lib.generators.mkValueStringDefault { }, 230 }: 231 let 232 optionFormat = optionName: { 233 option = if isLong optionName then "--${optionName}" else "-${optionName}"; 234 sep = if isLong optionName then "=" else ""; 235 inherit explicitBool formatArg; 236 }; 237 in 238 lib.cli.toCommandLine optionFormat; 239 240 /** 241 Converts the given attributes into a single shell-escaped command-line string. 242 Similar to `toCommandLine`, but returns a single escaped string instead of an array of arguments. 243 For further reference see: [`lib.cli.toCommandLine`](#function-library-lib.cli.toCommandLine) 244 */ 245 toCommandLineShell = 246 optionFormat: attrs: lib.escapeShellArgs (lib.cli.toCommandLine optionFormat attrs); 247 248 /** 249 Converts an attribute set into a list of command line options. 250 251 `toCommandLine` returns a list of string arguments. 252 253 # Inputs 254 255 `optionFormat` 256 257 : The option format that describes how options and their arguments should be formatted. 258 259 `attrs` 260 261 : The attributes to transform into arguments. 262 263 # Examples 264 :::{.example} 265 ## `lib.cli.toCommandLine` usage example 266 267 ```nix 268 let 269 optionFormat = optionName: { 270 option = "-${optionName}"; 271 sep = "="; 272 explicitBool = true; 273 }; 274 in lib.cli.toCommandLine optionFormat { 275 v = true; 276 verbose = [true true false null]; 277 i = ".bak"; 278 testsuite = ["unit" "integration"]; 279 e = ["s/a/b/" "s/b/c/"]; 280 n = false; 281 data = builtins.toJSON {id = 0;}; 282 } 283 => [ 284 "-data={\"id\":0}" 285 "-e=s/a/b/" 286 "-e=s/b/c/" 287 "-i=.bak" 288 "-n=false" 289 "-testsuite=unit" 290 "-testsuite=integration" 291 "-v=true" 292 "-verbose=true" 293 "-verbose=true" 294 "-verbose=false" 295 ] 296 ``` 297 298 ::: 299 */ 300 toCommandLine = 301 optionFormat: attrs: 302 let 303 handlePair = 304 k: v: 305 if k == "" then 306 lib.throw "lib.cli.toCommandLine only accepts non-empty option names." 307 else if builtins.isList v then 308 builtins.concatMap (handleOption k) v 309 else 310 handleOption k v; 311 312 handleOption = k: renderOption (optionFormat k) k; 313 314 renderOption = 315 { 316 option, 317 sep, 318 explicitBool, 319 formatArg ? lib.generators.mkValueStringDefault { }, 320 }: 321 k: v: 322 if v == null || (!explicitBool && v == false) then 323 [ ] 324 else if !explicitBool && v == true then 325 [ option ] 326 else 327 let 328 arg = formatArg v; 329 in 330 if sep != null then 331 [ "${option}${sep}${arg}" ] 332 else 333 [ 334 option 335 arg 336 ]; 337 in 338 builtins.concatLists (lib.mapAttrsToList handlePair attrs); 339}