Introduction

The type_description crate is the basic block to allow for discovery of configuration options in your Rust projects.

It allows for self-describing types in your project, and an easy way to export them. This description can then be manipulated like any other data, allow for processing into for example Markdown or a questionnaire.

An example would be the following:

//
//   This Source Code Form is subject to the terms of the Mozilla Public
//   License, v. 2.0. If a copy of the MPL was not distributed with this
//   file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
extern crate type_description;
extern crate serde_json;

use type_description::{AsTypeDescription, TypeDescription};

fn main() {


#[derive(TypeDescription)]
struct MyConfiguration {
    /// The name of this configuration
    name: String,

    /// List of actions this config can take
    action: Vec<String>,
}

assert_eq!(serde_json::to_string_pretty(&MyConfiguration::as_type_description()).unwrap(), r#"
{
  "name": "MyConfiguration",
  "kind": {
    "Struct": [
      {
        "name": "name",
        "doc": "The name of this configuration",
        "kind": {
          "name": "String",
          "kind": "String",
          "doc": "An UTF-8 string"
        }
      },
      {
        "name": "action",
        "doc": "List of actions this config can take",
        "kind": {
          "name": "Array of 'String's",
          "kind": {
            "Array": {
              "name": "String",
              "kind": "String",
              "doc": "An UTF-8 string"
            }
          },
          "doc": null
        }
      }
    ]
  },
  "doc": null
}
"#.trim())

}

This would then output a datastructure (if rendered as JSON) like this:

//
//   This Source Code Form is subject to the terms of the Mozilla Public
//   License, v. 2.0. If a copy of the MPL was not distributed with this
//   file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
extern crate type_description;
extern crate serde_json;

use type_description::{AsTypeDescription, TypeDescription};

fn main() {


#[derive(TypeDescription)]
struct MyConfiguration {
    /// The name of this configuration
    name: String,

    /// List of actions this config can take
    action: Vec<String>,
}

assert_eq!(serde_json::to_string_pretty(&MyConfiguration::as_type_description()).unwrap(), r#"
{
  "name": "MyConfiguration",
  "kind": {
    "Struct": [
      {
        "name": "name",
        "doc": "The name of this configuration",
        "kind": {
          "name": "String",
          "kind": "String",
          "doc": "An UTF-8 string"
        }
      },
      {
        "name": "action",
        "doc": "List of actions this config can take",
        "kind": {
          "name": "Array of 'String's",
          "kind": {
            "Array": {
              "name": "String",
              "kind": "String",
              "doc": "An UTF-8 string"
            }
          },
          "doc": null
        }
      }
    ]
  },
  "doc": null
}
"#.trim())

}

As you can see, not only is the struct itself described, but also all of its constituents as well as the documentation you have provided.

If you want to, you could use the binary provided by the type_description crate to transform it into markdown!

This would output something akin to:

MyConfiguration

Fields:

  • name (String): The name of this configuration
  • action (Array of 'String's): List of actions this config can take

String

An UTF-8 string

Array of 'String's

Array Elements of String

If this sounds intruiging, read on and find out how it can be used!

Generating Markdown

Depending on your case you can interact with TypeDescriptions in multiple ways.

If you are given a JSON formatted type description file, then you can use the CLI for further processing. (This may be the case if you are using someone else's project).

If you have some Rust types and want to generate markdown directly from their TypeDescription then you might be best served with the library.

Using the CLI

The preferred way of interacting with the CLI is to use the type_description binary.

It can be invoked in the following ways:

  • With the nix package manager (flakes enabled): nix run github:TheNeikos/type_description
  • With cargo: cargo install type_description
  • Directly from source: git clone https://github.com/TheNeikos/type_description && cd type_description && cargo run -F bin

Either way you have installed it. You can now generate markdown from it by sending it to standard input.

E.g.:

  • Using nix: cat <your description file> | nix run github:TheNeikos/type_description
  • After installing it: curl example.com/some_type_description | type_description
  • Building from source: curl example.com/some_type_description | cargo run -F bin

Using the library

Rendering a TypeDescription to markdown can be done with the type_description::render::render_to_markdown method.

extern crate type_description;
fn main() {
use type_description::AsTypeDescription;

let ty_desc = std::collections::HashMap::<String, f64>::as_type_description();

let markdown: String = type_description::render::render_to_markdown(&ty_desc).expect("Could not render to markdown");

println!("{}", markdown);
}

You can then use a markdown to HTML processor to generate HTML, or maybe print it to the terminal with the termimad crate.