Skip to main content

ebi_bpmn/
importer.rs

1use crate::{
2    BusinessProcessModelAndNotation,
3    parser::{
4        parser::{can_eof, close_tag, empty_tag, is_in_namespace, open_tag},
5        parser_state::ParserState,
6    },
7};
8use anyhow::{Context, Error, Result};
9use quick_xml::{
10    NsReader,
11    events::{BytesStart, Event},
12};
13use std::{io::BufRead, str::FromStr};
14
15impl BusinessProcessModelAndNotation {
16    /// Attempts to import a BPMN model. If `disallow_sequence_flow_weights` is set to true, parsing will fail if any sequence flow has a weight.
17    pub fn import_from_reader(
18        reader: &mut dyn BufRead,
19        disallow_sequence_flow_weights: bool,
20    ) -> Result<Self>
21    where
22        Self: Sized,
23    {
24        let mut xml_reader = NsReader::from_reader(reader);
25        xml_reader.config_mut().trim_text(true);
26
27        let mut buf = vec![];
28        let mut state = ParserState::new();
29        loop {
30            buf.clear();
31            let (namespace, xml_event) = xml_reader
32                .read_resolved_event_into(&mut buf)
33                .with_context(|| "cannot read XML event")?;
34            let in_namespace = is_in_namespace(namespace);
35            match (in_namespace, xml_event) {
36                //start tag
37                (Some(n), Event::Start(e)) => {
38                    open_tag(&mut state, &e, n).with_context(|| {
39                        format!(
40                            "start tag `{}` at position {}",
41                            String::from_utf8_lossy(e.local_name().as_ref()),
42                            xml_reader.buffer_position()
43                        )
44                    })?;
45                }
46
47                //end of tag
48                (Some(n), Event::End(e)) => close_tag(&mut state, &e, n).with_context(|| {
49                    format!(
50                        "close tag `{}` at position {}",
51                        String::from_utf8_lossy(e.local_name().as_ref()),
52                        xml_reader.buffer_position()
53                    )
54                })?,
55
56                //empty tag
57                (Some(n), Event::Empty(e)) => empty_tag(&mut state, &e, n).with_context(|| {
58                    format!(
59                        "empty tag `{}` at position {}",
60                        String::from_utf8_lossy(e.local_name().as_ref()),
61                        xml_reader.buffer_position()
62                    )
63                })?,
64
65                //end of file: check whether we can finish
66                (_, Event::Eof) => {
67                    can_eof(&state).with_context(|| "unexpected end of file")?;
68                    return Ok(state.to_model(disallow_sequence_flow_weights)?);
69                }
70
71                _ => (),
72            }
73        }
74    }
75}
76
77impl FromStr for BusinessProcessModelAndNotation {
78    type Err = Error;
79
80    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
81        let mut reader = std::io::Cursor::new(s);
82        Self::import_from_reader(&mut reader, false)
83    }
84}
85
86pub(crate) fn parse_attribute(e: &BytesStart, attribute_name: &str) -> Option<String> {
87    if let Ok(Some(attribute)) = e.try_get_attribute(attribute_name) {
88        Some(
89            attribute
90                .decode_and_unescape_value(e.decoder())
91                .ok()?
92                .as_ref()
93                .to_owned(),
94        )
95    } else {
96        None
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use crate::{
103        BusinessProcessModelAndNotation,
104        stochastic_business_process_model_and_notation::StochasticBusinessProcessModelAndNotation,
105        traits::processable::Processable,
106    };
107    use std::fs::{self};
108
109    #[test]
110    fn bpmn_import() {
111        let fin = fs::read_to_string("testfiles/model.bpmn").unwrap();
112        let bpmn = fin.parse::<BusinessProcessModelAndNotation>().unwrap();
113
114        assert_eq!(bpmn.sequence_flows_non_recursive().len(), 0);
115        assert_eq!(bpmn.elements().len(), 10);
116    }
117
118    #[test]
119    fn sbpmn_import() {
120        let fin = fs::read_to_string("testfiles/model.sbpmn").unwrap();
121        let _bpmn = fin
122            .parse::<StochasticBusinessProcessModelAndNotation>()
123            .unwrap();
124    }
125
126    #[test]
127    #[should_panic]
128    fn sbpmn_import_fail() {
129        let fin = fs::read_to_string("testfiles/model-lanes.bpmn").unwrap();
130        let bpmn = fin
131            .parse::<StochasticBusinessProcessModelAndNotation>()
132            .unwrap();
133
134        dbg!(bpmn);
135    }
136
137    #[test]
138    #[should_panic]
139    fn bpmn_pool_invalid() {
140        let fin = fs::read_to_string("testfiles/invalid-pool.bpmn").unwrap();
141        let _bpmn = fin.parse::<BusinessProcessModelAndNotation>().unwrap();
142    }
143
144    #[test]
145    #[should_panic]
146    fn bpmn_message_invalid() {
147        let fin = fs::read_to_string("testfiles/invalid-message.bpmn").unwrap();
148        fin.parse::<BusinessProcessModelAndNotation>().unwrap();
149    }
150
151    #[test]
152    fn bpmn_lanes_import() {
153        let fin = fs::read_to_string("testfiles/model-lanes.bpmn").unwrap();
154        let bpmn = fin.parse::<BusinessProcessModelAndNotation>().unwrap();
155
156        assert_eq!(bpmn.elements.len(), 2);
157        assert_eq!(bpmn.message_flows.len(), 1);
158    }
159}