Skip to main content

rbnx/cmd/
docs.rs

1// SPDX-License-Identifier: MulanPSL-2.0
2// `rbnx docs` — regenerate the mdBook contract + ROS IDL reference
3// (`docs/src/reference/{contracts,idl}.md`) from `capabilities/`.
4//
5// This is a developer-side step run inside the robonix source tree: it
6// reads the live `capabilities/` (contracts + lib IDL) through the same
7// loader codegen uses, and writes plain-markdown pages into the docs
8// (robonix-book) submodule. Those pages are committed there, so the
9// mdBook / GitHub Pages build compiles them with NO robonix environment —
10// no rbnx, no Rust, no `capabilities/`. Re-run after changing a contract
11// or IDL so the browsable reference stays in sync.
12
13use anyhow::{Context, Result};
14use colored::*;
15use robonix_cli::{Config, SourcePathKey};
16use std::path::{Path, PathBuf};
17use std::process::Command;
18
19use super::codegen::{build_codegen_cmd, locate_codegen_bin, run_cmd};
20
21pub async fn execute(config: Config, out_dir: Option<PathBuf>) -> Result<()> {
22    let root = config.resolve_source_path(SourcePathKey::Root)?;
23    let rust_root = config.resolve_source_path(SourcePathKey::RustRoot)?;
24    let capabilities_dir = config.resolve_source_path(SourcePathKey::Capabilities)?;
25    let interfaces_lib = config.resolve_source_path(SourcePathKey::InterfacesLib)?;
26
27    // Default into the docs submodule's reference dir. The generated files
28    // are committed there; mdBook / Pages need only the markdown.
29    let out = match out_dir {
30        Some(d) if d.is_absolute() => d,
31        Some(d) => root.join(d),
32        None => root.join("docs").join("src").join("reference"),
33    };
34    std::fs::create_dir_all(&out)
35        .with_context(|| format!("create reference dir {}", out.display()))?;
36
37    let stamp = version_stamp(&root);
38
39    let direct = locate_codegen_bin(&rust_root);
40    let cargo = if direct.is_none() {
41        Some(std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()))
42    } else {
43        None
44    };
45
46    println!(
47        "{} robonix-codegen --lang docs → {}",
48        "[docs]".bold(),
49        out.display()
50    );
51    println!("{} version stamp: {}", "[docs]".bold(), stamp);
52    let mut cmd = build_codegen_cmd(direct.as_ref(), cargo.as_deref(), &rust_root);
53    cmd.args(["--lang", "docs", "-I"])
54        .arg(&interfaces_lib)
55        .arg("--contracts")
56        .arg(&capabilities_dir)
57        .arg("-o")
58        .arg(&out)
59        .arg("--doc-stamp")
60        .arg(&stamp);
61    run_cmd("robonix-codegen docs", &mut cmd)?;
62
63    println!(
64        "{} wrote contracts.md + idl.md. Commit them into the docs repo — \
65         the mdBook / Pages build needs no robonix environment.",
66        "[docs]".green().bold()
67    );
68    Ok(())
69}
70
71/// `v<rbnx-version> · robonix commit <short-sha>[-dirty] · <commit-date>`,
72/// read from git in `root`. The commit is what the reference was generated
73/// from — always stated explicitly. Degrades gracefully when git or repo
74/// metadata is unavailable so the reference still carries *some* version.
75fn version_stamp(root: &Path) -> String {
76    let git = |args: &[&str]| -> Option<String> {
77        let out = Command::new("git")
78            .arg("-C")
79            .arg(root)
80            .args(args)
81            .output()
82            .ok()?;
83        if !out.status.success() {
84            return None;
85        }
86        let s = String::from_utf8(out.stdout).ok()?.trim().to_string();
87        (!s.is_empty()).then_some(s)
88    };
89    let sha = git(&["rev-parse", "--short", "HEAD"]).unwrap_or_else(|| "unknown".to_string());
90    let dirty = git(&["status", "--porcelain"]).is_some_and(|s| !s.is_empty());
91    let date = git(&["log", "-1", "--format=%cd", "--date=short"]).unwrap_or_default();
92    let ver = env!("CARGO_PKG_VERSION");
93    let dirty_tag = if dirty { "-dirty" } else { "" };
94    let date_part = if date.is_empty() {
95        String::new()
96    } else {
97        format!(" · {date}")
98    };
99    format!("v{ver} · commit {sha}{dirty_tag}{date_part}")
100}