Skip to main content

draxl/
lib.rs

1#![forbid(unsafe_code)]
2//! Public facade for Draxl, an agent-native source language with
3//! explicit syntax identity.
4//!
5//! Draxl makes syntax identity explicit with stable node ids, ranks, anchors,
6//! and semantic patch operators so tools can edit the program tree directly
7//! instead of patching text spans.
8//!
9//! This crate is the intended Rust integration surface for the workspace. It
10//! keeps the common workflows small and explicit while still re-exporting the
11//! lower-level crates for callers that need finer control.
12//!
13//! ```rust
14//! let source = "@m1 mod demo { @f1[a] fn run() { @s1[a] @e1 work(); } }";
15//! let file = draxl::parse_and_validate(source)?;
16//! let formatted = draxl::format_source(source)?;
17//! let lowered = draxl::lower_rust_source(source)?;
18//! assert_eq!(file.items.len(), 1);
19//! assert!(formatted.contains("@f1[a] fn run()"));
20//! assert!(lowered.contains("fn run()"));
21//! # Ok::<(), draxl::Error>(())
22//! ```
23
24use std::error::Error as StdError;
25use std::fmt;
26
27pub use draxl_ast as ast;
28pub use draxl_lower_rust as lower_rust;
29pub use draxl_parser as parser;
30pub use draxl_patch as patch;
31pub use draxl_printer as printer;
32pub use draxl_validate as validate;
33
34/// Convenience result type for `draxl` workflows.
35pub type Result<T> = std::result::Result<T, Error>;
36
37/// Top-level error for parse-and-validate workflows.
38#[derive(Debug)]
39pub enum Error {
40    /// The source could not be parsed into the Draxl AST.
41    Parse(parser::ParseError),
42    /// The source parsed, but failed structural validation.
43    Validation(Vec<validate::ValidationError>),
44}
45
46impl Error {
47    /// Returns validation errors when the failure happened after parsing.
48    pub fn validation_errors(&self) -> Option<&[validate::ValidationError]> {
49        match self {
50            Self::Validation(errors) => Some(errors),
51            Self::Parse(_) => None,
52        }
53    }
54}
55
56impl fmt::Display for Error {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        match self {
59            Self::Parse(error) => error.fmt(f),
60            Self::Validation(errors) => {
61                f.write_str("validation failed:")?;
62                for error in errors {
63                    f.write_str("\n- ")?;
64                    f.write_str(&error.message)?;
65                }
66                Ok(())
67            }
68        }
69    }
70}
71
72impl StdError for Error {
73    fn source(&self) -> Option<&(dyn StdError + 'static)> {
74        match self {
75            Self::Parse(error) => Some(error),
76            Self::Validation(_) => None,
77        }
78    }
79}
80
81impl From<parser::ParseError> for Error {
82    fn from(error: parser::ParseError) -> Self {
83        Self::Parse(error)
84    }
85}
86
87/// Parses a Draxl source string into the typed AST.
88pub fn parse_file(source: &str) -> std::result::Result<ast::File, parser::ParseError> {
89    parser::parse_file(source)
90}
91
92/// Validates a parsed Draxl file.
93pub fn validate_file(file: &ast::File) -> std::result::Result<(), Vec<validate::ValidationError>> {
94    validate::validate_file(file)
95}
96
97/// Parses and validates a Draxl source string in one step.
98pub fn parse_and_validate(source: &str) -> Result<ast::File> {
99    let file = parse_file(source)?;
100    validate_file(&file).map_err(Error::Validation)?;
101    Ok(file)
102}
103
104/// Canonically formats a parsed Draxl file.
105pub fn format_file(file: &ast::File) -> String {
106    printer::print_file(file)
107}
108
109/// Parses, validates, and canonically formats a Draxl source string.
110pub fn format_source(source: &str) -> Result<String> {
111    let file = parse_and_validate(source)?;
112    Ok(format_file(&file))
113}
114
115/// Emits deterministic JSON for a parsed Draxl file.
116pub fn dump_json_file(file: &ast::File) -> String {
117    printer::canonicalize_file(file).to_json_pretty()
118}
119
120/// Parses, validates, and emits deterministic JSON for a Draxl source string.
121pub fn dump_json_source(source: &str) -> Result<String> {
122    let file = parse_and_validate(source)?;
123    Ok(dump_json_file(&file))
124}
125
126/// Lowers a validated Draxl file to ordinary Rust source.
127pub fn lower_rust_file(file: &ast::File) -> String {
128    lower_rust::lower_file(file)
129}
130
131/// Parses, validates, and lowers Draxl source to ordinary Rust.
132pub fn lower_rust_source(source: &str) -> Result<String> {
133    let file = parse_and_validate(source)?;
134    Ok(lower_rust_file(&file))
135}
136
137/// Applies a single structured patch operation.
138pub fn apply_patch(
139    file: &mut ast::File,
140    op: patch::PatchOp,
141) -> std::result::Result<(), patch::PatchError> {
142    patch::apply_op(file, op)
143}
144
145/// Applies multiple structured patch operations in order.
146pub fn apply_patches(
147    file: &mut ast::File,
148    ops: impl IntoIterator<Item = patch::PatchOp>,
149) -> std::result::Result<(), patch::PatchError> {
150    patch::apply_ops(file, ops)
151}