Skip to main content

gcf/
lib.rs

1//! GCF (Graph Compact Format) encoder and decoder for Rust.
2//!
3//! GCF is a compact, text-only, graph-native wire format designed for MCP tool
4//! responses. It exploits referential identity (local IDs), graph topology
5//! (edges as references), and hierarchical grouping (distance-based sections)
6//! to achieve significant token savings over JSON while remaining human-readable.
7//!
8//! Specification: <https://github.com/blackwell-systems/gcf>
9//!
10//! # Quick Start
11//!
12//! ```
13//! use gcf::{Payload, Symbol, Edge, encode, decode};
14//!
15//! let p = Payload {
16//!     tool: "context_for_task".to_string(),
17//!     token_budget: 5000,
18//!     tokens_used: 1847,
19//!     pack_root: String::new(),
20//!     symbols: vec![
21//!         Symbol {
22//!             qualified_name: "pkg.AuthMiddleware".to_string(),
23//!             kind: "function".to_string(),
24//!             score: 0.78,
25//!             provenance: "lsp_resolved".to_string(),
26//!             distance: 0,
27//!             signature: String::new(),
28//!             components: Default::default(),
29//!         },
30//!     ],
31//!     edges: vec![],
32//! };
33//!
34//! let output = encode(&p);
35//! let decoded = decode(&output).unwrap();
36//! assert_eq!(decoded.tool, "context_for_task");
37//! ```
38
39pub mod decode;
40pub mod delta;
41pub mod encode;
42pub mod generic;
43pub mod session;
44pub mod stream;
45pub mod types;
46
47pub use decode::{decode, DecodeError};
48pub use delta::encode_delta;
49pub use encode::encode;
50pub use generic::encode_generic;
51pub use session::{encode_with_session, Session};
52pub use stream::{StreamEncoder, StreamOptions};
53pub use types::{Components, DeltaPayload, Edge, Payload, Symbol};
54
55use std::collections::HashMap;
56use std::sync::LazyLock;
57
58/// Map from full kind names to short GCF abbreviations.
59static KIND_ABBREV_MAP: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
60    let mut m = HashMap::new();
61    m.insert("function", "fn");
62    m.insert("type", "type");
63    m.insert("method", "method");
64    m.insert("interface", "iface");
65    m.insert("var", "var");
66    m.insert("const", "const");
67    m.insert("resource", "resource");
68    m.insert("table", "table");
69    m.insert("class", "class");
70    m.insert("selector", "selector");
71    m.insert("field", "field");
72    m.insert("route_handler", "route");
73    m.insert("external", "ext");
74    m.insert("file", "file");
75    m.insert("package", "pkg");
76    m.insert("service", "svc");
77    m
78});
79
80/// Map from short GCF abbreviations to full kind names.
81static KIND_EXPAND_MAP: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
82    let mut m = HashMap::new();
83    m.insert("fn", "function");
84    m.insert("type", "type");
85    m.insert("method", "method");
86    m.insert("iface", "interface");
87    m.insert("var", "var");
88    m.insert("const", "const");
89    m.insert("resource", "resource");
90    m.insert("table", "table");
91    m.insert("class", "class");
92    m.insert("selector", "selector");
93    m.insert("field", "field");
94    m.insert("route", "route_handler");
95    m.insert("ext", "external");
96    m.insert("file", "file");
97    m.insert("pkg", "package");
98    m.insert("svc", "service");
99    m
100});
101
102/// Returns the GCF abbreviation for a kind, or the original string if no abbreviation exists.
103pub fn kind_abbrev(kind: &str) -> String {
104    KIND_ABBREV_MAP
105        .get(kind)
106        .map(|s| s.to_string())
107        .unwrap_or_else(|| kind.to_string())
108}
109
110/// Returns the expanded kind for a GCF abbreviation, or the original string if not recognized.
111pub fn kind_expand(abbrev: &str) -> String {
112    KIND_EXPAND_MAP
113        .get(abbrev)
114        .map(|s| s.to_string())
115        .unwrap_or_else(|| abbrev.to_string())
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_kind_abbrev() {
124        assert_eq!(kind_abbrev("function"), "fn");
125        assert_eq!(kind_abbrev("interface"), "iface");
126        assert_eq!(kind_abbrev("route_handler"), "route");
127        assert_eq!(kind_abbrev("external"), "ext");
128        assert_eq!(kind_abbrev("package"), "pkg");
129        assert_eq!(kind_abbrev("service"), "svc");
130        assert_eq!(kind_abbrev("unknown_kind"), "unknown_kind");
131    }
132
133    #[test]
134    fn test_kind_expand() {
135        assert_eq!(kind_expand("fn"), "function");
136        assert_eq!(kind_expand("iface"), "interface");
137        assert_eq!(kind_expand("route"), "route_handler");
138        assert_eq!(kind_expand("ext"), "external");
139        assert_eq!(kind_expand("pkg"), "package");
140        assert_eq!(kind_expand("svc"), "service");
141        assert_eq!(kind_expand("unknown_abbrev"), "unknown_abbrev");
142    }
143
144    #[test]
145    fn test_abbrev_expand_roundtrip() {
146        for (full, abbrev) in KIND_ABBREV_MAP.iter() {
147            assert_eq!(kind_expand(abbrev), *full);
148            assert_eq!(kind_abbrev(full), *abbrev);
149        }
150    }
151}