vic/clap-nix
{ "defaultBranch": "main", "description": "Command line argument parser in pure Nix. Supports sub-commands, typed positional arguments, value coercion and resolution via Nix Modules.", "fullName": "vic/clap-nix", "homepage": "", "language": "Nix", "name": "clap-nix", "pushedAt": "2025-07-15T04:38:14Z", "stargazersCount": 7, "updatedAt": "2025-11-25T01:26:57Z", "url": "https://github.com/vic/clap-nix"}clap.nix - Command Line Argument Processing in Nix.
Section titled “clap.nix - Command Line Argument Processing in Nix.”This library provides a clap Nix function for parsing command line arguments into a Nix attribute set.
Features
Section titled “Features”-
The [implementation]!(lib/default.nix) and [tests]!(test) are pure Nix.
-
Familiar
--longand-short option styles. -
Boolean long options can be negated with
--no-surprises. -
Short options can be collapsed.
-abcequals-a -b -c. -
Option values can be any Nix data type, not only strings.
-
Nested trees of commands as popularized by tools like
git. -
A path of subcommands can be enabled by default. (eg, you can make
foo helpbe executed whenfooreceives no more arguments) -
Options are specified by virtue of Nix
lib.mkOptionandlib.types. Meaning your options can provide defaults, value coercions, aggregation or a composition of different types. -
Leverages the power of
lib.evalModulesso you can define option aliases (eg,-hand--helphaving the same value) or define your own config by providing custom Nix modules and uselib.mkIfand friends. -
Supports typed positional arguments on each command.
-
Distributed as a flake or legacy library.
-
Made with <3 by oeiuwq.
The slac tree made of { short ? {}, long ? {}, argv ? [], command ? {}, ...}
Section titled “The slac tree made of { short ? {}, long ? {}, argv ? [], command ? {}, ...}”An slac tree describes the structure of the command line interface
that will be parsed using the clap Nix function:
{ # an optional attribute set of one letter options short = { f = lib.mkOption { description = "file"; type = lib.types.path; }; };
# an optional attribute set of long options long = { help = lib.mkEnableOption "help"; };
# an optional list of positional typed arguments argv = [ lib.types.int (lib.types.separatedString ":") ];
# an optional attribute set of sub-commands and their `slac` tree. command = { show = { long = { pretty = lib.mkEnableOption "pretty print"; }; # ... other nested `short`, `command` or `argv` }; };}Calling the clap function.
Section titled “Calling the clap function.”Once you have your slac tree definition, you are ready to invoke clap with some
command line arguments.
{ clap, ... }:let slac = {...}; # the attribute set from the snippet above.
#### # The important thing on this snipped is how to invoke the `clap` function: # # The firsr argument is the `slac` tree structure that defines the CLI design. # Second argument is a list of Nix values (not just strings) representing # the user entered command line arguments. cli = clap slac [ "--help" ];in # More on `clap` return value in the following section. if cli.opts.long.help then # somehow help the user. else # actually do the thing.The clap return value.
Section titled “The clap return value.”The following is an annotated attribute set with the values returned to you by clap:
{ # A list of all arguments not processed by `clap` # Unknown options and unused values will be aggregated in this list. # Also, if `clap` finds the string `--` in the command line arguments, # it will stop further processing, so `--` and it's following arguments # will be in `rest` untouched. rest = [ "--" "skipped-values" ];
# Typically you'd want to inspect the `opts` attribute in order to # know what options the user assigned values to. # Notice that it basically follows the same structure a `slac` has. # # Note: Accessing `opts` will make sure that all options correspond to # their defined type, by virtue of using `lib.evalModules` -more on this later-, # and of course Nix will throw an error if some option has incorrect value type. opts = { # here you'll find `long` and `short` options assigned to their values. long = { help = false; }; # from `--no-help` short = { f = /home/vic/some-file; }; # from `-f /home/vic/some-file`
argv = [ 42 "foo:bar" ]; # from positional arguments matching types
# commands also map to their resolved values. command = { show = { enabled = true; # meaning the user specified the `show` command. long = { pretty = true; # from `show --pretty` }; }; };
}; # end opts
##-# That's it. The attributes bellow are lower level representations of the # `opts` set. But could be useful anyways to you:
optsSet = {}; # Another slac-like set. *BUT* this one is not type-checked at all.
optsMod = {}; # A Nix Module that contains all the options declarations and definitions. # # This one is useful if you want to mix with your own modules using `lib.evalModules` # for example, for creating option aliases or merging with other conditions. # # Actually `opts = (lib.evalModules { modules = [ optsMod ]; }).config`.
optsAcc = []; # A list of attribute sets that enable options and subcommands as they are seen. # This is the lowest level output, optsSet and optsMod are a by-product of it # and it is used directly mostly in tests bellow number 100 to assert the order # in which options are read from the command line.
}Examples
Section titled “Examples”Enabling a default subcommand
Section titled “Enabling a default subcommand”Enabling a default command means that the user does not have to explicitly name the subcommand yet they can specify the subcommand’s options directly. [see test]!(test/150-can-take-an-option-of-default-enabled-command-test.nix)
To enable a default command you can set it’s command.foo.enabled attribute to either a true boolean
or an option with default value of true.
{lib, ...}:let # an option that takes integers, not relevant to this example; intOption = mkOption { type = lib.types.int; };in{ short.a = intOption;
# auto-enable this command by default, so that the user can directly use `-b` without naming `foo` command.foo.enabled = true; command.foo.short.b = intOption;
# bar is not auto-enabled, user must explicitly the name `bar` command before setting `-c`. command.bar.short.c = intOption;
# since foo is enabled, and its baz subcommand is also enabled, the user could simply provide `-d` directly. command.foo.command.baz.enabled = true; command.foo.command.baz.short.d = intOption;}Other examples as tests.
Section titled “Other examples as tests.”Some other examples can be found in the [test]!(test/) directory.
Developing
Section titled “Developing”This repo checks for nixfmt on all .nix files.
Tests can be run using nix flake check -L --show-trace.
Adding more test by adding a 10th step consecutive -test.nix file inside the test/ directory.
Wait, but why?
Section titled “Wait, but why?”I know… Nix is a configuration language not a general purpose one. Who needs to parse command line arguments via pure-nix, right? That very person happens to be vic, like many other people I’ve been trying to learn Nix and configure my system with it.
Also I’m planning to release a nix-related tool soon and really wanted to get away from bash this time. So I’m just trying to program as much as I can in Nix. Yet I’m liking doing Nix a lot more than writing shell scripts with sed,grep,read,tr,awk,bashfulness.
Contributing
Section titled “Contributing”Yes, please. Pull-requests are more than welcome!