Heading 2 Recipes class mt-12 Heading 3 Basic Document Parsing class mb-2 Markdown Parse a simple Terrace document and print all nodes with their levels. CodeBlock rust use terrace::{TerraceDocument, StringReader}; #[tokio::main] async fn main() { let content = r#" config database host localhost port 5432 server port 3000 host 0.0.0.0 "#; let reader = StringReader::new(content); let mut doc = TerraceDocument::with_reader(reader); while let Some(node) = doc.next().await { if !node.is_empty() { println!("{:indent$}{}: '{}'", "", node.head(), node.tail(), indent = node.level() * 2); } } } Markdown This will output: ``` config: '' database: '' host: 'localhost' port: '5432' server: '' port: '3000' host: '0.0.0.0' ``` Heading 3 Read Configuration into Struct class mb-2 Markdown Parse a Terrace configuration file and map it to a Rust struct. CodeBlock rust use terrace::{TerraceDocument, StringReader}; use std::collections::HashMap; #[derive(Debug)] struct Config { database: DatabaseConfig, server: ServerConfig, } #[derive(Debug)] struct DatabaseConfig { host: String, port: u16, name: String, } #[derive(Debug)] struct ServerConfig { host: String, port: u16, } #[tokio::main] async fn main() { let content = r#" config database host localhost port 5432 name mydb server host 0.0.0.0 port 3000 "#; let reader = StringReader::new(content); let doc = TerraceDocument::with_reader(reader); let mut config = Config { database: DatabaseConfig { host: String::new(), port: 0, name: String::new(), }, server: ServerConfig { host: String::new(), port: 0, }, }; let nodes = doc.collect().await; for node in nodes { match (node.level(), node.head()) { (1, "database") => { // Parse database section // In a real implementation, you'd iterate through children } (1, "server") => { // Parse server section } (2, "host") if node.tail().starts_with("localhost") => { config.database.host = node.tail().to_string(); } (2, "port") => { if let Ok(port) = node.tail().parse::() { if node.tail() == "5432" { config.database.port = port; } else if node.tail() == "3000" { config.server.port = port; } } } (2, "name") => { config.database.name = node.tail().to_string(); } _ => {} } } println!("{:?}", config); } Heading 3 Filter and Process Specific Nodes class mb-2 Markdown Find all nodes with a specific head value and process them. CodeBlock rust use terrace::{TerraceDocument, StringReader}; #[tokio::main] async fn main() { let content = r#" users user alice active user bob inactive user charlie active user david inactive groups group admins member alice member charlie group users member bob member david "#; let reader = StringReader::new(content); let doc = TerraceDocument::with_reader(reader); // Find all active users let active_users = doc.filter(|node| { node.head() == "user" && node.tail().contains("active") }).await; println!("Active users:"); for user in active_users { let parts: Vec<&str> = user.tail().split_whitespace().collect(); if parts.len() >= 2 { println!(" {} ({})", parts[0], parts[1]); } } // Alternative: Process all users let reader2 = StringReader::new(content); let doc2 = TerraceDocument::with_reader(reader2); let all_users: Vec<(String, String)> = doc2 .filter(|node| node.head() == "user") .await .into_iter() .filter_map(|node| { let parts: Vec<&str> = node.tail().split_whitespace().collect(); if parts.len() >= 2 { Some((parts[0].to_string(), parts[1].to_string())) } else { None } }) .collect(); println!("\nAll users:"); for (name, status) in all_users { println!(" {}: {}", name, status); } } Heading 3 Build Hierarchical Data Structure class mb-2 Markdown Parse a Terrace document into a hierarchical data structure. CodeBlock rust use terrace::{TerraceDocument, StringReader}; use std::collections::HashMap; #[derive(Debug)] enum Value { String(String), Number(f64), Boolean(bool), Object(HashMap), } #[tokio::main] async fn main() { let content = r#" app name My Application version 1.0.0 debug true database host localhost port 5432 credentials username admin password secret features auth true logging false "#; let reader = StringReader::new(content); let mut doc = TerraceDocument::with_reader(reader); let mut root = HashMap::new(); let mut stack: Vec<(String, HashMap)> = Vec::new(); let mut current = &mut root; while let Some(node) = doc.next().await { if node.is_empty() { continue; } match node.level() { 0 => { // Root level - should be the main object name if node.head() == "app" { // Already at root } } level => { // Adjust stack to match current level while stack.len() >= level { stack.pop(); } // Update current reference if let Some((_, ref mut obj)) = stack.last_mut() { current = obj; } else { current = &mut root; } // Parse the value let value = if let Ok(num) = node.tail().parse::() { Value::Number(num) } else if node.tail() == "true" { Value::Boolean(true) } else if node.tail() == "false" { Value::Boolean(false) } else if node.tail().is_empty() { // This is a nested object let mut nested = HashMap::new(); current.insert(node.head().to_string(), Value::Object(nested.clone())); stack.push((node.head().to_string(), nested)); continue; } else { Value::String(node.tail().to_string()) }; current.insert(node.head().to_string(), value); } } } println!("Parsed configuration:"); println!("{:?}", root); } Heading 3 Async File Reading class mb-2 Markdown Read a Terrace document from a file asynchronously. CodeBlock rust use terrace::{TerraceDocument, readers::AsyncReader}; use tokio::fs::File; #[tokio::main] async fn main() -> Result<(), Box> { // Open the file asynchronously let file = File::open("config.tce").await?; let reader = AsyncReader::new(file); let mut doc = TerraceDocument::with_reader(reader); println!("Configuration from file:"); while let Some(node) = doc.next().await { if !node.is_empty() { println!("{:indent$}{}: '{}'", "", node.head(), node.tail(), indent = node.level() * 2); } } Ok(()) } Heading 3 Error Handling class mb-2 Markdown Handle parsing errors and edge cases gracefully. CodeBlock rust use terrace::{TerraceDocument, StringReader}; #[tokio::main] async fn main() { let content = r#" config database host localhost port not_a_number timeout 30 server port 3000 host "#; let reader = StringReader::new(content); let mut doc = TerraceDocument::with_reader(reader); while let Some(node) = doc.next().await { if node.is_empty() { continue; } match node.head() { "port" => { match node.tail().parse::() { Ok(port) => println!("Port: {}", port), Err(_) => eprintln!("Warning: Invalid port '{}'", node.tail()), } } "host" => { if node.tail().is_empty() { eprintln!("Warning: Empty host value"); } else { println!("Host: {}", node.tail()); } } "timeout" => { match node.tail().parse::() { Ok(timeout) => println!("Timeout: {}ms", timeout), Err(_) => eprintln!("Warning: Invalid timeout '{}'", node.tail()), } } _ => { println!("{}: {}", node.head(), node.tail()); } } } } Heading 3 Streaming Large Documents class mb-2 Markdown Process very large documents without loading everything into memory. CodeBlock rust use terrace::{TerraceDocument, StringReader}; #[tokio::main] async fn main() { // Simulate a large document let mut large_content = String::new(); for i in 0..10000 { large_content.push_str(&format!("item{}\n value{}\n count {}\n", i, i * 2, i * 3)); } let reader = StringReader::new(large_content); let mut doc = TerraceDocument::with_reader(reader); let mut item_count = 0; let mut total_values = 0i64; while let Some(node) = doc.next().await { match node.head() { "item" => { item_count += 1; } "value" => { if let Ok(value) = node.tail().parse::() { total_values += value; } } _ => {} } } println!("Processed {} items", item_count); println!("Total values: {}", total_values); println!("Average value: {}", total_values as f64 / item_count as f64); }