fast_yaml_parallel/lib.rs
1//! fast-yaml-parallel: Multi-threaded YAML processing.
2//!
3//! Provides parallel processing for:
4//! - Multi-document YAML streams (document-level parallelism)
5//! - Multiple YAML files (file-level parallelism)
6//!
7//! # Performance
8//!
9//! Expected speedup on multi-document files:
10//! - 4 cores: 3-3.5x faster
11//! - 8 cores: 6-6.5x faster
12//! - 16 cores: 10-12x faster
13//!
14//! # When to Use
15//!
16//! Use parallel processing when:
17//! - Processing multi-document YAML streams (logs, configs, data dumps)
18//! - Batch processing multiple YAML files
19//! - Input size > 1MB with multiple documents
20//! - Running on multi-core hardware (4+ cores recommended)
21//!
22//! Use sequential processing when:
23//! - Single document files
24//! - Small files (<100KB)
25//! - Memory constrained environments
26//!
27//! # Examples
28//!
29//! Document-level parallelism:
30//!
31//! ```
32//! use fast_yaml_parallel::parse_parallel;
33//!
34//! let yaml = "---\nfoo: 1\n---\nbar: 2\n---\nbaz: 3";
35//! let docs = parse_parallel(yaml)?;
36//! assert_eq!(docs.len(), 3);
37//! # Ok::<(), Box<dyn std::error::Error>>(())
38//! ```
39//!
40//! File-level parallelism:
41//!
42//! ```no_run
43//! use fast_yaml_parallel::{FileProcessor, Config};
44//! use std::path::PathBuf;
45//!
46//! let processor = FileProcessor::new();
47//! let paths = vec![PathBuf::from("file1.yaml"), PathBuf::from("file2.yaml")];
48//! let result = processor.parse_files(&paths);
49//!
50//! println!("Processed {} files, {} successful, {} failed",
51//! result.total, result.success, result.failed);
52//! # Ok::<(), Box<dyn std::error::Error>>(())
53//! ```
54//!
55//! Custom configuration:
56//!
57//! ```
58//! use fast_yaml_parallel::{parse_parallel_with_config, Config};
59//!
60//! let config = Config::new()
61//! .with_workers(Some(8))
62//! .with_sequential_threshold(2048);
63//!
64//! let yaml = "---\nfoo: 1\n---\nbar: 2";
65//! let docs = parse_parallel_with_config(yaml, &config)?;
66//! # Ok::<(), Box<dyn std::error::Error>>(())
67//! ```
68
69#![warn(missing_docs)]
70
71mod chunker;
72mod config;
73mod error;
74mod processor;
75
76// New modules
77mod files;
78mod io;
79mod result;
80
81// Core public API
82pub use config::Config;
83pub use error::{Error, Result};
84pub use fast_yaml_core::Value;
85
86// File-level parallelism
87pub use files::FileProcessor;
88pub use io::{FileContent, SmartReader};
89pub use result::{BatchResult, FileOutcome, FileResult};
90
91/// Parse multi-document YAML stream in parallel.
92///
93/// Automatically detects document boundaries and distributes
94/// parsing across multiple threads. Falls back to sequential
95/// parsing for single-document inputs.
96///
97/// # Errors
98///
99/// Returns `Error::Parse` if any document fails to parse.
100/// The error includes the document index for debugging.
101///
102/// # Examples
103///
104/// ```
105/// use fast_yaml_parallel::parse_parallel;
106///
107/// let yaml = "---\nfoo: 1\n---\nbar: 2";
108/// let docs = parse_parallel(yaml)?;
109/// assert_eq!(docs.len(), 2);
110/// # Ok::<(), Box<dyn std::error::Error>>(())
111/// ```
112pub fn parse_parallel(input: &str) -> Result<Vec<Value>> {
113 let config = Config::default();
114 processor::process_parallel(input, &config)
115}
116
117/// Parse multi-document YAML with custom configuration.
118///
119/// Allows fine-tuning of parallelism parameters for specific
120/// workloads and hardware configurations.
121///
122/// # Errors
123///
124/// Returns `Error` if parsing or configuration fails.
125///
126/// # Examples
127///
128/// ```
129/// use fast_yaml_parallel::{parse_parallel_with_config, Config};
130///
131/// let config = Config::new()
132/// .with_workers(Some(4));
133///
134/// let yaml = "---\nfoo: 1\n---\nbar: 2";
135/// let docs = parse_parallel_with_config(yaml, &config)?;
136/// # Ok::<(), Box<dyn std::error::Error>>(())
137/// ```
138pub fn parse_parallel_with_config(input: &str, config: &Config) -> Result<Vec<Value>> {
139 processor::process_parallel(input, config)
140}
141
142/// Process multiple YAML files in parallel.
143///
144/// Convenience function for batch file processing with default config.
145///
146/// # Examples
147///
148/// ```no_run
149/// use fast_yaml_parallel::process_files;
150/// use std::path::PathBuf;
151///
152/// let paths = vec![PathBuf::from("file1.yaml"), PathBuf::from("file2.yaml")];
153/// let result = process_files(&paths);
154///
155/// println!("Processed {} files", result.total);
156/// # Ok::<(), Box<dyn std::error::Error>>(())
157/// ```
158pub fn process_files(paths: &[std::path::PathBuf]) -> BatchResult {
159 FileProcessor::new().parse_files(paths)
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_parse_parallel_basic() {
168 let yaml = "---\nfoo: 1\n---\nbar: 2";
169 let docs = parse_parallel(yaml).unwrap();
170 assert_eq!(docs.len(), 2);
171 }
172
173 #[test]
174 fn test_parse_parallel_single_document() {
175 let yaml = "foo: 1\nbar: 2";
176 let docs = parse_parallel(yaml).unwrap();
177 assert_eq!(docs.len(), 1);
178 }
179
180 #[test]
181 fn test_parse_parallel_error() {
182 let yaml = "---\nvalid: true\n---\ninvalid: [";
183 let result = parse_parallel(yaml);
184 assert!(result.is_err());
185 }
186
187 #[test]
188 fn test_parse_parallel_with_config() {
189 let config = Config::new().with_workers(Some(2));
190 let yaml = "---\nfoo: 1\n---\nbar: 2";
191 let docs = parse_parallel_with_config(yaml, &config).unwrap();
192 assert_eq!(docs.len(), 2);
193 }
194
195 #[test]
196 fn test_parse_parallel_empty_input() {
197 let yaml = "";
198 let docs = parse_parallel(yaml).unwrap();
199 assert_eq!(docs.len(), 0);
200 }
201
202 #[test]
203 fn test_parse_parallel_many_documents() {
204 use std::fmt::Write;
205 let mut yaml = String::new();
206 for i in 0..100 {
207 yaml.push_str("---\n");
208 let _ = writeln!(yaml, "id: {i}");
209 }
210
211 let docs = parse_parallel(&yaml).unwrap();
212 assert_eq!(docs.len(), 100);
213 }
214
215 #[test]
216 fn test_process_files_empty() {
217 let result = process_files(&[]);
218 assert_eq!(result.total, 0);
219 assert!(result.is_success());
220 }
221}