Giter Site home page Giter Site logo

vic / clap-nix Goto Github PK

View Code? Open in Web Editor NEW
3.0 4.0 0.0 46 KB

Command line argument parser in pure Nix. Supports sub-commands, typed positional arguments, value coercion and resolution via Nix Modules.

License: Apache License 2.0

Nix 100.00%
nix clap command-line-arguments-parser cli cli-parser getopt

clap-nix's Introduction

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.

Test

Features

  • The implementation and tests are pure Nix.

  • Familiar --long and -short option styles.

  • Boolean long options can be negated with --no- surprises.

  • Short options can be collapsed. -abc equals -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 help be executed when foo receives no more arguments)

  • Options are specified by virtue of Nix lib.mkOption and lib.types. Meaning your options can provide defaults, value coercions, aggregation or a composition of different types.

  • Leverages the power of lib.evalModules so you can define option aliases (eg, -h and --help having the same value) or define your own config by providing custom Nix modules and use lib.mkIf and 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 ? {}, ...}

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.

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.

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

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

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.

Some other examples can be found in the test directory.

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?

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

Yes, please. Pull-requests are more than welcome!

clap-nix's People

Contributors

vic avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

clap-nix's Issues

Allow usage from outside of nix as a command line tool.

Since clap-nix is implemented in Nix, it will always need a Nix interpreter to work. However there would be some benefit on creating an external interface (a nix-clap command perhaps) that you could use from the shell and get the resolved options output either in another nix file or json or anything that nix can write to.

command enabled flag

Change the enabled flag to be true only if anything inside the command was explicitly set by the user. Or perhaps this should be another flag ?

Automatic help generator.

Since nix options are self-documenting, it would be nice to be able to output help in various formats: plain-text (for showing as part of --help), as a file for man(1) for longer documentation, as json for processing from other tools.

At least plain text should be implemented.

Allow setting parent options from subcommands.

Currently if you select a command options from their parent commands are no longer recognized. This should not be case since, for example the following should be possible:

mycmd foo --enable-x bar --enable-y <=> mycmd foo bar --enable-x --enable-y

and clap-nix should automatically know that enable-x belongs to the foo command (only if there's not an enable-x in bar itself.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.