Skip to main content

bpmn_engine/model/
format.rs

1//! BPMN Format Detection and Unified Interface
2//!
3//! Format detection and unified parsing interface for BPMN 2.0 definitions.
4
5use crate::model::ProcessDefinition;
6use thiserror::Error;
7
8/// BPMN Format
9///
10/// Supported BPMN serialization formats.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum BpmnFormat {
13    /// JSON format
14    Json,
15    /// XML format (BPMN 2.0 standard)
16    Xml,
17}
18
19/// Format Detection Error
20#[derive(Debug, Error)]
21pub enum DetectionError {
22    #[error("Unable to detect format: input is empty or ambiguous")]
23    UnableToDetect,
24    #[error("Invalid format: {0}")]
25    InvalidFormat(String),
26}
27
28/// Format Detector
29///
30/// Detects the format of BPMN input data (JSON or XML).
31pub struct FormatDetector;
32
33impl FormatDetector {
34    /// Detect the format of input data
35    ///
36    /// # Arguments
37    /// * `input` - Input string (JSON or XML)
38    ///
39    /// # Returns
40    /// * `Ok(BpmnFormat)` - Detected format
41    /// * `Err(DetectionError)` - Detection error
42    pub fn detect(input: &str) -> Result<BpmnFormat, DetectionError> {
43        let trimmed = input.trim();
44        
45        if trimmed.is_empty() {
46            return Err(DetectionError::UnableToDetect);
47        }
48
49        // Check for XML indicators
50        if trimmed.starts_with("<?xml") || trimmed.starts_with("<bpmn2:") || trimmed.starts_with("<bpmn:") {
51            return Ok(BpmnFormat::Xml);
52        }
53
54        // Check for JSON indicators
55        if trimmed.starts_with('{') || trimmed.starts_with('[') {
56            return Ok(BpmnFormat::Json);
57        }
58
59        // Try to detect by looking for XML tags
60        if trimmed.contains('<') && trimmed.contains('>') {
61            // Check for common BPMN XML elements
62            if trimmed.contains("bpmn2:") || trimmed.contains("bpmn:") || trimmed.contains("definitions") {
63                return Ok(BpmnFormat::Xml);
64            }
65        }
66
67        // Default to JSON if it looks like JSON structure
68        if trimmed.starts_with('{') {
69            return Ok(BpmnFormat::Json);
70        }
71
72        Err(DetectionError::UnableToDetect)
73    }
74
75    /// Detect format with confidence score
76    ///
77    /// Returns the detected format along with a confidence score (0.0 to 1.0).
78    pub fn detect_with_confidence(input: &str) -> Result<(BpmnFormat, f64), DetectionError> {
79        let format = Self::detect(input)?;
80        
81        let confidence = match format {
82            BpmnFormat::Xml => {
83                if input.trim().starts_with("<?xml") {
84                    1.0
85                } else if input.contains("bpmn2:") || input.contains("bpmn:") {
86                    0.9
87                } else {
88                    0.7
89                }
90            }
91            BpmnFormat::Json => {
92                if input.trim().starts_with('{') && input.trim().ends_with('}') {
93                    0.95
94                } else {
95                    0.8
96                }
97            }
98        };
99
100        Ok((format, confidence))
101    }
102}
103
104/// Parse Error
105///
106/// Unified error type for parsing operations.
107#[derive(Debug, Error)]
108pub enum ParseError {
109    #[error("JSON parse error: {0}")]
110    Json(#[from] serde_json::Error),
111    #[error("XML parse error: {0}")]
112    Xml(String),
113    #[error("Format detection error: {0}")]
114    Detection(#[from] DetectionError),
115    #[error("Unsupported format")]
116    UnsupportedFormat,
117}
118
119/// Serialize Error
120///
121/// Unified error type for serialization operations.
122#[derive(Debug, Error)]
123pub enum SerializeError {
124    #[error("JSON serialize error: {0}")]
125    Json(#[from] serde_json::Error),
126    #[error("XML serialize error: {0}")]
127    Xml(String),
128    #[error("Unsupported format")]
129    UnsupportedFormat,
130}
131
132/// BPMN Parser Trait
133///
134/// Unified interface for parsing BPMN definitions from different formats.
135pub trait BpmnParser: Send + Sync {
136    /// Parse input string into ProcessDefinition
137    fn parse(&self, input: &str) -> Result<ProcessDefinition, ParseError>;
138    
139    /// Get the format this parser handles
140    fn format(&self) -> BpmnFormat;
141}
142
143/// JSON Parser
144///
145/// Parser for BPMN JSON format.
146pub struct JsonParser;
147
148impl BpmnParser for JsonParser {
149    fn parse(&self, input: &str) -> Result<ProcessDefinition, ParseError> {
150        ProcessDefinition::from_json(input).map_err(ParseError::Json)
151    }
152
153    fn format(&self) -> BpmnFormat {
154        BpmnFormat::Json
155    }
156}
157
158/// XML Parser
159///
160/// Parser for BPMN XML format.
161pub struct XmlParser;
162
163impl BpmnParser for XmlParser {
164    fn parse(&self, input: &str) -> Result<ProcessDefinition, ParseError> {
165        ProcessDefinition::from_xml(input)
166    }
167
168    fn format(&self) -> BpmnFormat {
169        BpmnFormat::Xml
170    }
171}
172
173/// Auto Parser
174///
175/// Parser that automatically detects format and parses accordingly.
176pub struct AutoParser;
177
178impl AutoParser {
179    /// Parse with automatic format detection
180    pub fn parse(input: &str) -> Result<(ProcessDefinition, BpmnFormat), ParseError> {
181        let format = FormatDetector::detect(input)?;
182        
183        let definition = match format {
184            BpmnFormat::Json => ProcessDefinition::from_json(input).map_err(ParseError::Json)?,
185            BpmnFormat::Xml => ProcessDefinition::from_xml(input)?,
186        };
187
188        Ok((definition, format))
189    }
190}
191