Skip to content

Instantly share code, notes, and snippets.

@mappum
Created October 6, 2014 21:54
Show Gist options
  • Save mappum/32cb25c9016ccd0219c8 to your computer and use it in GitHub Desktop.
Save mappum/32cb25c9016ccd0219c8 to your computer and use it in GitHub Desktop.

IPFS command interface design notes

A package to provide a succinct way to implement transport-agnostic functionality

(e.g. accessible through CLI, HTTP RPC, raw TCP, websocket, etc.)

Features/goals:

  • Subcommands

    • Commands should register their own subcommands

      Something along the lines of:

      // cmd/bitswap.go
      bitswapCmd := &commands.Command{...} // a command that does bitswap stuff
      strategyCmd := &commands.Command{...} // a command that controls the bitswap strategy
      bitswapCmd.Register("strategy", strategyCmd) // add `strategy` as a subcommand of the bitswap cmd
      
      // cmd/ipfs.go
      ipfsCmd := &commands.Command{...} // root command
      ipfsCmd.Register("bitswap", bitswapCmd) // register the bitswap command as a subcommand
    • Names resolved on any transport naming scheme

      Example:

      • CLI: ipfs bitswap strategy set
      • HTTP: POST /bitswap/strategy
  • Same options/args across transports

    • Example:
      • CLI: ipfs beep [-f=foo] <boop> <bop>
      • HTTP: GET /beep?f=foo&args=boop,bop
    • Register options by both short and long names (ipfs add -c ./config equivalent to ipfs add --config=./config)
  • Allow transport-specific output (e.g. nicely formatted CLI text)

  • Zero or minimal boilerplate for RPC-call commands

    • Most commands from command line simply send an RPC request, would be nice for these to be registered succinctly and/or automatically
  • Make it easy to provide command documentation

    • Agnostic to type of documentation, e.g. CLI help text or HTML docs
    • Prefer explicitly created output for optimal UX, e.g. hand-formatted strings
    • Could support markdown, then we could have 1 copy of text for everywhere. Sidenote: We could make a cool markdown -> ANSI terminal output renderer for really nice looking CLI output.
  • Preferably let devs implement new commands in a single file

    • Provides ease of maintenance, codebase simplicity
  • Make command output encoding-agnostic

    • Encoding chosen by user (JSON, XML, protobuf, CSV, etc.)
    • Specified in options, something like GET /peers?enc=json or ipfs ls foo -e=xml
    • Easy implementation: commands return a interface{} for easy marshalling via golang stdlib encoders. Commands can each create a struct to specify output format.

API

Option

Options are values specified by the consumer (as command line flags, HTTP querystring params, etc.). We can do things like programmatically checking to make sure an option is supported, so we can warn the user if they used an unrecognized flag. By giving options a datatype, we can make sure transports unmarshal values correctly and error if the user provided the wrong type.

// Option is used to specify a field that will be provided by a consumer
type Option struct {
  Long string         // long name (e.g. "config")
  Short string        // short name (e.g. "c")
  Type reflect.Kind   // value must be this type
                      // (we could use our own consts instead of the ones from `reflect`)
  Default interface{} // the default value
}

Request

A request is a call to a command (e.g. a HTTP request, or a command line call). Commands can use them to read the options or arguments that were specified wihtout worrying about how they unmarshalled (the transport does that part).

// Request represents a call to a command from a consumer
type Request struct {
  opts map[string]interface{}
  args []interface{}
  transport *Transport
}

Command

Transport

Usage Example

cmd := commands.Command{
  help: `
    # ipfs
    ### global versioned p2p merkledag file system

    ### Basic commands:
    * `add <path>    Add an object to ipfs.`
    * `cat <ref>     Show ipfs object data.`
    * `ls <ref>      List links from an object.`
    * `refs <ref>    List link hashes from an object.`

    ### Tool commands:
    * `config        Manage configuration.`
    * `version       Show ipfs version information.`
    ...
  `,

  options: [
    commands.Option{ "config", "c", commands.String, "~/.go-ipf/config" },
  ],

  f: func(req Request) interface{}, error {
    ...
  },
}
@whyrusleeping
Copy link

Forking is hard... But I want to make sure that flags registered to a command can be accessed by the subcommands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment