Skip to main content

netget/protocol/
mod.rs

1//! Protocol type definitions
2//!
3//! The application supports multiple protocol implementations.
4//! Protocol behavior is controlled by the LLM based on the chosen protocol and instructions.
5
6pub mod binding_defaults;
7pub mod client_registry;
8pub mod connect_context;
9pub mod default_port;
10pub mod dependencies;
11pub mod docs;
12pub mod dual;
13pub mod easy_registry;
14pub mod event_logger;
15pub mod event_type;
16pub mod log_template;
17pub mod metadata;
18pub mod server_registry;
19pub mod spawn_context;
20
21pub use binding_defaults::BindingDefaults;
22pub use client_registry::CLIENT_REGISTRY;
23pub use connect_context::ConnectContext;
24pub use dependencies::ProtocolDependency;
25pub use docs::render_all_protocol_docs;
26pub use dual::{client_protocol_for_server, compiled_client_protocol_for_server};
27pub use easy_registry::EASY_REGISTRY;
28pub use event_logger::{log_action_result, EventLogContext};
29pub use event_type::{Event, EventType};
30pub use log_template::{LogLevel, LogTemplate};
31pub use metadata::{DevelopmentState, ProtocolMetadata, ProtocolMetadataV2};
32pub use server_registry::registry;
33pub use spawn_context::{SpawnContext, StartupParamError, StartupParamResult, StartupParams};
34
35/// Build a human-readable listing of every registered server and client
36/// protocol, grouped by its declared [`DevelopmentState`], most-mature first.
37///
38/// Both registries are read at runtime, so the output always reflects the
39/// compiled-in feature set — nothing here is hardcoded. Each line shows the
40/// protocol name, its state, a `[blocked by --min-stability]` marker when
41/// `min_stability` would refuse it, and the one-line `notes` from its metadata
42/// when present. Backs the `/stability` (aka `/protocols`) slash command.
43pub fn stability_report(min_stability: Option<DevelopmentState>) -> Vec<String> {
44    fn one_line_note(notes: Option<&'static str>) -> Option<String> {
45        let raw = notes?;
46        let first = raw.lines().next().unwrap_or(raw).trim();
47        if first.is_empty() {
48            return None;
49        }
50        Some(crate::utils::truncate::truncate_with_suffix(
51            first, 120, "…",
52        ))
53    }
54
55    // (state, name, one-line note) for each registered protocol.
56    let mut server_rows: Vec<(DevelopmentState, String, Option<String>)> = registry()
57        .all_protocols()
58        .into_iter()
59        .map(|(name, p)| {
60            let m = p.metadata();
61            (m.state, name, one_line_note(m.notes))
62        })
63        .collect();
64    let mut client_rows: Vec<(DevelopmentState, String, Option<String>)> = CLIENT_REGISTRY
65        .get_all()
66        .into_iter()
67        .map(|p| {
68            let m = p.metadata();
69            (
70                m.state,
71                p.protocol_name().to_string(),
72                one_line_note(m.notes),
73            )
74        })
75        .collect();
76
77    // Alphabetical within each maturity group.
78    server_rows.sort_by_key(|r| r.1.to_lowercase());
79    client_rows.sort_by_key(|r| r.1.to_lowercase());
80
81    let mut lines = Vec::new();
82    match min_stability {
83        Some(min) => lines.push(format!(
84            "Minimum stability to start (--min-stability): {} — protocols below this are refused",
85            min.as_str()
86        )),
87        None => lines.push(
88            "Minimum stability to start (--min-stability): not set (only Incomplete is hidden from the model)"
89                .to_string(),
90        ),
91    }
92
93    for (label, rows) in [("Servers", &server_rows), ("Clients", &client_rows)] {
94        lines.push(String::new());
95        lines.push(format!("=== {} ({}) ===", label, rows.len()));
96        // Most-mature first.
97        for state in DevelopmentState::ALL.iter().rev().copied() {
98            let group: Vec<_> = rows.iter().filter(|r| r.0 == state).collect();
99            if group.is_empty() {
100                continue;
101            }
102            lines.push(format!("-- {} ({}) --", state.as_str(), group.len()));
103            for (st, name, note) in group {
104                let blocked = min_stability.map(|min| *st < min).unwrap_or(false);
105                let marker = if blocked {
106                    " [blocked by --min-stability]"
107                } else {
108                    ""
109                };
110                match note {
111                    Some(n) => {
112                        lines.push(format!("  {} [{}]{} — {}", name, st.as_str(), marker, n))
113                    }
114                    None => lines.push(format!("  {} [{}]{}", name, st.as_str(), marker)),
115                }
116            }
117        }
118    }
119
120    lines
121}