copybook_core/lib.rs
1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! Core parsing and schema types for COBOL copybooks
3//!
4//! This crate provides the fundamental types and parsing logic for COBOL copybook
5//! processing, including AST construction, layout resolution, and schema validation.
6//!
7
8#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
9#![cfg_attr(
10 test,
11 allow(
12 clippy::expect_used,
13 clippy::unwrap_used,
14 clippy::panic,
15 clippy::duplicated_attributes
16 )
17)]
18// Allow missing inline for public methods in this library - too many methods to inline individually
19#![allow(clippy::missing_inline_in_public_items)]
20#![allow(clippy::missing_errors_doc)]
21#![allow(clippy::missing_panics_doc)]
22#![allow(clippy::format_push_string)]
23#![allow(clippy::uninlined_format_args)]
24#![allow(clippy::match_same_arms)]
25#![allow(clippy::module_name_repetitions)]
26#![allow(clippy::too_many_lines)]
27#![allow(clippy::must_use_candidate)]
28#![allow(clippy::collapsible_else_if)]
29#![allow(clippy::unused_self)]
30#![allow(clippy::doc_markdown)]
31#![allow(clippy::unnecessary_wraps)]
32#![allow(clippy::cast_possible_wrap)]
33#![allow(clippy::bool_to_int_with_if)]
34#![allow(clippy::cast_lossless)]
35#![allow(clippy::cast_sign_loss)]
36#![allow(clippy::assigning_clones)]
37#![allow(clippy::cast_possible_truncation)]
38#![allow(clippy::manual_midpoint)]
39#![allow(clippy::redundant_closure_for_method_calls)]
40#![allow(clippy::single_match_else)]
41#![allow(clippy::ignored_unit_patterns)]
42
43//! ## Key Features
44//!
45//! ### Enhanced COBOL Support
46//! - **Level-88 Condition Values**: Full support for condition name definitions with VALUE clauses
47//! - **Binary Width Syntax**: Support for explicit `BINARY(n)` width specifications
48//! - **Comprehensive Numeric Types**: Full parsing of zoned, packed, and binary field definitions
49//! - **Recursion Limits**: Parser protection against deeply nested or malformed copybooks
50//! - **Error Context**: Detailed error reporting with field paths and source locations
51//!
52//! ### Schema Generation
53//! - **Field Hierarchy**: Complete representation of COBOL data structures with Level-88 conditions
54//! - **Layout Resolution**: Accurate byte offset and size calculations
55//! - **Structural Validation**: Comprehensive checks for ODO positioning, Level-88 placement, REDEFINES compatibility
56//!
57//! ### Parser Improvements
58//! - **Robustness**: Enhanced handling of edge cases and malformed input
59//! - **Performance**: Efficient parsing with minimal memory allocation
60//! - **Standards Compliance**: Adherence to COBOL copybook syntax standards
61//! - **Safety**: Zero unsafe code with comprehensive validation
62//!
63//! ## Usage Example
64//!
65//! ```rust
66//! use copybook_core::{parse_copybook, parse_copybook_with_options, ParseOptions, FieldKind};
67//!
68//! // Parse a copybook with Level-88 condition values
69//! let copybook_text = r#"
70//! 01 CUSTOMER-RECORD.
71//! 05 CUSTOMER-STATUS PIC X(1).
72//! 88 STATUS-ACTIVE VALUE 'A'.
73//! 88 STATUS-INACTIVE VALUE 'I'.
74//! 88 STATUS-PENDING VALUE 'P'.
75//! 05 CUSTOMER-ID PIC 9(6).
76//! 05 ORDER-COUNT PIC 9(3).
77//! 05 ORDERS OCCURS 1 TO 100 TIMES
78//! DEPENDING ON ORDER-COUNT.
79//! 10 ORDER-ID PIC 9(8).
80//! 88 RUSH-ORDER VALUE 99999999.
81//! "#;
82//!
83//! // Parse with default options and handle any errors gracefully
84//! let schema_default = match parse_copybook(copybook_text) {
85//! Ok(schema) => schema,
86//! Err(error) => {
87//! eprintln!("Failed to parse copybook: {error}");
88//! return;
89//! }
90//! };
91//! println!(
92//! "Default parsing produced {} top-level fields",
93//! schema_default.fields.len()
94//! );
95//!
96//! // Parse with custom options for enhanced validation
97//! let options = ParseOptions {
98//! allow_inline_comments: true,
99//! strict: true,
100//! ..Default::default()
101//! };
102//! let schema_strict = match parse_copybook_with_options(copybook_text, &options) {
103//! Ok(schema) => schema,
104//! Err(error) => {
105//! eprintln!("Strict parsing failed: {error}");
106//! return;
107//! }
108//! };
109//!
110//! // Examine the parsed schema including Level-88 conditions
111//! for field in &schema_strict.fields {
112//! match &field.kind {
113//! FieldKind::Condition { values } => {
114//! println!("Level-88 condition '{}' with values: {:?}", field.name, values);
115//! }
116//! FieldKind::Group => {
117//! println!("Group field: {}", field.name);
118//! }
119//! _ => {
120//! println!(
121//! "Data field: {} (offset: {}, size: {})",
122//! field.name, field.offset, field.len
123//! );
124//! }
125//! }
126//! }
127//! ```
128
129#[cfg(feature = "audit")]
130pub mod audit;
131/// Dialect contract for ODO `min_count` semantics (Normative, ZeroTolerant, OneTolerant).
132pub mod dialect;
133/// Re-export of all error types from [`copybook_error`].
134pub mod error {
135 pub use copybook_error::*;
136}
137/// Structured error reporting with severity levels and summary statistics.
138pub mod error_reporter;
139/// Compile-time and runtime feature flag governance.
140pub mod feature_flags;
141/// Layout resolution: byte offsets, REDEFINES, and OCCURS DEPENDING ON.
142pub mod layout;
143/// Lexical analysis of COBOL copybook source text.
144pub mod lexer;
145/// Recursive-descent parser producing the [`Schema`] AST from tokens.
146pub mod parser;
147/// PICTURE clause analysis and field-size computation.
148pub mod pic;
149/// Field projection for selective decode/encode (`--select`).
150pub mod projection;
151/// Core schema types: [`Schema`], [`Field`], [`FieldKind`], and related structures.
152pub mod schema;
153/// COBOL feature support matrix and status registry.
154pub mod support_matrix;
155/// Shared utility functions and extension traits.
156pub mod utils;
157
158pub use copybook_utils::{OptionExt, SliceExt, VecExt, safe_ops};
159pub use dialect::Dialect;
160pub use error::{Error, ErrorCode, ErrorContext, Result};
161pub use error_reporter::{ErrorMode, ErrorReport, ErrorReporter, ErrorSeverity, ErrorSummary};
162pub use feature_flags::{Feature, FeatureCategory, FeatureFlags, FeatureFlagsHandle, all_features};
163pub use parser::ParseOptions;
164pub use projection::project_schema;
165pub use schema::{Field, FieldKind, Occurs, Schema, SignPlacement, SignSeparateInfo, TailODO};
166
167#[cfg(feature = "audit")]
168pub use audit::*;
169
170// Performance-optimized audit stubs when audit feature is disabled
171#[cfg(not(feature = "audit"))]
172pub mod audit {
173 //! No-op audit stubs for performance-critical builds
174
175 use serde::{Deserialize, Serialize};
176
177 /// Lightweight audit context stub (zero-cost when audit disabled)
178 #[derive(Debug, Clone, Default, Serialize, Deserialize)]
179 pub struct AuditContext {
180 // Zero-sized for maximum performance when audit is disabled
181 }
182
183 impl AuditContext {
184 /// Create a new audit context (no-op stub).
185 #[inline]
186 pub fn new() -> Self {
187 Self::default()
188 }
189 /// Create a lightweight audit context (no-op stub).
190 #[inline]
191 pub fn new_lightweight() -> Self {
192 Self::default()
193 }
194 /// Set the operation ID (no-op stub).
195 #[inline]
196 #[must_use]
197 pub fn with_operation_id(self, _id: impl Into<String>) -> Self {
198 self // No-op for zero-cost optimization
199 }
200 /// Set the user identity (no-op stub).
201 #[inline]
202 #[must_use]
203 pub fn with_user(self, _user: impl Into<String>) -> Self {
204 self
205 }
206 /// Set the security classification (no-op stub).
207 #[inline]
208 #[must_use]
209 pub fn with_security_classification(self, _classification: SecurityClassification) -> Self {
210 self
211 }
212 /// Set the compliance profile (no-op stub).
213 #[inline]
214 #[must_use]
215 pub fn with_compliance_profile(self, _profile: ComplianceProfile) -> Self {
216 self
217 }
218 /// Add a key-value metadata pair (no-op stub).
219 #[inline]
220 #[must_use]
221 pub fn with_metadata(self, _key: impl Into<String>, _value: impl Into<String>) -> Self {
222 self
223 }
224 /// Create a child context for nested operations (no-op stub).
225 #[inline]
226 #[must_use]
227 pub fn create_lightweight_child_context(&self, _id: impl Into<String>) -> Self {
228 // Return clone for no-op performance - avoid any allocations
229 self.clone()
230 }
231 }
232
233 /// Stub compliance profile enum (no-op when audit is disabled).
234 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
235 pub enum ComplianceProfile {
236 /// Sarbanes-Oxley Act.
237 SOX,
238 /// Health Insurance Portability and Accountability Act.
239 HIPAA,
240 /// General Data Protection Regulation.
241 GDPR,
242 /// Payment Card Industry Data Security Standard.
243 PCIQDSS,
244 }
245
246 /// Stub security classification enum (no-op when audit is disabled).
247 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
248 pub enum SecurityClassification {
249 /// Publicly accessible data.
250 Public,
251 /// Internal-only data.
252 Internal,
253 /// Confidential business data.
254 Confidential,
255 /// Material transaction data (SOX-relevant).
256 MaterialTransaction,
257 /// Protected Health Information (HIPAA-relevant).
258 PHI,
259 }
260
261 pub mod context {
262 pub use super::*;
263 }
264}
265
266/// Parse a COBOL copybook into a structured schema
267///
268/// # Examples
269///
270/// ```
271/// use copybook_core::parse_copybook;
272///
273/// let schema = parse_copybook("01 REC.\n 05 FLD PIC X(10).").unwrap();
274/// assert_eq!(schema.fields.len(), 1);
275/// assert_eq!(schema.fields[0].name, "REC");
276/// assert_eq!(schema.lrecl_fixed, Some(10));
277/// ```
278///
279/// # Errors
280/// Returns an error if the copybook contains syntax errors or unsupported features.
281#[inline]
282#[must_use = "Handle the Result or propagate the error"]
283pub fn parse_copybook(text: &str) -> Result<Schema> {
284 parser::parse(text)
285}
286
287/// Parse a COBOL copybook with specific options
288///
289/// # Examples
290///
291/// ```
292/// use copybook_core::{parse_copybook_with_options, ParseOptions, Dialect};
293///
294/// let options = ParseOptions {
295/// dialect: Dialect::ZeroTolerant,
296/// ..ParseOptions::default()
297/// };
298/// let schema = parse_copybook_with_options(
299/// "01 REC.\n 05 CNT PIC 9.\n 05 ARR OCCURS 0 TO 5 DEPENDING ON CNT PIC X(3).",
300/// &options,
301/// ).unwrap();
302/// assert_eq!(schema.lrecl_fixed, Some(16)); // 1 + 5*3
303/// ```
304///
305/// # Errors
306/// Returns an error if the copybook contains syntax errors or unsupported features.
307#[inline]
308#[must_use = "Handle the Result or propagate the error"]
309pub fn parse_copybook_with_options(text: &str, options: &ParseOptions) -> Result<Schema> {
310 parser::parse_with_options(text, options)
311}