jute/lib.rs
1//! # Jute
2//!
3//! Jute is a **schema-driven code generator** for Rust, inspired by
4//! Apache ZooKeeper’s *Jute* serialization format.
5//!
6//! This crate provides a high-level API to:
7//!
8//! - Parse `.jute` schema files into an abstract syntax tree (AST)
9//! - Resolve cross-file and cross-module type dependencies
10//! - Validate schemas (unknown types, ambiguities, cycles, etc.)
11//! - Generate idiomatic, type-safe Rust code
12//! - Generate binary serialization and deserialization logic
13//!
14//! The primary entry point to the API is [`JuteGenerator`], which
15//! orchestrates the full pipeline from schema parsing to code generation.
16//!
17//! ## Typical Workflow
18//!
19//! 1. Author one or more `.jute` schema files
20//! 2. Feed them into [`JuteGenerator`]
21//! 3. Generate Rust code into a target directory
22//! 4. Compile and use the generated types directly in your application
23//!
24//! ## Example
25//!
26//! ```no_run
27//! use jute::JuteGenerator;
28//!
29//! JuteGenerator::new()
30//! .add_src_file("schema/common.jute")
31//! .add_src_file("schema/model.jute")
32//! .add_out_dir("generated")
33//! .generate()
34//! .expect("Jute code generation failed");
35//! ```
36//!
37
38use crate::{
39 code_generator::{CodeGenerator, rust::writer::RustCodeGenerator},
40 compiler::{ast::Module, build_ast, dependency_resolver::resolve_dependencies},
41};
42use std::{
43 path::{Path, PathBuf},
44 str::FromStr,
45};
46
47pub mod code_generator;
48pub(crate) mod compiler;
49pub mod errors;
50pub use crate::code_generator::rust::JuteSerializable;
51pub use crate::errors::JuteError;
52
53/// High-level Jute code generation driver.
54///
55/// `JuteGenerator` follows a builder-style API and is responsible for:
56/// 1. Collecting source `.jute` files
57/// 2. Parsing them into ASTs
58/// 3. Resolving type and module dependencies
59/// 4. Invoking the language-specific code generator (Rust)
60///
61/// # Example
62///
63/// ```no_run
64/// use jute::JuteGenerator;
65/// let schema_path1 = "./schema/1.jute";
66/// let schema_path2 = "./schema/2.jute";
67/// let out_dir_path= "generated";
68///
69/// JuteGenerator::new()
70/// .add_src_file(schema_path1)
71/// .add_src_file(schema_path2)
72/// .add_out_dir(out_dir_path)
73/// .generate()
74/// .unwrap();
75/// ```
76#[derive(Default)]
77pub struct JuteGenerator {
78 /// Output directory where generated code will be written.
79 ///
80 /// If not provided, code generation defaults to the current(src) directory.
81 pub out_dir: Option<PathBuf>,
82 /// List of input Jute schema files.
83 pub src_files: Vec<PathBuf>,
84}
85impl JuteGenerator {
86 /// Creates a new empty `JuteGenerator`.
87 pub fn new() -> Self {
88 Self {
89 out_dir: None,
90 src_files: Vec::new(),
91 }
92 }
93
94 /// Adds a Jute source file to the generator.
95 ///
96 /// # Panics
97 ///
98 /// Panics if the same file is added more than once.
99 pub fn add_src_file<P: AsRef<Path>>(mut self, file_path: P) -> Self {
100 let file_path = file_path.as_ref().to_path_buf().canonicalize().unwrap();
101 if self.src_files.iter().any(|x| **x == file_path) {
102 panic!("Same file added multiple times");
103 }
104 self.src_files.push(file_path);
105 self
106 }
107 /// Sets the output directory for generated code.
108 ///
109 /// # Panics
110 ///
111 /// Panics if the output directory is already set.
112 pub fn add_out_dir<P: AsRef<Path>>(mut self, out_dir: P) -> Self {
113 if self.out_dir.is_some() {
114 panic!("Output dir alreay assigned");
115 }
116 let out_dir = out_dir.as_ref().to_path_buf();
117 self.out_dir = Some(out_dir);
118 self
119 }
120
121 /// Executes the full code generation pipeline.
122 ///
123 /// This method:
124 /// 1. Parses all provided `.jute` files into AST documents
125 /// 2. Resolves cross-module and cross-file dependencies
126 /// 3. Merges all modules into a single collection
127 /// 4. Generates Rust code into the configured output directory
128 ///
129 /// # Errors
130 ///
131 /// Returns an error if:
132 /// - Parsing fails
133 /// - Dependency resolution fails
134 /// - Code generation fails
135 pub fn generate(self) -> Result<(), JuteError> {
136 // this function will handle all the generation logic
137 // we will parse all the files one by one and push docs in the vector
138 let mut docs = Vec::new();
139 for file in self.src_files {
140 let doc = build_ast(Path::new(&file))?;
141 docs.push(doc);
142 }
143 let dependencies = resolve_dependencies(&docs)?;
144 let merged_modules: Vec<Module> =
145 docs.into_iter().map(|doc| doc.modules).flatten().collect();
146 // now we have a array of docs now we will validate this doc
147 RustCodeGenerator::new(
148 merged_modules,
149 dependencies,
150 self.out_dir.unwrap_or(PathBuf::from_str("").unwrap()), // unwrap will be never called
151 )
152 .generate()?;
153 Ok(())
154 }
155}