1use std::collections::HashMap;
2use std::str;
3
4use mathml_rs::Apply;
5use mathml_rs::Ci;
6use mathml_rs::Op;
7use mathml_rs::OpNode;
8use quick_xml::events::Event;
9use quick_xml::Reader;
10use sbml_macros::{attach, attach_math, close};
11
12pub mod structs;
13pub use structs::compartments::*;
14pub use structs::function_definitions::*;
15pub use structs::initial_assignments::*;
16pub use structs::math::*;
17pub use structs::model::*;
18pub use structs::parameters::*;
19pub use structs::reactions::*;
20pub use structs::root::*;
21pub use structs::rules::*;
22pub use structs::species::*;
23pub use structs::tag::*;
24pub use structs::units::*;
25
26#[allow(unused_variables, unused_assignments, dead_code)]
27pub fn parse(filename: &str) -> Result<Model, Vec<String>> {
28 let mut reader = Reader::from_file(filename).expect("File error.");
31 reader.trim_text(true);
32 reader.expand_empty_elements(true);
33 let mut buf = Vec::new();
34
35 let mut stack: Vec<TagIndex> = Vec::new();
36 let mut nodes = Vec::new();
37 let mut nodes_len = 0;
38 let mut model_attrs = HashMap::new();
39
40 let root = Root::default();
41 nodes.push(Tag::Root(root));
42 nodes_len += 1;
43 let mut current = 0;
44 stack.push(current);
45
46 loop {
47 match reader.read_event(&mut buf) {
48 Ok(Event::Start(ref e)) => {
50 let mut new_tag = None;
51 match e.name() {
52 b"sbml" => {}
53 b"model" => {
54 let attributes = e.attributes().map(|a| a.unwrap()).collect::<Vec<_>>();
55 for attribute in attributes {
56 let key = str::from_utf8(attribute.key).unwrap();
57 let value = attribute.unescape_and_decode_value(&reader).unwrap();
58 match key {
59 "id" | "substanceUnits" | "timeUnits" | "extentUnits"
60 | "volumeUnits" | "areaUnits" | "lengthUnits"
61 | "conversionFactor" | "metaid" | "name" => {
62 model_attrs.insert(key.to_string(), value);
63 }
64 _ => panic!("Attribute {} not parsed for model.", key),
65 }
66 }
67 }
68 b"listOfUnitDefinitions" => attach!(ListOfUnitDefinitions to Root),
69 b"unitDefinition" => attach!(UnitDefinition with
70 id as String
71 to ListOfUnitDefinitions),
72 b"listOfUnits" => attach!(ListOfUnits to UnitDefinition),
73 b"unit" => attach!(Unit with
74 kind as String,
75 exponent as f64,
76 scale as i64,
77 multiplier as f64
78 to ListOfUnits),
79 b"listOfCompartments" => attach!(ListOfCompartments to Root),
80 b"compartment" => attach!(Compartment with
81 name as String,
82 id as String,
83 units as String,
84 constant as bool,
85 spatial_dimensions as f64,
86 sbo_term as String,
87 size as f64
88 to ListOfCompartments),
89 b"listOfParameters" => attach!(ListOfParameters to Root),
90 b"parameter" => attach!(Parameter with
91 id as String,
92 name as String,
93 value as f64,
94 units as String,
95 sbo_term as String,
96 constant as bool
97 to ListOfParameters),
98 b"listOfSpecies" => attach!(ListOfSpecies to Root),
99 b"species" => attach!(Species with
100 id as String,
101 name as String,
102 meta_id as String,
103 sbo_term as String,
104 compartment as String,
105 initial_concentration as f64,
106 initial_amount as f64,
107 substance_units as String,
108 has_only_substance_units as bool,
109 boundary_condition as bool,
110 constant as bool,
111 conversion_factor as String,
112 to ListOfSpecies),
113 b"listOfReactions" => attach!(ListOfReactions to Root),
114 b"reaction" => attach!(Reaction with
115 id as String,
116 reversible as bool,
117 compartment as String,
118 name as String,
119 sbo_term as String
120 to ListOfReactions),
121 b"listOfReactants" => attach!(ListOfReactants to Reaction),
122 b"listOfProducts" => attach!(ListOfProducts to Reaction),
123 b"speciesReference" => attach!(SpeciesReference with
124 id as String,
125 name as String,
126 species as String,
127 constant as bool,
128 sbo_term as String,
129 stoichiometry as f64,
130 to ListOfReactants | ListOfProducts),
131 b"listOfModifiers" => attach!(ListOfModifiers to Reaction),
132 b"modifierSpeciesReference" => attach!(ModifierSpeciesReference with
133 id as String,
134 name as String,
135 species as String,
136 sbo_term as String,
137 to ListOfModifiers),
138 b"kineticLaw" => attach!(KineticLaw with
139 sbo_term as String,
140 to Reaction),
141 b"listOfLocalParameters" => attach!(ListOfLocalParameters to KineticLaw),
142 b"localParameter" => attach!(LocalParameter with
143 id as String,
144 value as f64,
145 units as String,
146 sbo_term as String,
147 to ListOfLocalParameters),
148 b"math" => {
149 let (math_nodes, returned_reader) = mathml_rs::parse_fragment(reader);
150 reader = returned_reader;
151
152 attach_math![
153 KineticLaw,
154 FunctionDefinition,
155 InitialAssignment,
156 AssignmentRule,
157 RateRule,
158 ];
159 }
160 b"listOfFunctionDefinitions" => attach!(ListOfFunctionDefinitions to Root),
161 b"functionDefinition" => {
162 attach!(FunctionDefinition with
163 id as String,
164 name as String,
165 sbo_term as String
166 to ListOfFunctionDefinitions)
167 }
168 b"listOfInitialAssignments" => attach!(ListOfInitialAssignments to Root),
169 b"initialAssignment" => {
170 attach!(InitialAssignment with
171 id as String,
172 symbol as String,
173 sbo_term as String
174 to ListOfInitialAssignments)
175 }
176 b"listOfRules" => attach!(ListOfRules to Root),
177 b"assignmentRule" => {
178 attach!(AssignmentRule with
179 id as String,
180 metaid as String,
181 variable as String,
182 sbo_term as String
183 to ListOfRules)
184 }
185 b"rateRule" => {
186 attach!(RateRule with
187 id as String,
188 metaid as String,
189 variable as String,
190 sbo_term as String
191 to ListOfRules)
192 }
193 _ => {
194 panic!("Tag not parsed: {}", str::from_utf8(e.name()).unwrap());
195 }
196 }
197 if let Some(t) = new_tag {
198 nodes.push(t);
199 nodes_len += 1;
200 }
201 }
202 Ok(Event::End(ref e)) => match e.name() {
204 b"listOfUnitDefinitions" => close![ListOfUnitDefinitions],
205 b"unitDefinition" => close![UnitDefinition],
206 b"listOfUnits" => close![ListOfUnits],
207 b"unit" => close![Unit],
208 b"listOfCompartments" => close![ListOfCompartments],
209 b"compartment" => close![Compartment],
210 b"listOfParameters" => close![ListOfParameters],
211 b"parameter" => close![Parameter],
212 b"listOfSpecies" => close![ListOfSpecies],
213 b"species" => close![Species],
214 b"listOfReactions" => close![ListOfReactions],
215 b"reaction" => close![Reaction],
216 b"listOfReactants" => close![ListOfReactants],
217 b"listOfProducts" => close![ListOfProducts],
218 b"speciesReference" => close![SpeciesReference],
219 b"listOfModifiers" => close![ListOfModifiers],
220 b"modifierSpeciesReference" => close![ModifierSpeciesReference],
221 b"kineticLaw" => close![KineticLaw],
222 b"listOfLocalParameters" => close![ListOfLocalParameters],
223 b"localParameter" => close![LocalParameter],
224 b"math" => close![MathTag],
225 b"listOfFunctionDefinitions" => close![ListOfFunctionDefinitions],
226 b"functionDefinition" => close![FunctionDefinition],
227 b"listOfInitialAssignments" => close![ListOfInitialAssignments],
228 b"initialAssignment" => close![InitialAssignment],
229 b"listOfRules" => close![ListOfRules],
230 b"assignmentRule" => close![AssignmentRule],
231 b"rateRule" => close![RateRule],
232 _ => {}
233 },
234 Ok(Event::Text(e)) => {
236 let s = e.unescape_and_decode(&reader).unwrap();
237 panic!("Unknown text found in {:?}", nodes[current]);
238 }
239 Ok(Event::Eof) => break, Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
241 _ => (), }
243 }
244
245 let model = Model::new(nodes, model_attrs);
246
247 Ok(model)
248}
249
250pub fn parse_with_converted_species(filename: &str) -> Result<Model, Vec<String>> {
251 let mut model = parse(filename)?;
252
253 let species = model.species();
254 let mut species_compartment_id = HashMap::<String, String>::new();
255 for sp in species {
256 if let Some(species_id) = sp.id {
257 if Some(false) == sp.has_only_substance_units {
258 if let Some(compartment_id) = sp.compartment {
259 species_compartment_id.insert(species_id, compartment_id);
260 }
261 }
262 }
263 }
264 let mut new_nodes = model.nodes.clone();
271
272 for i in 0..model.nodes.len() {
273 match &model.nodes[i] {
274 Tag::MathTag(math_tag) => {
276 for j in 0..math_tag.nodes.len() {
277 match &math_tag.nodes[j] {
278 MathNode::Ci(ci) => {
281 if let Some(species_id) = &ci.name {
282 if let Some(compartment) = species_compartment_id.get(species_id) {
284 if let Tag::MathTag(math_tag_copy) = &mut new_nodes[i] {
286 let mut species_math_node = math_tag_copy.nodes[j].clone();
290 let mut apply = Apply::default();
291 let mut divide = OpNode::default();
292 divide.op = Some(Op::Divide);
293 let mut compartment = Ci::with_name(compartment.clone());
294
295 let length = math_tag_copy.nodes.len();
297 apply.parent = ci.parent;
298 apply.children = vec![length, length + 1, length + 2];
299 apply.operator = Some(length);
300 apply.operands = vec![length + 1, length + 2];
301 divide.parent = Some(j);
302 compartment.parent = Some(j);
303 if let MathNode::Ci(species) = &mut species_math_node {
304 species.parent = Some(j);
305 }
306
307 let apply_math_node = MathNode::Apply(apply);
308 let divide_math_node = MathNode::Op(divide);
309 let compartment_math_node = MathNode::Ci(compartment);
310 math_tag_copy.nodes[j] = apply_math_node;
311 math_tag_copy.nodes.push(divide_math_node);
312 math_tag_copy.nodes.push(species_math_node);
313 math_tag_copy.nodes.push(compartment_math_node);
314 }
315 }
316 }
317 }
318 _ => {}
319 }
320 }
321 }
322 _ => {}
323 }
324 }
325
326 model.nodes = new_nodes;
327
328 Ok(model)
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 #[test]
341 fn it_works() {
342 for n in 1..2 {
343 let filename = format!(
344 "../../testsuites/core-semantic/{:0>5}/{:0>5}-sbml-l3v2.xml",
345 n, n
346 );
347 println!("{}", filename);
348 let result = parse_with_converted_species(&filename);
349 match result {
350 Ok(model) => {
351 let function_definitions = model.function_definitions();
352 for function_definition in function_definitions {
353 println!("{}", function_definition.math_tag(&model).unwrap());
354 }
355 }
356 Err(errors) => {
357 println!("{:?}", errors);
358 }
359 }
360 }
361 }
362}