Skip to main content

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}