Coder Social home page Coder Social logo

clap-rs / clap Goto Github PK

View Code? Open in Web Editor NEW
13.3K 64.0 993.0 18.46 MB

A full featured, fast Command Line Argument Parser for Rust

Home Page: docs.rs/clap

License: Apache License 2.0

Rust 89.91% Makefile 0.06% JavaScript 0.59% Roff 0.30% Shell 6.66% PowerShell 1.44% Nushell 0.60% Elvish 0.44%
argument-parsing subcommands rust positional-arguments parsed-arguments command-line command-line-parser argument-parser

clap's Introduction

clap

Command Line Argument Parser for Rust

Crates.io Crates.io License License Build Status Coverage Status Contributors

Dual-licensed under Apache 2.0 or MIT.

About

Create your command-line parser, with all of the bells and whistles, declaratively or procedurally.

For more details, see:

Sponsors

Gold

Silver

Bronze

Backer

clap's People

Contributors

blyxxyz avatar bors[bot] avatar byron avatar creepyskeleton avatar cstyles avatar dependabot[bot] avatar dylan-dpc avatar epage avatar fishface60 avatar gingerdevilish avatar homu avatar kbknapp avatar ldm0 avatar little-dude avatar mgeisler avatar mkantor avatar modprog avatar nabijaczleweli avatar nibon7 avatar nickhackman avatar pksunkara avatar rami3l avatar renovate[bot] avatar sru avatar texitoi avatar tormol avatar tshepang avatar vinatorul avatar williamyaoh avatar willmurphyscode avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clap's Issues

Unable to use `-` as positional argument

Working on an app that should be able to operate on either STDIN or a file, I was trying to use - as the file name to indicate STDIN (as is the convention used in the app I'm porting, and elsewhere), but that causes an index out of bounds panic.

% ./target/debug/signing-participants -
thread '<main>' panicked at 'index out of bounds: the len is 0 but the index is 0', /Users/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-mac/build/src/libcore/str/mod.rs:1685

So, I thought I'd try the common -- to indicate "only positional arguments follow", but that fails as well but much more gracefully.

% ./target/debug/signing-participants -- -
Argument -- isn't valid
USAGE:
    signing-participants [FLAGS] [OPTIONS] <INPUT>
For more information try --help

suggestion: less aggressive coloring

In v0.8.0, text coloring was introduced which I generally am a great fan of. Having it in usage strings seems like something I definitely want to have.

The first implementation of it applies it to the entire output of the usage string, which doesn't help one to focus on what's actually the problem.
Therefore I would recommend to use color (and/or bold text) only to highlight portions of it, like the required parameters that were missing. This could also be used to highlight suggestions in a friendly color.
Of course, a disclaimer shall be placed here that this is a somewhat cosmetical issue which is to some extend subjective. This issue is nothing more than a suggestion.

As a side-note, thanks for implementing everything with dependencies in a feature-gated fashion to allow an opt-out, which is what I chose as an intermediate solution in my case. Generally, that's a great practice that I will adopt were adequate as well.

Feature request: Add support for multiple required arguments, e.g. [-u <mode> <file> <mime>]

I need to support an optional argument like the one noted in the subject, but was unable to achieve this. Apparently there is no support for multiple arguments of a single flag. docopt can do this though, and I believe its a useful feature to have.

A workaround I tried was to use the Arg::requires() method to enforce flags to appear together. However, a doubly-linked requires definition doesn't seem to work. For example, in the following excerpt I want -u to appear with -f and vice-versa.

โžœ  google-apis-rs git:(clap) โœ— make groupsmigration1-cli-cargo ARGS="run -- archive insert groupid -h"
cd gen/groupsmigration1-cli && cargo run -- archive insert groupid -h
     Running `target/debug/groupsmigration1 archive insert groupid -h`
groupsmigration1-archive-insert
Inserts a new mail into the archive of the Google group.

USAGE:
    groupsmigration1 archive insert [FLAGS] [OPTIONS] <group-id>

FLAGS:
    -h, --help       Prints help information
    -v, --version    Prints version information

OPTIONS:
    -f <file>       The file to upload
    -u <mode>       Specify which file to upload [values: resumable simple]
    -o <out>        Specify the file into which to write the programs output
    -p <v>...       Set various fields of the request structure

POSITIONAL ARGUMENTS:
    group-id    The group ID

In code, this looks like this (example is manufactured, and not actual code, might not compile)

let mut uarg = Arg::with_name("u").required(false).takes_value(true);
uarg = uarg.requires("file");
farg = Arg::with_name("file")
           .short("f")
           .required(false)
           .requires("u")
           .takes_value(true));

Maybe I am doing it wrong though. The code above results in runtime errors like this:

โžœ  google-apis-rs git:(clap) โœ— make groupsmigration1-cli-cargo ARGS="run -- archive insert groupid -u simple -f file"
cd gen/groupsmigration1-cli && cargo run -- archive insert groupid -u simple -f file
     Running `target/debug/groupsmigration1 archive insert groupid -u simple -f file`
One or more required arguments were not supplied
USAGE:
    groupsmigration1 archive insert [FLAGS] [OPTIONS] -f <file>  <group-id>
For more information try --help

In any case, it would be very helpful to print which dependencies are actually missing :).
Thanks for your help !

Implement Suggestions

This will be implemented via feature as it will incur an additional dep.

This is for spelling mistakes, for example if we had an application with --file <file> option and we ran

$ myprog --flie words.txt
--flie isn't a valid argument for myprog
    Did you mean --file?

USAGE:
    myprog [FLAGS] [OPTIONS]
For more information try --help

Initial implementation will be longs and subcommnds only, but should be able to add values from .possible_values() later.

Implement multiple subcommands at once

Currently only one sub-command may be used per match. For example:
Assume myapp has two subcommands one and two, and each of those has subone and subtwo respectively.

myapp
    |-one
    |   |-subone
    |-two
        |-subtwo

Meaning currently you can only run the following permutations

$ myapp one
$ myapp one subone
$ myapp two
$ myapp two subtwo

This issue proposes using a .. syntax to mean "Back up one level" meaning you could then add these additional permutations to valid uses

$ myapp one ..two
$ myapp two ..one
$ myapp two subtwo ..one
$ myapp two subtwo ..one subone
$ myapp two ..one subone
$ myapp one ..two subtwo
$ myapp one subone ..two
$ myapp one subone ..two subtwo

This could also lead to .... meaning up three levels or any multiple of .. adding an additional level.

This will be labeled maybe because I'm unsure of why would need something like this other than combining multiple invocations of a program into a single call... I'll think on this one for a bit and see how it turns out.

Add named valued to `from_usage` methods

To allow something like

Arg::from_usage("[myopt] -o --opt <mode> <file> 'some help text'")

This would parse as
Name: myopt
Short: o
Long: opt
Values: <mode> <file>
Multiple: false
Required: false
Number of Values: 2

Some items to be determined are how to parse when an explicit name does not precede the values (Required true, or false, use the first value for determining? Use Long as name? etc.)

Static lifetimes on Arg strings

So, I went to do a bit more dynamic generation (this time with the help text of one of my app's arguments), and ran into the same static lifetime issue as #28.

I can provide a more concrete example if you'd like, but I wanted to file this before I forgot.

greedy argument parsing introduced by obviously evil ;) commit: 6669f0a

6669f0a introduces shorthand parsing for multiple values. However, this causes subcommands to be parsed as flag-values, thus making it impossible to properly call the program.

For example, calls like this ...

groupsmigration1 --scope foobar archive insert group -u simple README.md

... now fail as -u is not a valid argument. This is as --scope <url>... has eaten all arguments until the first -flag, then -u is interpreted as top-level flag which it is not. Usually archive and insert are subcommands, and group as well as -u are required flags of insert.

I have made a quick-fix (notes) which might not be ready for a PR, and I am unsure whether I am fit to fix this properly. Clearly we would want to add a few tests to assure this truly works and to protect from regression.

Possibly incorrect generated usage grammar

When specifying an argument like this:

Arg::with_name("url")
    .long("scope")
    .help("Specify the authentication a method should be executed in. Each scope requires the user to grant this application permission to use it.If unset, it defaults to the shortest scope url for a particular method.")
    .multiple(true)
    .takes_value(true))

The auto-generated grammar shows up like this --scope <url>...:

groupsmigration1 0.2.0
Sebastian Thiel <[email protected]>
Groups Migration Api.

USAGE:
    groupsmigration1 [FLAGS] [OPTIONS] [SUBCOMMANDS]

FLAGS:
        --debug         Output all server communication to standard error. `tx` and `rx` are placed into the same stream.
        --debug-auth    Output all communication related to authentication to standard error. `tx` and `rx` are placed into the same stream.
    -h, --help          Prints help information
    -v, --version       Prints version information

OPTIONS:
        --config-dir <folder>    A directory into which we will store our persistent data. Defaults to a user-writable directory that we will create during the first invocation.[default: ~/.google-service-cli
        --scopes <url>...        Specify the authentication a method should be executed in. Each scope requires the user to grant this application permission to use it.If unset, it defaults to the shortest scope url for a particular method.

SUBCOMMANDS:
    archive
    help       Prints this message

All documentation details can be found at http://byron.github.io/google-apis-rs/google_groupsmigration1_cli

This grammar was used similarly by docopt, but meant you could call it like program --scope url1 url2. However, with clap it only works if the flag is repeated: program --scope url1 --scope url2 which should be resulting in a different grammar, more along the lines of [--scope <url>]....

I believe this inconsistency should be reviewed, either docopt has an issue there, or clap :).

Side note

In any case, it would be great if the expected number of arguments could be + (one or more) as I also have the case where I specify -r <value>..., which previously allowed me to make calls like -r v1 v2 v3 v4, which is very convenient. With clap, I will now have to write -r v1 -r v2 -r v3 -r v4, resulting in much more noise.
This one might be related to #88, which requests a fixed amount of arguments. When thinking about python's argparse module, I remember that they have the notion of num_args, which is either +, * or an explicit number. Implementing it this way would probably be a breaking change as takes_value(true|false) would be more like takes_value(Values::ZeroOrMore|Values::OneOrMore|Values::Exactly(5)|Values::None).

Positional args with multiple values

I'm working on a command that allows a user to specify an arbitrary number of files/directories on the command line (Ex: dupe-finder /path/to/dir1 /path/to/dir2 /path/to/dir3). I know I could do this currently, if I changed the form to use an option that allowed multiple values (dupe-finder -d /path/to/dir1 -d /path/to/dir2 -d /path/to/dir3), but it would be handy to be able to have a positional argument that captured any positional arguments at & after its index into a vector.

Display all required arguments in usage

Currently, if an argument is "required by" a "required by default" argument it will not be shown in the usage string.

i.e. If I set up two positional arguments "input" and "output" mark only input as required(true), but specify requires("output") on the input argument, "output" will not be listed in the usage string which can be confusing to users:

$ myprog infile.txt
One or more required arguments weren't specified
USAGE:
    myprog [FLAGS] <INPUT>
[...]

It SHOULD display:

USAGE:
    myprog [FLAGS] <INPUT> <OUTPUT>

clap-rs 0.7.1: dynamic usage strings seem 'inverted'

With a program exhibiting the following grammar ...

$ groupsmigration1 archive insert --help`
groupsmigration1-archive-insert
Inserts a new mail into the archive of the Google group.

USAGE:
    groupsmigration1 archive insert <group-id>  [FLAGS] [OPTIONS] [ -u <mode> <file> ]

FLAGS:
    -h, --help       Prints help information
    -v, --version    Prints version information

OPTIONS:
    -m <mime>          The file's mime time, like 'application/octet-stream'
    -u <mode> <file>        Specify the upload protocol (simple|resumable) and the file to upload
    -o <out>           Specify the file into which to write the programs output
    -p <v>...          Set various fields of the request structure

POSITIONAL ARGUMENTS:
    group-id    The group ID

... I would expect a partial invocation like groupsmigration1 archive insert to yield a usage more along the lines of ...

One or more required arguments were not supplied: <group-id>, '-u <mode> <file>'
USAGE:
    groupsmigration1 archive insert <group-id> -u <mode> <file>
For more information try --help

... but what you get is just as follows:

One or more required arguments were not supplied
USAGE:
    groupsmigration1 archive insert
For more information try --help

Another example for missing required parameters is groupsmigration1 archive insert -u simple README.md where <group-id> is missing. However, this is not actually said in the resulting usage text:

$ groupsmigration1 archive insert -u simple README.md
One or more required arguments were not supplied
USAGE:
    groupsmigration1 archive insert    [ -u <mode> <file> ]
For more information try --help

What I would expect here is something like this:

One or more required arguments were not supplied: <group-id>
USAGE:
    groupsmigration1 archive insert <group-id> -u simple README.md
For more information try --help

In a call like groupsmigration1 archive insert group one will see a usage string like this:

One or more required arguments were not supplied
USAGE:
    groupsmigration1 archive insert  <group-id>
For more information try --help

and I think think it would be much better to get something like that:

One or more required arguments were not supplied: -u <mode> <file>
USAGE:
    groupsmigration1 archive insert group -u <mode> <file>
For more information try --help

Once the usage is improved, clap will finally be top-of-the-line when it comes to usability I think, and I really want it to be there :).
Thanks for taking care.

Implement smart usage

Usage strings, if possible should be re-generated upon error for the current usage case. For example if you have a non-required by default argument which requires another argument, and the user fails to supply the third argument (which is required by proxy), the usage string is generic - yet the error message says a required argument is missing...which argument?

let m = App::new("app")
                .arg(Arg::from_usage("--reqs").requires("input"))
                .arg_from_usage("[input] 'some input'")
                .get_matches();

Run with app --reqs and the usage does not identify [input] as a requirement, yet states a generic "required argument missing"

Now this does happen in certain instances, but I want to implement it in all instances.

Add support for building arguments from yaml

Adds a feature and an additional dep, which is not included by default.

To be released this should support the following methods of App, Arg, and ArgGroup

App

  • App::new (i.e. setting the name)
  • App::version
  • App::author
  • App::bin_name
  • App::about
  • App::after_help
  • App::usage
  • App::help
  • App::help_short
  • App::version_short
  • App::setting(s)
  • App::arg(s)
  • App::arg_group(s)
  • App::subcommand(s)

The following will not be implemented, yet.

  • App::arg(s)_from_usage
  • All methods deprecated by App::setting(s)

Arg

  • Arg::with_name
  • Arg::short
  • Arg::long
  • Arg::help
  • Arg::required
  • Arg::takes_value
  • Arg::index
  • Arg::multiple
  • Arg::global
  • Arg::empty_values
  • Arg::group
  • Arg::number_of_values
  • Arg::max_values
  • Arg::min_values
  • Arg::value_name
  • Arg::value_names
  • Arg::conflicts_with
  • Arg::requires
  • Arg::mutually_overrides_with
  • Arg::possible_values

The following will not be implemented, yet.

  • Arg::validator
  • Arg::*_all (These because the functionality is implemented in the single method)
  • Arg::from_usage

ArgGroup

  • ArgGroup::with_name (i.e. setting the name)
  • ArgGroup::add
  • ArgGroup::required
  • ArgGroup::requires
  • ArgGroup::conflicts_with

The following will not be implemented, yet.

  • ArgGroup::*_all (These because the functionality is implemented in the single method)

Requiring version string have static lifetime makes for awkward code

In trying to get the version string to automatically populate based on the version I specify in my Cargo.toml using something like:

...
    let version = format!("{}.{}.{}{}",
                          env!("CARGO_PKG_VERSION_MAJOR"),
                          env!("CARGO_PKG_VERSION_MINOR"),
                          env!("CARGO_PKG_VERSION_PATCH"),
                          option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));

    let matches = App::new("signing-participants")
        .version(&version[..])
...

However, this fails with:

% cargo build
   Compiling signing-participants v0.0.1 (file:///Users/jacobhelwig/btsync/projects/signing-participants)
src/main.rs:27:19: 27:26 error: `version` does not live long enough
src/main.rs:27         .version(&version[..])
                                 ^~~~~~~
note: reference must be valid for the static lifetime...
src/main.rs:24:78: 93:2 note: ...but borrowed value is only valid for the block suffix following statement 1 at 24:77
src/main.rs:24                           option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
src/main.rs:25
src/main.rs:26     let matches = App::new("signing-participants")
src/main.rs:27         .version(&version[..])
src/main.rs:28         .author("Jacob Helwig <[email protected]")
src/main.rs:29         .about("Generate a list for key-signing parties")
               ...
error: aborting due to previous error
Could not compile `signing-participants`.

To learn more, run the command again with --verbose.

I can get it to work if I don't care about the prerelease marker, and switch to concat! (haven't been able to get concat! to play nicely with option_env!):

    let version = concat!(env!("CARGO_PKG_VERSION_MAJOR"), ".",
                          env!("CARGO_PKG_VERSION_MINOR"), ".",
                          env!("CARGO_PKG_VERSION_PATCH"));

    let matches = App::new("signing-participants")
        .version(&version)
% cargo run -- --version
   Compiling signing-participants v0.0.1 (file:///Users/jacobhelwig/btsync/projects/signing-participants)
     Running `target/debug/signing-participants --version`
signing-participants 0.0.1

Sticking with format! so I can use option_env! is really not nice (as it involves unsafe code provided by dougxxx in the #rust IRC channel):

#![feature(collections)]
use std::mem::forget;
use std::mem::transmute;

unsafe fn as_static(v:&str) -> &'static str {
  let tmp = String::from_str(v);
  let boxed = Box::new(tmp);
  let rtn: *const str = &**boxed as *const str; 
  forget(boxed);
  return transmute(rtn);
}

fn awkward_function(v:&'static str) {
  println!("{}", v);
}

fn main() {
  let foo = String::from_str("Hello World");
  unsafe { awkward_function(as_static(&*foo)); }
}

Is there a way the elements of the App struct could be changed to not require static lifetimes on its elements? I haven't looked at what the implications of this would be with the rest of the code, but allowing the format! trick I initially tried for defining the version seems like reasonable usage of the library.

Add Arg::from_usage(&str) as a shorthand builder

Currently creating simple arguments (those without relational rules) is just as verbose as more complex configurations. Adding something like a Arg::from_usage() would greatly simplify building simple arguments.

Example:

Arg::from_usage("[name] -f --flag 'a flag used for something'");
// Or a required option that allows multiple occurrences
Arg::from_usage("--some-opt <name>... 'An option that takes multiple values and is required'");

Improve error message when argument is mutually excluded

Currently if an argument is mutually excluded by another argument and appears before the argument that declared the exclusion, the error message only states "a required argument wasn't supplied" but it should state that the specific argument is excluded by another (which it correctly does when the excluded argument appears after the one declaring the exclusion).

Limit values of Options or Positional Arguments

Add something like a .possible_values(Vec<&str>) to Arg which denotes all the possible values for an option or positional argument, and at runtime ensures one of those values was used, or fails with a usage string.

Correctly distinguish between multiple values, and multiple allowances

This is a slightly breaking change - not enough to warrant a label.

Currently, using named values -f <file> <mode> requires you to set multiple(true)...but this isn't correct logic. By this token, someone could legally do:

$ myprog -f some.txt fast -f other.txt slow

This may, or may not, be what the developer wanted. Setting multiple(true) shouldn't be a requirement for named values...only for unnamed multiple values. This allows the developer to decide which style of argument he wants, multiple allowances, or multiple values.

suggested API improvement: make `Arg::possible_values()` more flexible

Currently it enforces the usage of Vec<&str>, which makes it harder to use for people who prefer different means of passing this information. They could have a statically allocated array for instance, which contains enumerations, which in turn implement AsRef<str> to allow a light conversion.

I suggest to have a look at this method's signature which uses an iterator over items that convert into a &str, allowing all sorts of inputs. Internally, you can still easily create the Vec you require by using Iterator::collect()

Solidify API for 1.0

Current API is quite verbose, I'm considering using common short names to save some key strokes and cut back on the the verbosity. Things such as value -> val, etc. There are also several method names which have several synonyms that would fit. Once this is stabilized I will start considering a 1.0 release (along with a Rust 1.0).

This would ultimately be a breaking change (if names are changed), but legacy methods will be left in until a proper deprecation period has passed.

If anyone has ideas of overly verbose method names that should be changed, let's here it :)

Main methods I'm considering are those of ArgMatches but those of Arg and App could also change if needed. I'm also not apposed to leaving all method names as is, I believe being too verbose is better than too short.

Allow App names with hyphens ('-')

Currently application names with a hyphen '-' get replaced with a space ' ' when displaying usage strings. This is ideal for determining subcommands, but the original application name, or subcommand name should allow '-'s.

Add all preceding positionals to required list when one is required

Currently if you have several positional arguments, and one of the latter ones is required, the preceding ones are not added to the required list, which can make for a confusing error message (i.e. you're putting all the required arguments in according to the usage statement, but still getting "one or more required arguments not supplied")

Move argument assertions into app.rs

Argument assertions to determine if incompatible options have been used are currently evaluated inside arg.rs within the individual method. This allows for certain assertions to pass, that shouldn't, depending on the order in which argument options are listed.

Although assertions should normally be evaluated as soon as possible, all options must first be present. Thus assertions need to be moved to app.rs inside the arg() method.

Allow case insensitivity in arg_enum! macro

Currently if you use arg_enum! to create possible values into an enum, you must either allow non-camel-case enum variants (to suppress the warning), or force users to start their possible values with an upper case character. For example, if you had a --mode which accepted an arg_enum! of

arg_enum! {
    enum {
        Vi,
        Emacs
    }
}

The user must use $ myprog --mode Vi which isn't optimal. This enum should be able to match vi or Vi.

Simply converting to lowercase is also a very tricky subject if utf-8 is to be supported. But being, I don't use any non-ascii characters on a regular basis, I'm unfamiliar with capital/lowercase unicode graphemes.

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.