Crate doku

source ·
Expand description

fn(Code) -> Docs

Overview

Doku is a framework for documenting Rust data structures - it allows to generate aesthetic, human-friendly descriptions of configuration types, requests / responses, and so on.

Say goodbye to stale, hand-written documentation - with Doku, code is the documentation!

Example

Say, you’re writing an application that requires some TOML configuration to work:

use serde::Deserialize;

#[derive(Deserialize)]
struct Config {
    db_engine: DbEngine,
    db_host: String,
    db_port: usize,
}

#[derive(Deserialize)]
enum DbEngine {
    #[serde(rename = "pgsql")]
    PostgreSQL,

    #[serde(rename = "mysql")]
    MySQL,
}

Usually you’ll want to create a config.example.toml, describing the configuration’s format for your users to copy-paste and adjust:

db_engine = "pgsql" # or mysql
db_host = "localhost"
db_port = 5432

But writing such config.example.toml by hand is both tedious to maintain and error-prone, since there’s no guarantee that e.g. someone won’t rename a field, forgetting to update the documentation.

Now, with Doku, all you need to do is add a few #[derive(Document)]:

use doku::Document;

#[derive(Deserialize, Document)]
struct Config {
    /* ... */
}

#[derive(Deserialize, Document)]
enum DbEngine {
    /* ... */
}

… and call doku::to_json() / doku::to_toml(), which will generate the docs for you!

println!("{}", doku::to_toml::<Config>());

/*
  db_engine = "pgsql" | "mysql"
  db_host = "string"
  db_port = 123
*/

This automatically-generated documentation can be then fine-tuned e.g. by providing examples:

#[derive(Deserialize, Document)]
struct Config {
    /// Database's engine
    db_engine: DbEngine,

    /// Database's host
    #[doku(example = "localhost")]
    db_host: String,

    /// Database's port
    #[doku(example = "5432")]
    db_port: usize,
}

println!("{}", doku::to_toml::<Config>());

/*
  # Database's engine
  db_engine = "pgsql" | "mysql"

  # Database's host
  db_host = "localhost"

  # Database's port
  db_port = 5432
*/

And voilà, ready to deploy!

What’s more – because doku::to_json() returns a good-old String, it’s possible to create a test to make sure your docs always stay up-to-date:

use std::fs;

#[test]
fn docs() {
    let expected_docs = doku::to_toml::<Config>();
    let actual_docs = fs::read_to_string("config.example.toml").unwrap();

    if actual_docs != expected_docs {
        fs::write("config.example.toml.new", actual_docs);
        panic!("`config.example.toml` is stale");
    }
}

Let go & let the pipelines worry about your docs!

Plug and Play

Doku has been made with plug-and-play approach in mind - it understands most of the Serde’s annotations and comes with a predefined, curated formatting settings, so that just adding #[derive(Document)] should get you started quickly & painlessly.

At the same time, Doku is extensible - if the formatting settings don’t match your taste, you can tune them; if the derive macro doesn’t work because you’ve got a custom impl Serialize, you can write impl Document by hand as well.

So - come join the doc side!

Limitations

Formats

At the moment Doku provides functions for rendering JSON-like and TOML-like documents.

All models used by Doku are public though, so if you wanted, you could very easily roll your own pretty-printer, for you own custom format:

fn to_my_own_format<T>() -> String
where
    T: doku::Document
{
   match T::ty().kind {
       doku::TypeKind::String => "got a string!".to_string(),
       doku::TypeKind::Struct { .. } => "got a struct!".to_string(),
       _ => todo!(),
   }
}

println!("{}", to_my_own_format::<String>());

Annotations

Doku understands most of Serde’s annotations, so e.g. the following will work as expected:

#[derive(Serialize, Document)]
struct Something {
    #[serde(rename = "foo")]
    bar: String,
}

If you’re not using Serde, but you’d like to pass Serde-like attributes for Doku to understand, there’s also:

#[derive(Document)]
struct Something {
    #[doku(rename = "foo")] // (note the attribute name here)
    bar: String,
}

Language features

Doku supports most of Rust language’s & standard library’s features (such as strings, vectors, maps or generic types); the only exceptions are recursive types (which will cause the pretty-printers to panic, since they don’t support those).

Some external crates (such as chrono or url) are supported behind feature-flags.

How does it work?

When you wrap a type with #[derive(Document)]:

#[derive(Document)]
struct User {
    /// Who? Who?
    #[doku(example = "alan.turing")]
    login: String,
}

… the macro will generate an impl doku::Document:

impl doku::Document for User {
    fn ty() -> doku::Type {
        let login = doku::Field {
            ty: doku::Type {
                comment: Some("Who? Who?"),
                example: Some(doku::Example::Simple("alan.turing")),
                ..String::ty()
            },
            flattened: false,
            aliases: &[],
        };

        doku::Type::from(doku::TypeKind::Struct {
            fields: doku::Fields::Named {
                fields: vec![
                    ("login", login)
                ],
            },
            transparent: false,
        })
    }
}

… that will be invoked later, when you call doku::to_*():

fn to_json<T>() -> String
where
    T: doku::Document
{
    match T::ty().kind {
        doku::TypeKind::String => print_string(/* ... */),
        doku::TypeKind::Struct { .. } => print_struct(/* ... */),
        /* ... */
    }
}

There’s no magic, no RTTI hacks, no unsafety - it’s all just-Rust.

Modules

Structs

Enums

Traits

  • A type that can be pretty-printed by Doku.

Functions

  • Generates a JSON documentation for specified type.
  • Generates a JSON documentation for specified type using custom formatting settings.
  • Generates a JSON documentation for specified type using custom formatting settings, and extracting example values from given serializable object.
  • Generates a JSON documentation for specified type, extracting example values from given serializable object.
  • Generates a TOML documentation for specified type.
  • Generates a TOML documentation for specified type using custom formatting settings.
  • Generates a TOML documentation for specified type using custom formatting settings, and extracting example values from given serializable object.
  • Generates a TOML documentation for specified type, extracting example values from given serializable object.

Derive Macros