Installation
Add to yourCargo.toml:
[dependencies]
glyph-codec = "1.0"
serde_json = "1.0" # For JSON bridge
Quick Start
use glyph_codec::{from_json, canonicalize_loose, GValue};
use serde_json::json;
fn main() {
// JSON → GLYPH (30-50% more token-efficient)
let data = json!({
"action": "search",
"query": "weather in NYC",
"max_results": 10
});
let gvalue = from_json(&data);
let glyph = canonicalize_loose(&gvalue);
println!("{}", glyph);
// Output: {action=search max_results=10 query="weather in NYC"}
}
Core API
GValue Type
use glyph_codec::GValue;
// Create values
let null_val = GValue::Null;
let bool_val = GValue::Bool(true);
let int_val = GValue::Int(42);
let float_val = GValue::Float(3.14);
let str_val = GValue::Str("hello".to_string());
let bytes_val = GValue::Bytes(vec![1, 2, 3]);
// Lists
let list = GValue::List(vec![
GValue::Int(1),
GValue::Int(2),
GValue::Int(3),
]);
// Maps
use glyph_codec::MapEntry;
let map = GValue::Map(vec![
MapEntry::new("name".to_string(), GValue::Str("Alice".to_string())),
MapEntry::new("age".to_string(), GValue::Int(30)),
]);
Canonicalization
use glyph_codec::{canonicalize_loose, canonicalize_loose_no_tabular};
// Default: auto-tabular enabled
let text = canonicalize_loose(&value);
// Without auto-tabular
let text = canonicalize_loose_no_tabular(&value);
JSON Bridge
use glyph_codec::{from_json, to_json};
use serde_json::{json, Value as JsonValue};
// JSON → GValue
let json = json!({
"name": "Alice",
"age": 30,
"active": true
});
let gvalue = from_json(&json);
// GValue → JSON
let back_to_json = to_json(&gvalue);
assert_eq!(json, back_to_json);
Pattern Matching
use glyph_codec::GValue;
match value {
GValue::Null => println!("null"),
GValue::Bool(b) => println!("bool: {}", b),
GValue::Int(n) => println!("int: {}", n),
GValue::Float(f) => println!("float: {}", f),
GValue::Str(s) => println!("string: {}", s),
GValue::List(items) => {
for item in items {
println!("item: {:?}", item);
}
}
GValue::Map(entries) => {
for entry in entries {
println!("{}: {:?}", entry.key, entry.value);
}
}
_ => println!("other type"),
}
Helper Functions
get_map_value
use glyph_codec::{GValue, get_map_value};
let map = GValue::Map(vec![
MapEntry::new("name".to_string(), GValue::Str("Alice".to_string())),
MapEntry::new("age".to_string(), GValue::Int(30)),
]);
// Get value by key
if let Some(name) = get_map_value(&map, "name") {
if let GValue::Str(s) = name {
println!("Name: {}", s);
}
}
as_* Methods
// Extract typed values with pattern matching
if let GValue::Int(n) = value {
println!("Integer: {}", n);
}
if let GValue::Str(s) = value {
println!("String: {}", s);
}
if let GValue::List(items) = value {
println!("List length: {}", items.len());
}
MapEntry
use glyph_codec::MapEntry;
// Create map entry
let entry = MapEntry {
key: "name".to_string(),
value: GValue::Str("Alice".to_string()),
};
// Or use new() constructor
let entry = MapEntry::new(
"age".to_string(),
GValue::Int(30)
);
Error Handling
All operations returnResult<T, GlyphError>:
use glyph_codec::{from_json, to_json, GlyphError};
use serde_json::json;
fn process() -> Result<(), GlyphError> {
let data = json!({"x": 1, "y": 2});
let gvalue = from_json(&data);
let glyph = canonicalize_loose(&gvalue);
// Operations are infallible for valid GValues
// Errors mainly occur in parsing (not yet implemented)
Ok(())
}
match process() {
Ok(_) => println!("Success"),
Err(e) => eprintln!("Error: {}", e),
}
Real-World Examples
LLM Tool Calls
use glyph_codec::{from_json, canonicalize_loose};
use serde_json::json;
fn handle_tool_call(action: &str, args: &GValue) -> String {
// Agent receives args as GLYPH (30-50% fewer tokens)
let glyph = canonicalize_loose(args);
// Execute and return results in GLYPH
let results = execute_action(action, args);
let result_gvalue = from_json(&results);
canonicalize_loose(&result_gvalue)
}
fn main() {
let args = json!({
"action": "search",
"query": "weather in SF",
"max_results": 5
});
let gvalue = from_json(&args);
let response = handle_tool_call("search", &gvalue);
println!("Response: {}", response);
}
Serialize API Request
use glyph_codec::{from_json, canonicalize_loose};
use serde::Serialize;
use serde_json::json;
#[derive(Serialize)]
struct User {
id: u32,
name: String,
score: u32,
}
fn serialize_users(users: Vec<User>) -> String {
// Convert to JSON value
let json = serde_json::to_value(users).unwrap();
// Convert to GLYPH (auto-tabular for homogeneous lists)
let gvalue = from_json(&json);
canonicalize_loose(&gvalue)
// Output:
// @tab _ rows=3 cols=3 [id name score]
// |1|Alice|95|
// |2|Bob|87|
// |3|Carol|92|
// @end
}
fn main() {
let users = vec![
User { id: 1, name: "Alice".to_string(), score: 95 },
User { id: 2, name: "Bob".to_string(), score: 87 },
User { id: 3, name: "Carol".to_string(), score: 92 },
];
let compact = serialize_users(users);
println!("{}", compact);
}
Deserialize from GLYPH
use glyph_codec::{from_json, to_json};
use serde::Deserialize;
use serde_json::Value as JsonValue;
#[derive(Deserialize, Debug)]
struct SearchArgs {
action: String,
query: String,
max_results: u32,
}
fn parse_tool_call(glyph_text: &str) -> Result<SearchArgs, Box<dyn std::error::Error>> {
// In a real implementation, you'd parse the GLYPH text
// For now, demonstrate the flow:
// 1. Parse GLYPH → GValue (parser not yet in Rust SDK)
// 2. GValue → JSON
// 3. JSON → typed struct
// Placeholder: assume we have a GValue
let json = json!({
"action": "search",
"query": "weather",
"max_results": 10
});
let gvalue = from_json(&json);
let json_back = to_json(&gvalue);
let args: SearchArgs = serde_json::from_value(json_back)?;
Ok(args)
}
Type Checking
use glyph_codec::GValue;
fn is_string(value: &GValue) -> bool {
matches!(value, GValue::Str(_))
}
fn is_integer(value: &GValue) -> bool {
matches!(value, GValue::Int(_))
}
fn is_list(value: &GValue) -> bool {
matches!(value, GValue::List(_))
}
fn is_map(value: &GValue) -> bool {
matches!(value, GValue::Map(_))
}
Cloning and Ownership
use glyph_codec::GValue;
// GValue implements Clone
let original = GValue::Str("hello".to_string());
let cloned = original.clone();
// Deep clone for nested structures
let map = GValue::Map(vec![
MapEntry::new("x".to_string(), GValue::Int(1)),
]);
let map_clone = map.clone();
// Move semantics
fn take_ownership(value: GValue) {
println!("Owned: {:?}", value);
}
take_ownership(original); // original moved
// println!("{:?}", original); // Error: value moved
// Borrow instead
fn borrow_value(value: &GValue) {
println!("Borrowed: {:?}", value);
}
borrow_value(&cloned); // cloned still usable
Building Complex Structures
use glyph_codec::{GValue, MapEntry};
// Nested map with list
let data = GValue::Map(vec![
MapEntry::new(
"users".to_string(),
GValue::List(vec![
GValue::Map(vec![
MapEntry::new("id".to_string(), GValue::Int(1)),
MapEntry::new("name".to_string(), GValue::Str("Alice".to_string())),
]),
GValue::Map(vec![
MapEntry::new("id".to_string(), GValue::Int(2)),
MapEntry::new("name".to_string(), GValue::Str("Bob".to_string())),
]),
])
),
MapEntry::new("count".to_string(), GValue::Int(2)),
]);
let glyph = canonicalize_loose(&data);
println!("{}", glyph);
// {count=2 users=@tab _ rows=2 cols=2 [id name]
// |1|Alice|
// |2|Bob|
// @end}
Debug and Display
use glyph_codec::GValue;
let value = GValue::Map(vec![
MapEntry::new("x".to_string(), GValue::Int(1)),
]);
// Debug output (Rust debug format)
println!("{:?}", value);
// Map([MapEntry { key: "x", value: Int(1) }])
// Pretty debug
println!("value = {:#?}", value);
// GLYPH output (use canonicalize_loose)
let glyph = canonicalize_loose(&value);
println!("{}", glyph);
// {x=1}
Integration with Serde
use glyph_codec::{from_json, to_json};
use serde::{Serialize, Deserialize};
use serde_json::json;
#[derive(Serialize, Deserialize, Debug)]
struct Config {
host: String,
port: u16,
enabled: bool,
}
fn main() {
let config = Config {
host: "localhost".to_string(),
port: 8080,
enabled: true,
};
// Struct → JSON → GValue → GLYPH
let json = serde_json::to_value(&config).unwrap();
let gvalue = from_json(&json);
let glyph = canonicalize_loose(&gvalue);
println!("GLYPH: {}", glyph);
// {enabled=t host=localhost port=8080}
// Round-trip: GLYPH → GValue → JSON → Struct
let json_back = to_json(&gvalue);
let config_back: Config = serde_json::from_value(json_back).unwrap();
println!("Config: {:?}", config_back);
}
Performance Considerations
- Cloning - GValue uses
StringandVec, so cloning is O(n) - Canonicalization - O(n) with efficient string building
- Map lookup - O(n) linear search (use for small maps)
- Auto-tabular - Automatic detection for ≥3 homogeneous maps
Type Definitions
use glyph_codec::{GValue, MapEntry};
// Core enum
pub enum GValue {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
Bytes(Vec<u8>),
List(Vec<GValue>),
Map(Vec<MapEntry>),
}
// Map entry
pub struct MapEntry {
pub key: String,
pub value: GValue,
}
impl MapEntry {
pub fn new(key: String, value: GValue) -> Self {
MapEntry { key, value }
}
}
Testing
#[cfg(test)]
mod tests {
use super::*;
use glyph_codec::{from_json, canonicalize_loose, to_json};
use serde_json::json;
#[test]
fn test_roundtrip() {
let original = json!({
"name": "Alice",
"age": 30,
"active": true
});
let gvalue = from_json(&original);
let glyph = canonicalize_loose(&gvalue);
let back_to_json = to_json(&gvalue);
assert_eq!(original, back_to_json);
}
#[test]
fn test_auto_tabular() {
let data = json!([
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
{"id": 3, "name": "Carol"}
]);
let gvalue = from_json(&data);
let glyph = canonicalize_loose(&gvalue);
// Should use tabular format
assert!(glyph.contains("@tab"));
assert!(glyph.contains("@end"));
}
}
Future Features
The Rust SDK currently provides core types and JSON bridge. Future versions will add:- Parser - Parse GLYPH text → GValue
- Schema support - Type validation and wire keys
- Streaming - Incremental parsing and emission
- Time type - Datetime support
- RefID type - Reference IDs (^prefix:value)
- Struct/Sum types - Tagged types for better semantics