1use crate::model::elements::*;
9use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, Event};
10use quick_xml::Reader;
11use quick_xml::Writer;
12use std::collections::HashMap;
13use std::io::Cursor;
14
15pub mod namespaces {
17 pub const BPMN2: &str = "http://www.omg.org/spec/BPMN/20100524/MODEL";
18 pub const BPMNDI: &str = "http://www.omg.org/spec/BPMN/20100524/DI";
19 pub const DC: &str = "http://www.omg.org/spec/DD/20100524/DC";
20 pub const DI: &str = "http://www.omg.org/spec/DD/20100524/DI";
21}
22
23fn extract_attributes(e: &BytesStart) -> HashMap<String, String> {
25 let mut attrs = HashMap::new();
26 for attr in e.attributes() {
27 if let Ok(attr) = attr {
28 let key = String::from_utf8_lossy(attr.key.as_ref()).to_string();
29 let value = String::from_utf8_lossy(&attr.value).to_string();
30 attrs.insert(key, value);
31 }
32 }
33 attrs
34}
35
36fn matches_element_name(name: &[u8], patterns: &[&[u8]]) -> bool {
38 patterns.iter().any(|&pattern| name == pattern)
39}
40
41pub fn parse_bpmn_xml(xml: &str) -> Result<ProcessDefinition, crate::model::format::ParseError> {
50 let mut reader = Reader::from_str(xml);
51 reader.trim_text(true);
52
53 let mut buf = Vec::new();
54 let mut process_id: Option<String> = None;
55 let mut process_name: Option<String> = None;
56 let mut is_executable: bool = true;
57 let mut elements: HashMap<String, ProcessElement> = HashMap::new();
58 let mut flows: HashMap<String, SequenceFlow> = HashMap::new();
59 let mut variables: HashMap<String, Variable> = HashMap::new();
60
61 loop {
62 match reader.read_event_into(&mut buf) {
63 Ok(Event::Start(e)) | Ok(Event::Empty(e)) => {
64 let name = e.name();
65 let attrs = extract_attributes(&e);
66
67 if matches_element_name(name.as_ref(), &[b"bpmn2:process", b"bpmn:process", b"process"]) {
69 if let Some(id) = attrs.get("id") {
70 process_id = Some(id.clone());
71 }
72 if let Some(name_attr) = attrs.get("name") {
73 process_name = Some(name_attr.clone());
74 }
75 if let Some(exec) = attrs.get("isExecutable") {
76 is_executable = exec == "true";
77 }
78 }
79 else if matches_element_name(name.as_ref(), &[b"bpmn2:startEvent", b"bpmn:startEvent", b"startEvent"]) {
81 if let Some(id) = attrs.get("id") {
82 elements.insert(
83 id.clone(),
84 ProcessElement::StartEvent(StartEvent {
85 base: ElementBase {
86 id: id.clone(),
87 name: attrs.get("name").cloned(),
88 documentation: None,
89 },
90 event_definition: None,
91 }),
92 );
93 }
94 }
95 else if matches_element_name(name.as_ref(), &[b"bpmn2:endEvent", b"bpmn:endEvent", b"endEvent"]) {
97 if let Some(id) = attrs.get("id") {
98 elements.insert(
99 id.clone(),
100 ProcessElement::EndEvent(EndEvent {
101 base: ElementBase {
102 id: id.clone(),
103 name: attrs.get("name").cloned(),
104 documentation: None,
105 },
106 event_definition: None,
107 }),
108 );
109 }
110 }
111 else if matches_element_name(name.as_ref(), &[b"bpmn2:serviceTask", b"bpmn:serviceTask", b"serviceTask"]) {
113 if let Some(id) = attrs.get("id") {
114 elements.insert(
115 id.clone(),
116 ProcessElement::ServiceTask(ServiceTask {
117 base: ElementBase {
118 id: id.clone(),
119 name: attrs.get("name").cloned(),
120 documentation: None,
121 },
122 implementation: attrs.get("implementation").cloned(),
123 operation_ref: attrs.get("operationRef").cloned(),
124 io_mapping: Default::default(),
125 }),
126 );
127 }
128 }
129 else if matches_element_name(name.as_ref(), &[b"bpmn2:userTask", b"bpmn:userTask", b"userTask"]) {
131 if let Some(id) = attrs.get("id") {
132 elements.insert(
133 id.clone(),
134 ProcessElement::UserTask(UserTask {
135 base: ElementBase {
136 id: id.clone(),
137 name: attrs.get("name").cloned(),
138 documentation: None,
139 },
140 assignment: None,
141 form_key: attrs.get("formKey").cloned(),
142 }),
143 );
144 }
145 }
146 else if matches_element_name(name.as_ref(), &[b"bpmn2:scriptTask", b"bpmn:scriptTask", b"scriptTask"]) {
148 if let Some(id) = attrs.get("id") {
149 elements.insert(
150 id.clone(),
151 ProcessElement::ScriptTask(ScriptTask {
152 base: ElementBase {
153 id: id.clone(),
154 name: attrs.get("name").cloned(),
155 documentation: None,
156 },
157 script_format: attrs.get("scriptFormat").cloned(),
158 script: None,
159 }),
160 );
161 }
162 }
163 else if matches_element_name(name.as_ref(), &[b"bpmn2:manualTask", b"bpmn:manualTask", b"manualTask"]) {
165 if let Some(id) = attrs.get("id") {
166 elements.insert(
167 id.clone(),
168 ProcessElement::ManualTask(ManualTask {
169 base: ElementBase {
170 id: id.clone(),
171 name: attrs.get("name").cloned(),
172 documentation: None,
173 },
174 }),
175 );
176 }
177 }
178 else if matches_element_name(name.as_ref(), &[b"bpmn2:exclusiveGateway", b"bpmn:exclusiveGateway", b"exclusiveGateway"]) {
180 if let Some(id) = attrs.get("id") {
181 elements.insert(
182 id.clone(),
183 ProcessElement::ExclusiveGateway(ExclusiveGateway {
184 base: ElementBase {
185 id: id.clone(),
186 name: attrs.get("name").cloned(),
187 documentation: None,
188 },
189 default_flow: attrs.get("default").cloned(),
190 }),
191 );
192 }
193 }
194 else if matches_element_name(name.as_ref(), &[b"bpmn2:parallelGateway", b"bpmn:parallelGateway", b"parallelGateway"]) {
196 if let Some(id) = attrs.get("id") {
197 elements.insert(
198 id.clone(),
199 ProcessElement::ParallelGateway(ParallelGateway {
200 base: ElementBase {
201 id: id.clone(),
202 name: attrs.get("name").cloned(),
203 documentation: None,
204 },
205 }),
206 );
207 }
208 }
209 else if matches_element_name(name.as_ref(), &[b"bpmn2:inclusiveGateway", b"bpmn:inclusiveGateway", b"inclusiveGateway"]) {
211 if let Some(id) = attrs.get("id") {
212 elements.insert(
213 id.clone(),
214 ProcessElement::InclusiveGateway(InclusiveGateway {
215 base: ElementBase {
216 id: id.clone(),
217 name: attrs.get("name").cloned(),
218 documentation: None,
219 },
220 default_flow: attrs.get("default").cloned(),
221 }),
222 );
223 }
224 }
225 else if matches_element_name(name.as_ref(), &[b"bpmn2:sequenceFlow", b"bpmn:sequenceFlow", b"sequenceFlow"]) {
227 if let (Some(id), Some(source), Some(target)) = (
228 attrs.get("id"),
229 attrs.get("sourceRef"),
230 attrs.get("targetRef"),
231 ) {
232 flows.insert(
233 id.clone(),
234 SequenceFlow {
235 id: id.clone(),
236 name: attrs.get("name").cloned(),
237 source_ref: source.clone(),
238 target_ref: target.clone(),
239 condition_expression: None,
240 },
241 );
242 }
243 }
244 }
245 Ok(Event::Eof) => break,
246 Err(e) => {
247 return Err(crate::model::format::ParseError::Xml(format!(
248 "XML parse error: {}",
249 e
250 )));
251 }
252 _ => {}
253 }
254 buf.clear();
255 }
256
257 let process_id = process_id.ok_or_else(|| {
258 crate::model::format::ParseError::Xml("Process ID not found".to_string())
259 })?;
260
261 Ok(ProcessDefinition {
262 id: process_id,
263 name: process_name,
264 process_type: "process".to_string(),
265 is_executable,
266 elements,
267 flows,
268 variables,
269 })
270}
271
272pub fn serialize_bpmn_xml(definition: &ProcessDefinition) -> Result<String, crate::model::format::SerializeError> {
281 let mut writer = Writer::new(Cursor::new(Vec::new()));
282
283 let decl = BytesDecl::new("1.0", Some("UTF-8"), None);
285 writer.write_event(Event::Decl(decl))
286 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
287
288 let mut definitions = BytesStart::new("bpmn2:definitions");
290 definitions.push_attribute(("xmlns:bpmn2", namespaces::BPMN2));
291 definitions.push_attribute(("xmlns:bpmndi", namespaces::BPMNDI));
292 definitions.push_attribute(("xmlns:dc", namespaces::DC));
293 definitions.push_attribute(("xmlns:di", namespaces::DI));
294 definitions.push_attribute(("id", "Definitions"));
295 definitions.push_attribute(("targetNamespace", "http://bpmn.io/schema/bpmn"));
296
297 writer.write_event(Event::Start(definitions))
298 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
299
300 let mut process = BytesStart::new("bpmn2:process");
302 process.push_attribute(("id", definition.id.as_str()));
303 if let Some(name) = &definition.name {
304 process.push_attribute(("name", name.as_str()));
305 }
306 process.push_attribute(("isExecutable", if definition.is_executable { "true" } else { "false" }));
307
308 writer.write_event(Event::Start(process))
309 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
310
311 for element in definition.elements.values() {
313 match element {
314 ProcessElement::StartEvent(e) => {
315 let mut start = BytesStart::new("bpmn2:startEvent");
316 start.push_attribute(("id", e.base.id.as_str()));
317 if let Some(name) = &e.base.name {
318 start.push_attribute(("name", name.as_str()));
319 }
320 writer.write_event(Event::Empty(start))
321 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
322 }
323 ProcessElement::EndEvent(e) => {
324 let mut end = BytesStart::new("bpmn2:endEvent");
325 end.push_attribute(("id", e.base.id.as_str()));
326 if let Some(name) = &e.base.name {
327 end.push_attribute(("name", name.as_str()));
328 }
329 writer.write_event(Event::Empty(end))
330 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
331 }
332 ProcessElement::ServiceTask(e) => {
333 let mut task = BytesStart::new("bpmn2:serviceTask");
334 task.push_attribute(("id", e.base.id.as_str()));
335 if let Some(name) = &e.base.name {
336 task.push_attribute(("name", name.as_str()));
337 }
338 writer.write_event(Event::Empty(task))
339 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
340 }
341 ProcessElement::UserTask(e) => {
342 let mut task = BytesStart::new("bpmn2:userTask");
343 task.push_attribute(("id", e.base.id.as_str()));
344 if let Some(name) = &e.base.name {
345 task.push_attribute(("name", name.as_str()));
346 }
347 writer.write_event(Event::Empty(task))
348 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
349 }
350 ProcessElement::ExclusiveGateway(e) => {
351 let mut gateway = BytesStart::new("bpmn2:exclusiveGateway");
352 gateway.push_attribute(("id", e.base.id.as_str()));
353 if let Some(name) = &e.base.name {
354 gateway.push_attribute(("name", name.as_str()));
355 }
356 writer.write_event(Event::Empty(gateway))
357 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
358 }
359 ProcessElement::ParallelGateway(e) => {
360 let mut gateway = BytesStart::new("bpmn2:parallelGateway");
361 gateway.push_attribute(("id", e.base.id.as_str()));
362 if let Some(name) = &e.base.name {
363 gateway.push_attribute(("name", name.as_str()));
364 }
365 writer.write_event(Event::Empty(gateway))
366 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
367 }
368 _ => {
369 }
371 }
372 }
373
374 for flow in definition.flows.values() {
376 let mut seq_flow = BytesStart::new("bpmn2:sequenceFlow");
377 seq_flow.push_attribute(("id", flow.id.as_str()));
378 seq_flow.push_attribute(("sourceRef", flow.source_ref.as_str()));
379 seq_flow.push_attribute(("targetRef", flow.target_ref.as_str()));
380 writer.write_event(Event::Empty(seq_flow))
381 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
382 }
383
384 writer.write_event(Event::End(BytesEnd::new("bpmn2:process")))
386 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
387
388 writer.write_event(Event::End(BytesEnd::new("bpmn2:definitions")))
390 .map_err(|e| crate::model::format::SerializeError::Xml(format!("{}", e)))?;
391
392 let result = writer.into_inner().into_inner();
393 String::from_utf8(result).map_err(|e| {
394 crate::model::format::SerializeError::Xml(format!("UTF-8 error: {}", e))
395 })
396}