Skip to main content

netget/protocol/
docs.rs

1//! Human-readable documentation dump for every compiled-in protocol.
2//!
3//! Backs the `--docs` CLI flag. Renders, for each registered server and client
4//! protocol, what the model is told about it: base-stack identity, keywords,
5//! startup parameters, events and their actions, and setup examples. Everything
6//! is read live from the registries and the `Protocol` trait, so it never goes
7//! stale relative to the build.
8
9use crate::llm::actions::protocol_trait::Protocol;
10
11/// Render documentation for every server and client protocol in this build.
12pub fn render_all_protocol_docs() -> String {
13    let mut out = String::new();
14    out.push_str("# NetGet protocol documentation\n\n");
15    out.push_str(
16        "Every protocol compiled into this binary, as the model sees it. Names in \
17         parentheses are keywords you can use in a prompt.\n\n",
18    );
19
20    // Servers
21    let server_registry = crate::protocol::server_registry::registry();
22    let mut servers = server_registry.all_protocols();
23    servers.sort_by(|a, b| a.0.cmp(&b.0));
24    out.push_str(&format!("## Server protocols ({})\n\n", servers.len()));
25    for (name, p) in &servers {
26        out.push_str(&render_protocol_doc(name, p.as_ref()));
27    }
28
29    // Clients
30    let mut client_names = crate::protocol::CLIENT_REGISTRY.list_protocols();
31    client_names.sort();
32    out.push_str(&format!("## Client protocols ({})\n\n", client_names.len()));
33    for name in &client_names {
34        if let Some(p) = crate::protocol::CLIENT_REGISTRY.get(name) {
35            out.push_str(&render_protocol_doc(name, p.as_ref()));
36        }
37    }
38
39    out
40}
41
42fn render_protocol_doc<P: Protocol + ?Sized>(name: &str, p: &P) -> String {
43    let mut s = String::new();
44    let meta = p.metadata();
45
46    s.push_str(&format!("### {}\n", name));
47    s.push_str(&format!("- Stack: `{}`\n", p.stack_name()));
48    s.push_str(&format!("- Maturity: {:?}\n", meta.state));
49    s.push_str(&format!("- Privilege: {:?}\n", meta.privilege_requirement));
50    let desc = p.description();
51    if !desc.is_empty() {
52        s.push_str(&format!("- Description: {}\n", desc));
53    }
54    let keywords = p.keywords();
55    if !keywords.is_empty() {
56        s.push_str(&format!("- Keywords: {}\n", keywords.join(", ")));
57    }
58    if let Some(notes) = meta.notes {
59        s.push_str(&format!("- Notes: {}\n", notes));
60    }
61
62    // Startup parameters
63    let params = p.get_startup_parameters();
64    if !params.is_empty() {
65        s.push_str("\n**Startup parameters:**\n");
66        for param in &params {
67            let req = if param.required {
68                "required"
69            } else {
70                "optional"
71            };
72            s.push_str(&format!(
73                "- `{}` ({}, {}): {}\n",
74                param.name, param.type_hint, req, param.description
75            ));
76        }
77    }
78
79    // Events and the actions attached to each
80    let events = p.get_event_types();
81    if !events.is_empty() {
82        s.push_str("\n**Events:**\n");
83        for ev in &events {
84            s.push_str(&format!("- `{}` — {}\n", ev.id, ev.description));
85            for action in &ev.actions {
86                s.push_str(&format!(
87                    "    - action `{}`: {}\n",
88                    action.name, action.description
89                ));
90            }
91        }
92    }
93
94    // Sync (user/network triggered) actions declared outside an event
95    let sync_actions = p.get_sync_actions();
96    if !sync_actions.is_empty() {
97        s.push_str("\n**Actions:**\n");
98        for action in &sync_actions {
99            s.push_str(&format!("- `{}`: {}\n", action.name, action.description));
100            for param in &action.parameters {
101                let req = if param.required {
102                    "required"
103                } else {
104                    "optional"
105                };
106                s.push_str(&format!(
107                    "    - `{}` ({}, {}): {}\n",
108                    param.name, param.type_hint, req, param.description
109                ));
110            }
111        }
112    }
113
114    // Setup examples (LLM / script / static), if the protocol provides real ones
115    let examples = p.get_startup_examples().to_prompt_text();
116    if !examples.trim().is_empty() {
117        s.push_str("\n**Examples:**\n");
118        s.push_str(&examples);
119        s.push('\n');
120    }
121
122    s.push('\n');
123    s
124}