Terrace/packages/rust/docs/recipes.inc.tce
Joshua Bemenderfer 9d9757e868 Updates.
2025-09-08 16:24:38 -04:00

417 lines
11 KiB
Plaintext

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::<u16>() {
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<String, Value>),
}
#[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<String, Value>)> = 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::<f64>() {
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<dyn std::error::Error>> {
// 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::<u16>() {
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::<u64>() {
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::<i64>() {
total_values += value;
}
}
_ => {}
}
}
println!("Processed {} items", item_count);
println!("Total values: {}", total_values);
println!("Average value: {}", total_values as f64 / item_count as f64);
}