Updates.
This commit is contained in:
79
packages/rust/docs/core-api.inc.tce
Normal file
79
packages/rust/docs/core-api.inc.tce
Normal file
@@ -0,0 +1,79 @@
|
||||
Heading 2 Core API
|
||||
class mt-12
|
||||
Markdown
|
||||
**Note:** The Core API provides low-level parsing functionality optimized for performance
|
||||
and memory efficiency. It uses direct mutation patterns similar to C for optimal performance.
|
||||
|
||||
For most projects you'll want to use the [Document API](#document-api) instead.
|
||||
It provides an ergonomic wrapper around the Core API and lets you focus on parsing
|
||||
your documents without worrying about low-level details.
|
||||
|
||||
Heading 3 LineData
|
||||
class mb-4 mt-12
|
||||
CodeBlock rust
|
||||
// Struct Definition
|
||||
/// Holds the parsed information from each line.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LineData {
|
||||
/// Which character is being used for indentation.
|
||||
pub indent: char,
|
||||
/// How many indent characters are present in the current line before the first non-indent character.
|
||||
pub level: usize,
|
||||
/// The number of characters before the start of the line's "head" section.
|
||||
pub offset_head: usize,
|
||||
/// The number of characters before the start of the line's "tail" section.
|
||||
pub offset_tail: usize,
|
||||
}
|
||||
|
||||
Heading 3 create_line_data()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| indent | char | The character used for indentation in the document. Only a single character is permitted.
|
||||
| **@returns** | [LineData](#line-data) | A LineData instance with the specified indent character and all other values initialized to 0.
|
||||
|
||||
Initialize a LineData instance with default values to pass to [parse_line()](#parse-line).
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn create_line_data(indent: char) -> LineData
|
||||
|
||||
// Import Path
|
||||
use terrace::parser::{create_line_data, LineData};
|
||||
|
||||
// Usage
|
||||
let line_data = create_line_data(' ');
|
||||
println!("{:?}", line_data);
|
||||
// LineData { indent: ' ', level: 0, offset_head: 0, offset_tail: 0 }
|
||||
|
||||
// Use the same line_data object for all calls to parse_line in the same document.
|
||||
|
||||
Heading 3 parse_line()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| line | &str | A string slice containing a line to parse. Shouldn't end with a newline.
|
||||
| line_data | &mut [LineData](#line-data) | A mutable reference to a LineData object to store information about the current line, from [create_line_data()](#create-line-data).<br/>**Mutated in-place!**
|
||||
|
||||
Core Terrace parser function, sets `level`, `offset_head`, and `offset_tail` in a [LineData](#line-data) object based on the passed line.
|
||||
Note that this is a C-style function, `line_data` is treated as a mutable reference and mutated in-place for performance.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn parse_line(line: &str, line_data: &mut LineData)
|
||||
|
||||
// Import Path
|
||||
use terrace::parser::{create_line_data, parse_line};
|
||||
|
||||
// Usage
|
||||
let mut line_data = create_line_data(' ');
|
||||
parse_line("title Example Title", &mut line_data);
|
||||
println!("{:?}", line_data);
|
||||
// LineData { indent: ' ', level: 0, offset_head: 0, offset_tail: 5 }
|
||||
|
||||
// Parse indented line
|
||||
parse_line(" subtitle Example Subtitle", &mut line_data);
|
||||
println!("{:?}", line_data);
|
||||
// LineData { indent: ' ', level: 2, offset_head: 2, offset_tail: 10 }
|
||||
296
packages/rust/docs/document-api.inc.tce
Normal file
296
packages/rust/docs/document-api.inc.tce
Normal file
@@ -0,0 +1,296 @@
|
||||
Heading 2 Document API
|
||||
class mt-12
|
||||
|
||||
Heading 3 TerraceDocument::new()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| reader | impl [Reader](#reader) | An object that implements the Reader trait for reading lines.
|
||||
| indent | char | The character used for indentation in the document. Only a single character is permitted.
|
||||
| **@returns** | [TerraceDocument](#terrace-document) | An async iterator for parsing a Terrace document line by line.
|
||||
|
||||
Creates a new TerraceDocument with the specified reader and indentation character.
|
||||
This is the main entry point for parsing Terrace documents.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn new<R: Reader + Send + Sync + 'static>(reader: R, indent: char) -> Self
|
||||
|
||||
// Import Path
|
||||
use terrace::{TerraceDocument, StringReader};
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n host localhost");
|
||||
let mut doc = TerraceDocument::new(reader, ' ');
|
||||
|
||||
Heading 3 TerraceDocument::with_reader()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| reader | impl [Reader](#reader) | An object that implements the Reader trait for reading lines.
|
||||
| **@returns** | [TerraceDocument](#terrace-document) | An async iterator for parsing a Terrace document with default space indentation.
|
||||
|
||||
Creates a new TerraceDocument with the specified reader and default space (' ') indentation.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn with_reader<R: Reader + Send + Sync + 'static>(reader: R) -> Self
|
||||
|
||||
// Import Path
|
||||
use terrace::{TerraceDocument, StringReader};
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n host localhost");
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
Heading 3 TerraceDocument
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
The main document iterator that provides async access to parsed Terrace nodes.
|
||||
Use this for ergonomic document parsing with automatic memory management and async iteration.
|
||||
|
||||
CodeBlock rust
|
||||
// Struct Definition
|
||||
pub struct TerraceDocument {
|
||||
// Implementation details...
|
||||
}
|
||||
|
||||
Heading 3 TerraceDocument::next()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| **@returns** | Option<[TerraceNode](#terrace-node)> | The next parsed node in the document, or None if the document has ended.
|
||||
|
||||
Advances to the next line in the document and returns a parsed TerraceNode.
|
||||
This method is async and should be called in an async context.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub async fn next(&mut self) -> Option<TerraceNode>
|
||||
|
||||
// Import Path
|
||||
use terrace::{TerraceDocument, StringReader};
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("line1\n line2\nline3");
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
while let Some(node) = doc.next().await {
|
||||
println!("Level: {}, Content: '{}'", node.level(), node.content());
|
||||
}
|
||||
|
||||
Heading 3 TerraceNode
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
Represents a single parsed line/node in a Terrace document.
|
||||
Provides convenient access to different parts of the parsed line.
|
||||
|
||||
CodeBlock rust
|
||||
// Struct Definition
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TerraceNode {
|
||||
// Implementation details...
|
||||
}
|
||||
|
||||
Heading 3 TerraceNode::head()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| **@returns** | &str | The head portion of the node (text before the first space).
|
||||
|
||||
Returns the first word or identifier of the line.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn head(&self) -> &str
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config database localhost");
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
if let Some(node) = doc.next().await {
|
||||
assert_eq!(node.head(), "config");
|
||||
}
|
||||
|
||||
Heading 3 TerraceNode::tail()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| **@returns** | &str | The tail portion of the node (text after the first space).
|
||||
|
||||
Returns everything after the first space character in the line.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn tail(&self) -> &str
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config database localhost");
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
if let Some(node) = doc.next().await {
|
||||
assert_eq!(node.tail(), "database localhost");
|
||||
}
|
||||
|
||||
Heading 3 TerraceNode::content()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| **@returns** | &str | The full content of the node after indentation.
|
||||
|
||||
Returns the complete text content of the line, excluding the indentation characters.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn content(&self) -> &str
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new(" config database localhost");
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
if let Some(node) = doc.next().await {
|
||||
assert_eq!(node.content(), "config database localhost");
|
||||
assert_eq!(node.level(), 2);
|
||||
}
|
||||
|
||||
Heading 3 TerraceNode::level()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| **@returns** | usize | The indentation level of the node.
|
||||
|
||||
Returns the number of indentation characters at the beginning of the line.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn level(&self) -> usize
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n host localhost");
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
let levels: Vec<usize> = doc.map(|node| node.level()).await;
|
||||
assert_eq!(levels, vec![0, 1, 2]);
|
||||
|
||||
Heading 3 TerraceNode::is()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| value | &str | The value to compare against the node's head.
|
||||
| **@returns** | bool | True if the node's head matches the given value.
|
||||
|
||||
Convenience method to check if the node's head matches a specific value.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub fn is(&self, value: &str) -> bool
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n server");
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
if let Some(node) = doc.next().await {
|
||||
assert!(node.is("config"));
|
||||
assert!(!node.is("database"));
|
||||
}
|
||||
|
||||
Heading 3 TerraceDocument::collect()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| **@returns** | Vec<[TerraceNode](#terrace-node)> | A vector containing all nodes in the document.
|
||||
|
||||
Collects all nodes from the document into a vector for batch processing.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub async fn collect(mut self) -> Vec<TerraceNode>
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n host localhost");
|
||||
let doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
let nodes = doc.collect().await;
|
||||
assert_eq!(nodes.len(), 3);
|
||||
assert_eq!(nodes[0].head(), "config");
|
||||
assert_eq!(nodes[1].head(), "database");
|
||||
assert_eq!(nodes[2].head(), "host");
|
||||
|
||||
Heading 3 TerraceDocument::filter()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| predicate | F | A closure that takes a &TerraceNode and returns bool.
|
||||
| **@returns** | Vec<[TerraceNode](#terrace-node)> | A vector containing only nodes that match the predicate.
|
||||
|
||||
Filters nodes based on a predicate function.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub async fn filter<F>(mut self, predicate: F) -> Vec<TerraceNode>
|
||||
where
|
||||
F: FnMut(&TerraceNode) -> bool,
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n server\n database");
|
||||
let doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
let databases = doc.filter(|node| node.head() == "database").await;
|
||||
assert_eq!(databases.len(), 2);
|
||||
|
||||
Heading 3 TerraceDocument::find()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| predicate | F | A closure that takes a &TerraceNode and returns bool.
|
||||
| **@returns** | Option<[TerraceNode](#terrace-node)> | The first node that matches the predicate, or None.
|
||||
|
||||
Finds the first node that matches a predicate function.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub async fn find<F>(mut self, predicate: F) -> Option<TerraceNode>
|
||||
where
|
||||
F: FnMut(&TerraceNode) -> bool,
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n server");
|
||||
let doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
let server_node = doc.find(|node| node.head() == "server").await;
|
||||
assert!(server_node.is_some());
|
||||
assert_eq!(server_node.unwrap().head(), "server");
|
||||
|
||||
Heading 3 TerraceDocument::map()
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| mapper | F | A closure that takes a TerraceNode and returns a value of type T.
|
||||
| **@returns** | Vec<T> | A vector containing the mapped values.
|
||||
|
||||
Transforms each node using a mapper function.
|
||||
|
||||
CodeBlock rust
|
||||
// Function Signature
|
||||
pub async fn map<F, T>(mut self, mapper: F) -> Vec<T>
|
||||
where
|
||||
F: FnMut(TerraceNode) -> T,
|
||||
|
||||
// Usage
|
||||
let reader = StringReader::new("config\n database\n server");
|
||||
let doc = TerraceDocument::with_reader(reader);
|
||||
|
||||
let heads: Vec<String> = doc.map(|node| node.head().to_string()).await;
|
||||
assert_eq!(heads, vec!["config", "database", "server"]);
|
||||
56
packages/rust/docs/index.tce
Normal file
56
packages/rust/docs/index.tce
Normal file
@@ -0,0 +1,56 @@
|
||||
layout layout.njk
|
||||
title Rust Documentation - Terrace
|
||||
description
|
||||
Rust language documentation for the Terrace programming language
|
||||
|
||||
Section light
|
||||
class flex flex-col md:flex-row gap-16
|
||||
|
||||
Block
|
||||
class w-full lg:w-1/3
|
||||
TableOfContents
|
||||
|
||||
Block
|
||||
Heading 1 Terrace Rust Documentation
|
||||
class -ml-2
|
||||
|
||||
Markdown
|
||||
Documentation is available for the following languages:
|
||||
- [C](/docs/c/) - 100% Complete
|
||||
- [JavaScript](/docs/javascript/) - 75% Complete
|
||||
- [Go](/docs/go/) - 50% Complete
|
||||
- [Python](/docs/python/) - 100% Complete
|
||||
- [Rust](/docs/rust/) - 100% Complete
|
||||
|
||||
Heading 2 Getting Started
|
||||
class mt-12 mb-6
|
||||
Markdown
|
||||
Add Terrace to your `Cargo.toml`:
|
||||
|
||||
CodeBlock toml
|
||||
[dependencies]
|
||||
terrace = "0.1"
|
||||
|
||||
Markdown
|
||||
Or use Cargo to add it:
|
||||
|
||||
CodeBlock bash
|
||||
$ cargo add terrace
|
||||
|
||||
Include ./core-api.inc.tce
|
||||
Include ./document-api.inc.tce
|
||||
Include ./reader-api.inc.tce
|
||||
Include ./recipes.inc.tce
|
||||
|
||||
Heading 2 Contributing
|
||||
class mt-12
|
||||
Markdown
|
||||
The Rust implementation is fully open source. Contributions are welcome!
|
||||
|
||||
- [GitHub Repository](https://github.com/terrace-lang/terrace)
|
||||
- [Issue Tracker](https://github.com/terrace-lang/terrace/issues)
|
||||
- [Rust Package](https://crates.io/crates/terrace)
|
||||
|
||||
Section dark
|
||||
Footer
|
||||
class w-full
|
||||
185
packages/rust/docs/reader-api.inc.tce
Normal file
185
packages/rust/docs/reader-api.inc.tce
Normal file
@@ -0,0 +1,185 @@
|
||||
Heading 2 Reader API
|
||||
class mt-12
|
||||
Markdown
|
||||
The [Document API](#document-api) requires `Reader` implementations to iterate through lines
|
||||
in a document. A reader is any type that implements the `Reader` trait, which provides
|
||||
an async method to read the next line from a source.
|
||||
|
||||
Terrace provides built-in readers for common use cases, but you can implement the trait
|
||||
for your own custom sources.
|
||||
|
||||
Heading 3 Reader Trait
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
The Reader trait defines the interface for reading lines from a document source.
|
||||
Implement this trait to create custom readers for different input sources.
|
||||
|
||||
CodeBlock rust
|
||||
// Trait Definition
|
||||
#[async_trait::async_trait]
|
||||
pub trait Reader {
|
||||
/// Read the next line from the source.
|
||||
/// Returns None if there are no more lines.
|
||||
async fn read_line(&mut self) -> io::Result<Option<String>>;
|
||||
}
|
||||
|
||||
Heading 3 StringReader
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| source | impl Into<StringReaderSource> | The source content as a string or vector of strings.
|
||||
| **@returns** | [StringReader](#string-reader) | A reader that iterates over the lines in the source.
|
||||
|
||||
A reader that reads from a string or vector of strings. This is the most common reader
|
||||
for parsing Terrace documents from memory.
|
||||
|
||||
CodeBlock rust
|
||||
// Struct Definition
|
||||
pub struct StringReader {
|
||||
// Implementation details...
|
||||
}
|
||||
|
||||
// Constructor
|
||||
pub fn new(source: impl Into<StringReaderSource>) -> Self
|
||||
|
||||
// Import Path
|
||||
use terrace::readers::StringReader;
|
||||
|
||||
// Usage
|
||||
// From a string
|
||||
let reader = StringReader::new("line1\nline2\nline3");
|
||||
|
||||
// From a vector of strings
|
||||
let reader = StringReader::new(vec!["line1", "line2", "line3"]);
|
||||
|
||||
// From a string slice
|
||||
let reader = StringReader::new("line1\nline2\nline3");
|
||||
|
||||
Heading 3 AsyncReader
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
| Parameter | Type | Description
|
||||
| -------------- | --------------------- | -----------------------------------------------------------------------
|
||||
| reader | R | Any async reader that implements AsyncRead.
|
||||
| **@returns** | [AsyncReader](#async-reader)<R> | A reader that reads lines from an async source.
|
||||
|
||||
A reader that reads from any async source that implements the `AsyncRead` trait.
|
||||
This is useful for reading from files, network streams, or other async sources.
|
||||
|
||||
CodeBlock rust
|
||||
// Struct Definition
|
||||
pub struct AsyncReader<R> {
|
||||
// Implementation details...
|
||||
}
|
||||
|
||||
// Constructor
|
||||
pub fn new(reader: R) -> Self
|
||||
|
||||
// Import Path
|
||||
use terrace::readers::AsyncReader;
|
||||
use tokio::fs::File;
|
||||
|
||||
// Usage
|
||||
let file = File::open("document.tce").await?;
|
||||
let reader = AsyncReader::new(file);
|
||||
|
||||
// Can also be used with other async sources
|
||||
use tokio::io::BufReader;
|
||||
let buffered = BufReader::new(file);
|
||||
let reader = AsyncReader::new(buffered);
|
||||
|
||||
Heading 3 Custom Reader Implementation
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
You can implement the Reader trait for your own custom sources. This allows you
|
||||
to read from databases, APIs, or any other source of line-based data.
|
||||
|
||||
CodeBlock rust
|
||||
// Custom Reader Example
|
||||
use async_trait::async_trait;
|
||||
use std::io;
|
||||
use terrace::readers::Reader;
|
||||
|
||||
struct DatabaseReader {
|
||||
connection: DatabaseConnection,
|
||||
query: String,
|
||||
current_row: usize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Reader for DatabaseReader {
|
||||
async fn read_line(&mut self) -> io::Result<Option<String>> {
|
||||
// Fetch next row from database
|
||||
match self.connection.fetch_row(&self.query, self.current_row).await {
|
||||
Ok(Some(row)) => {
|
||||
self.current_row += 1;
|
||||
Ok(Some(format!("{} {}", row.key, row.value)))
|
||||
}
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
let db_reader = DatabaseReader {
|
||||
connection: connect_to_db().await?,
|
||||
query: "SELECT key, value FROM config".to_string(),
|
||||
current_row: 0,
|
||||
};
|
||||
|
||||
let mut doc = TerraceDocument::with_reader(db_reader);
|
||||
while let Some(node) = doc.next().await {
|
||||
println!("{}: {}", node.head(), node.tail());
|
||||
}
|
||||
|
||||
Heading 3 Reader Trait Implementation Details
|
||||
class mb-4 mt-12
|
||||
Markdown
|
||||
When implementing the Reader trait, follow these guidelines:
|
||||
|
||||
- Return `Ok(Some(line))` for each line of content
|
||||
- Return `Ok(None)` when there are no more lines
|
||||
- Return `Err(error)` if an I/O error occurs
|
||||
- Lines should not include trailing newlines
|
||||
- The reader should be mutable to track state between calls
|
||||
|
||||
CodeBlock rust
|
||||
// Complete Reader Implementation Example
|
||||
use async_trait::async_trait;
|
||||
use std::io;
|
||||
use terrace::readers::Reader;
|
||||
|
||||
struct VecReader {
|
||||
lines: Vec<String>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl VecReader {
|
||||
fn new(lines: Vec<String>) -> Self {
|
||||
Self { lines, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Reader for VecReader {
|
||||
async fn read_line(&mut self) -> io::Result<Option<String>> {
|
||||
if self.index >= self.lines.len() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let line = self.lines[self.index].clone();
|
||||
self.index += 1;
|
||||
Ok(Some(line))
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
let lines = vec![
|
||||
"config".to_string(),
|
||||
" database".to_string(),
|
||||
" host localhost".to_string(),
|
||||
];
|
||||
let reader = VecReader::new(lines);
|
||||
let mut doc = TerraceDocument::with_reader(reader);
|
||||
416
packages/rust/docs/recipes.inc.tce
Normal file
416
packages/rust/docs/recipes.inc.tce
Normal file
@@ -0,0 +1,416 @@
|
||||
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);
|
||||
}
|
||||
42
packages/rust/docs/render.js
Normal file
42
packages/rust/docs/render.js
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Simple renderer for Terrace Rust documentation
|
||||
* This would be used by the documentation build system
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Simple template rendering for the Rust docs
|
||||
function renderRustDocs() {
|
||||
console.log('Rendering Terrace Rust Documentation...');
|
||||
|
||||
const docsDir = path.dirname(__filename);
|
||||
const files = [
|
||||
'index.tce',
|
||||
'core-api.inc.tce',
|
||||
'document-api.inc.tce',
|
||||
'reader-api.inc.tce',
|
||||
'recipes.inc.tce'
|
||||
];
|
||||
|
||||
files.forEach(file => {
|
||||
const filePath = path.join(docsDir, file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
console.log(`✓ Found ${file}`);
|
||||
} else {
|
||||
console.log(`✗ Missing ${file}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Rust documentation files are ready for the build system.');
|
||||
}
|
||||
|
||||
// Export for use in build scripts
|
||||
module.exports = { renderRustDocs };
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
renderRustDocs();
|
||||
}
|
||||
Reference in New Issue
Block a user