openmath/lib.rs
1#![allow(unexpected_cfgs)]
2#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
3#![allow(clippy::doc_markdown)]
4#![doc = include_str!("../README.md")]
5/*! ## Features */
6#![cfg_attr(doc,doc = document_features::document_features!())]
7pub mod ser;
8
9use std::{borrow::Cow, convert::Infallible};
10
11pub use ser::OMSerializable;
12pub mod de;
13pub use de::{OM, OMDeserializable};
14pub mod base64;
15mod int;
16/// reexported for convenience
17pub use either;
18pub use int::Int;
19
20use crate::ser::AsOMS;
21
22/// The base URI of official OᴘᴇɴMᴀᴛʜ dictionaries (`http://www.openmath.org/cd`)
23pub const CD_BASE: &str = "http://www.openmath.org/cd";
24
25/// XML namespace for OpenMath elements
26pub const XML_NS: &str = "http://www.openmath.org/OpenMath";
27
28macro_rules! omkinds {
29 ($( $(#[$meta:meta])* $id:ident = $v:literal ),* $(,)?) => {
30 /// All <span style="font-variant:small-caps;">OpenMath</span> tags/kinds
31 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32 #[repr(u8)]
33 #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
34 pub enum OMKind {
35 $(
36 $(#[$meta])*
37 $id = $v
38 ),*
39 }
40 impl OMKind {
41 /// as static string
42 #[must_use]
43 pub const fn as_str(self) -> &'static str {
44 match self {$(
45 Self::$id => stringify!($id)
46 ),*}
47 }
48 /// convert from a byte
49 #[must_use]
50 pub const fn from_u8(u:u8) -> Option<Self> {
51 match u {
52 $( $v => Some(Self::$id) ),*,
53 _ => None
54 }
55 }
56 }
57 impl std::fmt::Display for OMKind {
58 #[inline]
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.write_str(self.as_str())
61 }
62 }
63 };
64}
65
66omkinds! {
67 /** <div class="openmath">
68 Integers in the mathematical sense, with no predefined range.
69 They are “infinite precision” integers (also called “bignums” in computer algebra).
70 </div> */
71 OMI = 0,
72
73 /** <div class="openmath">
74 Double precision floating-point numbers following the IEEE 754-1985 standard.
75 </div> */
76 OMF = 1,
77
78 /** <div class="openmath">
79 A Unicode Character string. This also corresponds to “characters” in XML.
80 </div> */
81 OMSTR = 2,
82
83 /** <div class="openmath">
84 A sequence of bytes.
85 </div> */
86 OMB = 3,
87
88 ///<div class="openmath">
89 ///
90 /// A Variable must have a name which is a sequence of characters matching a regular
91 /// expression, as described in [Section 2.3](https://openmath.org/standard/om20-2019-07-01/omstd20.html#sec_names).
92 ///
93 ///</div>
94 ///
95 ///(Note: We do not enforce that names are valid XML names;)
96 OMV = 4,
97
98 ///<div class="openmath">
99 ///
100 /// A Symbol encodes three fields of information, a symbol name, a Content Dictionary name,
101 /// and (optionally) a Content Dictionary base URI, The name of a symbol is a sequence of
102 /// characters matching the regular expression described in
103 /// [Section 2.3](https://openmath.org/standard/om20-2019-07-01/omstd20.html#sec_names).
104 /// The Content Dictionary is the location of the definition of the symbol, consisting of a
105 /// name (a sequence of characters matching the regular expression described in
106 /// [Section 2.3](https://openmath.org/standard/om20-2019-07-01/omstd20.html#sec_names))
107 /// and, optionally, a unique prefix called a cdbase which is used to disambiguate multiple
108 /// Content Dictionaries of the same name. There are other properties of the symbol that are
109 /// not explicit in these fields but whose values may be obtained by inspecting the Content
110 /// Dictionary specified. These include the symbol definition, formal properties and examples
111 /// and, optionally, a role which is a restriction on where the symbol may appear in an
112 /// <span style="font-variant:small-caps;">OpenMath</span> object. The possible roles are described in
113 /// [Section 2.1.4](https://openmath.org/standard/om20-2019-07-01/omstd20.html#sec_roles).
114 ///
115 ///</div>
116 OMS = 5,
117
118 /** <div class="openmath">
119 If $A_1,...,A_n\;(n>0)$ are <span style="font-variant:small-caps;">OpenMath</span> objects, then
120 $\mathrm{application}(A_1,...,A_n)$ is an <span style="font-variant:small-caps;">OpenMath</span> application object.
121 We call $A_1$ the function and $A_2$ to $A_n$ the arguments.
122 </div> */
123 OMA = 6,
124
125 /** <div class="openmath">
126 If $B$ and $C$ are <span style="font-variant:small-caps;">OpenMath</span> objects, and $v_1,...,v_n\;(n\geq0)$
127 are <span style="font-variant:small-caps;">OpenMath</span> variables or attributed variables, then
128 $\mathrm{binding}(B,v_1,...,v_n,C)$ is an <span style="font-variant:small-caps;">OpenMath</span> binding object.
129 $B$ is called the binder, $v_1,...,v_n$ are called variable bindings, and
130 $C$ is called the body of the binding object above.
131 </div> */
132 OMBIND = 7,
133
134 /** <div class="openmath">
135 If $S$ is an <span style="font-variant:small-caps;">OpenMath</span> symbol and $A_1,...,A_n\;(n\geq0)$ are <span style="font-variant:small-caps;">OpenMath</span> objects or
136 derived <span style="font-variant:small-caps;">OpenMath</span> objects, then $\mathrm{error}(S,A_1,...,A_n)$ is an <span style="font-variant:small-caps;">OpenMath</span> error object.
137 </div> */
138 OME = 8,
139
140 /** <div class="openmath">
141 If $S_1,...,S_n$ are <span style="font-variant:small-caps;">OpenMath</span> symbols, and $A$ is an <span style="font-variant:small-caps;">OpenMath</span> object, and
142 $A_1,...,A_n\;(n>0)$ are <span style="font-variant:small-caps;">OpenMath</span> objects or derived <span style="font-variant:small-caps;">OpenMath</span> objects, then
143 $\mathrm{attribution}(A,S_1\;A_1,...,S_n\;A_n)$ is an <span style="font-variant:small-caps;">OpenMath</span> attribution object. We call
144 $A$ the attributed object, the $S_i$ the keys, and the $A_i$ the attribute values.
145 </div> */
146 OMATTR = 9,
147
148 /** <div class="openmath">
149 If $A$ is not an <span style="font-variant:small-caps;">OpenMath</span> object, then $\mathrm{foreign}(A)$ is an <span style="font-variant:small-caps;">OpenMath</span> foreign object.
150 An <span style="font-variant:small-caps;">OpenMath</span> foreign object may optionally have an encoding field which describes how its
151 contents should be interpreted.
152 </div> */
153 OMFOREIGN = 10,
154
155 /** <div class="openmath">
156 <span style="font-variant:small-caps;">OpenMath</span> integers, symbols, variables, floating point numbers, character strings, bytearrays,
157 applications, binding, attributions, error, and foreign objects can also be encoded as an empty
158 OMR element with an href attribute whose value is the value of a URI referencing an id attribute of an
159 <span style="font-variant:small-caps;">OpenMath</span> object of that type. The <span style="font-variant:small-caps;">OpenMath</span> element represented by this OMR reference is a copy of the
160 <span style="font-variant:small-caps;">OpenMath</span> element referenced href attribute. Note that this copy is structurally equal, but not
161 identical to the element referenced. These URI references will often be relative, in which case they
162 are resolved using the base URI of the document containing the <span style="font-variant:small-caps;">OpenMath</span>.
163 </div> */
164 OMR = 11,
165}
166
167/// Enum representing all possible OᴘᴇɴMᴀᴛʜ objects.
168///
169/// This enum encompasses the complete OᴘᴇɴMᴀᴛʜ object model, providing variants
170/// for each type of mathematical object that can be represented in <span style="font-variant:small-caps;">OpenMath</span>.
171///
172/// Note that we add `attributes` to each variant rather than having a separate
173/// [`OMATTR`](OMKind::OMATTR) case; that is to avoid having to deal with nested
174/// `OMATTR(OMATTR(OMATTR(...` terms or having to make the grammar significantly
175/// more complicated.
176///
177///<div class="openmath">
178/// OᴘᴇɴMᴀᴛʜ objects are built recursively as follows.
179/// </div>
180#[derive(Debug, Clone, PartialEq, Eq, Hash)]
181#[repr(u8)]
182pub enum OpenMath<'om> {
183 /** <div class="openmath">
184 Integers in the mathematical sense, with no predefined range.
185 They are “infinite precision” integers (also called “bignums” in computer algebra).
186 </div> */
187 OMI {
188 int: Int<'om>,
189 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
190 } = OMKind::OMI as _,
191
192 /** <div class="openmath">
193 Double precision floating-point numbers following the IEEE 754-1985 standard.
194 </div> */
195 OMF {
196 float: ordered_float::OrderedFloat<f64>,
197 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
198 } = OMKind::OMF as _,
199
200 /** <div class="openmath">
201 A Unicode Character string. This also corresponds to “characters” in XML.
202 </div> */
203 OMSTR {
204 string: Cow<'om, str>,
205 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
206 } = OMKind::OMSTR as _,
207
208 /** <div class="openmath">
209 A sequence of bytes.
210 </div> */
211 OMB {
212 bytes: Cow<'om, [u8]>,
213 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
214 } = OMKind::OMB as _,
215
216 ///<div class="openmath">
217 ///
218 /// A Variable must have a name which is a sequence of characters matching a regular
219 /// expression, as described in [Section 2.3](https://openmath.org/standard/om20-2019-07-01/omstd20.html#sec_names).
220 ///
221 ///</div>
222 ///
223 ///(Note: We do not enforce that names are valid XML names;)
224 OMV {
225 name: Cow<'om, str>,
226 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
227 } = OMKind::OMV as _,
228
229 /** <div class="openmath">
230 A Symbol encodes three fields of information, a symbol name, a Content Dictionary name,
231 and (optionally) a Content Dictionary base URI, The name of a symbol is a sequence of
232 characters matching the regular expression described in Section 2.3.
233 The Content Dictionary is the location of the definition of the symbol, consisting of a
234 name (a sequence of characters matching the regular expression described in Section 2.3)
235 and, optionally, a unique prefix called a cdbase which is used to disambiguate multiple
236 Content Dictionaries of the same name. There are other properties of the symbol that are
237 not explicit in these fields but whose values may be obtained by inspecting the Content
238 Dictionary specified. These include the symbol definition, formal properties and examples
239 and, optionally, a role which is a restriction on where the symbol may appear in an
240 <span style="font-variant:small-caps;">OpenMath</span> object. The possible roles are described in Section 2.1.4.
241 </div> */
242 OMS {
243 cd: Cow<'om, str>,
244 name: Cow<'om, str>,
245 cdbase: Option<Cow<'om, str>>,
246 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
247 } = OMKind::OMS as _,
248
249 /** <div class="openmath">
250 If $A_1,...,A_n\;(n>0)$ are <span style="font-variant:small-caps;">OpenMath</span> objects, then
251 $\mathrm{application}(A_1,...,A_n)$ is an <span style="font-variant:small-caps;">OpenMath</span> application object.
252 We call $A_1$ the function and $A_2$ to $A_n$ the arguments.
253 </div> */
254 OMA {
255 applicant: Box<Self>,
256 arguments: Vec<Self>,
257 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
258 } = OMKind::OMA as _,
259
260 /** <div class="openmath">
261 If $S$ is an <span style="font-variant:small-caps;">OpenMath</span> symbol and $A_1,...,A_n\;(n\geq0)$ are <span style="font-variant:small-caps;">OpenMath</span> objects or
262 derived <span style="font-variant:small-caps;">OpenMath</span> objects, then $\mathrm{error}(S,A_1,...,A_n)$ is an <span style="font-variant:small-caps;">OpenMath</span> error object.
263 </div> */
264 OME {
265 cd: Cow<'om, str>,
266 name: Cow<'om, str>,
267 cdbase: Option<Cow<'om, str>>,
268 arguments: Vec<OMMaybeForeign<'om, Self>>,
269 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
270 } = OMKind::OME as _,
271
272 /** <div class="openmath">
273 If $B$ and $C$ are <span style="font-variant:small-caps;">OpenMath</span> objects, and $v_1,...,v_n\;(n\geq0)$
274 are <span style="font-variant:small-caps;">OpenMath</span> variables or attributed variables, then
275 $\mathrm{binding}(B,v_1,...,v_n,C)$ is an <span style="font-variant:small-caps;">OpenMath</span> binding object.
276 $B$ is called the binder, $v_1,...,v_n$ are called variable bindings, and
277 $C$ is called the body of the binding object above.
278 </div> */
279 OMBIND {
280 binder: Box<Self>,
281 variables: Vec<BoundVariable<'om>>,
282 object: Box<Self>,
283 attributes: Vec<Attr<'om, OMMaybeForeign<'om, Self>>>,
284 } = OMKind::OMBIND as _,
285}
286
287/// A bound variable in an [`OMBIND`](OpenMath::OMBIND)
288#[derive(Debug, Clone, PartialEq, Eq, Hash)]
289pub struct BoundVariable<'om> {
290 /// the name of the variable
291 pub name: Cow<'om, str>,
292 /// (optional) attributes of the variable;
293 /// this Vec being non-empty represents the case `OMATTR(...,OMV(name))`
294 pub attributes: Vec<Attr<'om, OMMaybeForeign<'om, OpenMath<'om>>>>,
295}
296impl ser::BindVar for &BoundVariable<'_> {
297 #[inline]
298 fn attrs(&self) -> impl ExactSizeIterator<Item: ser::OMAttr> {
299 self.attributes.iter()
300 }
301 #[inline]
302 fn name(&self) -> impl std::fmt::Display {
303 &*self.name
304 }
305}
306
307/// An attribute in an [`OMATTR`](OMKind::OMATTR)
308///
309/// Generic over the attribute value, so it can be used in [OpenMath] and [OM]
310#[derive(Debug, Clone, PartialEq, Eq, Hash)]
311pub struct Attr<'o, I> {
312 pub cdbase: Option<Cow<'o, str>>,
313 pub cd: Cow<'o, str>,
314 pub name: Cow<'o, str>,
315 pub value: I,
316}
317impl<I> ser::OMAttr for &Attr<'_, I>
318where
319 for<'a> &'a I: ser::OMOrForeign,
320{
321 #[inline]
322 fn symbol(&self) -> impl AsOMS {
323 ser::Uri {
324 cdbase: self.cdbase.as_deref(),
325 cd: &self.cd,
326 name: &self.name,
327 }
328 }
329 fn value(&self) -> impl ser::OMOrForeign {
330 &self.value
331 }
332}
333
334/// Either an [OpenMath Expression](OpenMath) or an [`OMFOREIGN`](OMKind::OMFOREIGN).
335///
336/// Generic over the non-OMFOREIGN-case, so it can be used in both [OpenMath] and [OM]
337#[derive(Debug, Clone, PartialEq, Eq, Hash)]
338pub enum OMMaybeForeign<'o, I> {
339 // An OMExpr
340 OM(I),
341
342 /** <div class="openmath">
343 If $A$ is not an OpenMath object, then $\mathrm{foreign}(A)$ is an OpenMath foreign object.
344 An OpenMath foreign object may optionally have an encoding field which describes how its
345 contents should be interpreted.
346 </div> */
347 Foreign {
348 encoding: Option<Cow<'o, str>>,
349 value: Cow<'o, str>,
350 },
351}
352
353impl<I: ser::OMSerializable> ser::OMOrForeign for &OMMaybeForeign<'_, I> {
354 /// converts this into an `Either`(crate::either::Either)
355 fn om_or_foreign(
356 self,
357 ) -> crate::either::Either<
358 impl OMSerializable,
359 (Option<impl std::fmt::Display>, impl std::fmt::Display),
360 > {
361 match self {
362 OMMaybeForeign::OM(i) => either::Either::Left(i),
363 OMMaybeForeign::Foreign { encoding, value } => {
364 either::Either::Right((encoding.as_deref(), &**value))
365 }
366 }
367 }
368}
369
370impl ser::OMSerializable for OpenMath<'_> {
371 fn as_openmath<'s, S: ser::OMSerializer<'s>>(&self, serializer: S) -> Result<S::Ok, S::Err> {
372 struct NoAttrs<'s, 'o>(&'s OpenMath<'o>);
373 impl ser::OMSerializable for NoAttrs<'_, '_> {
374 fn as_openmath<'s, S: ser::OMSerializer<'s>>(
375 &self,
376 serializer: S,
377 ) -> Result<S::Ok, S::Err> {
378 match self.0 {
379 OpenMath::OMI { int, .. } => int.as_openmath(serializer),
380 OpenMath::OMF { float, .. } => float.0.as_openmath(serializer),
381 OpenMath::OMSTR { string, .. } => string.as_openmath(serializer),
382 OpenMath::OMB { bytes, .. } => bytes.as_openmath(serializer),
383 OpenMath::OMV { name, .. } => ser::Omv(name).as_openmath(serializer),
384 OpenMath::OMS {
385 cd, name, cdbase, ..
386 } => ser::Uri {
387 cdbase: cdbase.as_deref(),
388 name,
389 cd,
390 }
391 .as_oms()
392 .as_openmath(serializer),
393 OpenMath::OMA {
394 applicant,
395 arguments,
396 ..
397 } => serializer.oma(&**applicant, arguments.iter()),
398 OpenMath::OME {
399 cd,
400 name,
401 cdbase,
402 arguments,
403 ..
404 } => serializer.ome(
405 &ser::Uri {
406 cdbase: cdbase.as_deref(),
407 cd,
408 name,
409 },
410 arguments.iter(),
411 ),
412 OpenMath::OMBIND {
413 binder,
414 variables,
415 object,
416 ..
417 } => serializer.ombind(&**binder, variables.iter(), &**object),
418 }
419 }
420 }
421 match self {
422 Self::OMI { attributes, .. }
423 | Self::OMF { attributes, .. }
424 | Self::OMSTR { attributes, .. }
425 | Self::OMB { attributes, .. }
426 | Self::OMV { attributes, .. }
427 | Self::OMS { attributes, .. }
428 | Self::OMA { attributes, .. }
429 | Self::OME { attributes, .. }
430 | Self::OMBIND { attributes, .. }
431 if !attributes.is_empty() =>
432 {
433 serializer.omattr(attributes.iter(), NoAttrs(self))
434 }
435 _ => NoAttrs(self).as_openmath(serializer),
436 }
437 }
438}
439
440impl<'o> de::OMDeserializable<'o> for OpenMath<'o> {
441 type Ret = Self;
442 type Err = Infallible;
443 #[allow(clippy::too_many_lines)]
444 fn from_openmath(om: OM<'o, Self>, cdbase: &str) -> Result<Self, Self::Err>
445 where
446 Self: Sized,
447 {
448 /*fn do_attrs<'o>(
449 attrs: Vec<de::OMAttr<'o, OpenMath<'o>>>,
450 ) -> Vec<Attr<'o, OMMaybeForeign<'o, OpenMath<'o>>>> {
451 attrs
452 .into_iter()
453 .map(|a| Attr {
454 cdbase: a.cdbase,
455 cd: a.cd,
456 name: a.name,
457 value: match a.value {
458 either::Either::Left(a) => OMMaybeForeign::OM(a),
459 either::Either::Right(OMMaybeForeign::Foreign { encoding, value }) => {
460 OMMaybeForeign::Foreign { encoding, value }
461 }
462 either::Either::Right(OMMaybeForeign::OM(_)) => {
463 unreachable!("by construction")
464 }
465 },
466 })
467 .collect()
468 }*/
469 Ok(match om {
470 OM::OMI { int, attrs } => Self::OMI {
471 int,
472 attributes: attrs,
473 },
474 OM::OMF { float, attrs } => Self::OMF {
475 float: float.into(),
476 attributes: attrs,
477 },
478 OM::OMSTR { string, attrs } => Self::OMSTR {
479 string,
480 attributes: attrs,
481 },
482 OM::OMB { bytes, attrs } => Self::OMB {
483 bytes,
484 attributes: attrs,
485 },
486 OM::OMV { name, attrs } => Self::OMV {
487 name,
488 attributes: attrs,
489 },
490 OM::OMS { cd, name, attrs } => Self::OMS {
491 cd,
492 name,
493 cdbase: Some(Cow::Owned(cdbase.to_string())),
494 attributes: attrs,
495 },
496 OM::OMA {
497 applicant,
498 arguments,
499 attrs,
500 } => Self::OMA {
501 applicant: Box::new(applicant),
502 arguments: arguments.into_iter().collect(),
503 attributes: attrs,
504 },
505 OM::OMBIND {
506 binder,
507 variables,
508 object,
509 attrs,
510 } => Self::OMBIND {
511 binder: Box::new(binder),
512 variables: variables
513 .into_iter()
514 .map(|(name, a)| BoundVariable {
515 name,
516 attributes: a,
517 })
518 .collect(),
519 object: Box::new(object),
520 attributes: attrs,
521 },
522 OM::OME {
523 cdbase,
524 cd,
525 name,
526 arguments,
527 attrs,
528 } => Self::OME {
529 cd,
530 name,
531 cdbase,
532 arguments,
533 attributes: attrs,
534 },
535 })
536 }
537}
538
539#[cfg(all(test, feature = "xml", feature = "serde"))]
540#[test]
541#[allow(clippy::too_many_lines)]
542fn roundtrip() {
543 use OpenMath::*;
544 const XML: &str = r#"<OMOBJ version="2.0" xmlns="http://www.openmath.org/OpenMath">
545 <OMBIND>
546 <OMS cdbase="http://openmath.org/cd" cd="fns1" name="lambda"/>
547 <OMBVAR>
548 <OMV name="x"/>
549 <OMATTR>
550 <OMATP>
551 <OMS cdbase="http://openmath.org/cd" cd="nope" name="type"/>
552 <OMS cdbase="http://openmath.org/cd" cd="arith1" name="real"/>
553 </OMATP>
554 <OMV name="y"/>
555 </OMATTR>
556 </OMBVAR>
557 <OMA>
558 <OMS cdbase="http://my.namespace" cd="utils" name="either"/>
559 <OMA>
560 <OMS cdbase="http://openmath.org/cd" cd="arith1" name="plus"/>
561 <OMI>128</OMI>
562 <OMATTR>
563 <OMATP>
564 <OMS cdbase="http://openmath.org/cd" cd="nope" name="type"/>
565 <OMFOREIGN>
566 <MOOT>this is an opaque OMFOREIGN</MOOT>
567 </OMFOREIGN>
568 </OMATP>
569 <OMI>-1234567898765432123456789</OMI>
570 </OMATTR>
571 <OMF dec="3.88988"/>
572 <OMSTR>some number</OMSTR>
573 <OMV name="x"/>
574 </OMA>
575 <OME>
576 <OMS cdbase="http://openmath.org" cd="error" name="unhandled_arithmetics"/>
577 <OMFOREIGN encoding="application/nonsense">
578 ERROAR CODE MOO
579 </OMFOREIGN>
580 </OME>
581 </OMA>
582 </OMBIND>
583 </OMOBJ>"#;
584 const JSON: &str = r#"{
585 "kind": "OMOBJ",
586 "openmath": "2.0",
587 "object": {
588 "kind": "OMBIND",
589 "binder": {
590 "kind": "OMS",
591 "cdbase": "http://openmath.org/cd",
592 "cd": "fns1",
593 "name": "lambda"
594 },
595 "variables": [
596 {
597 "kind": "OMV",
598 "name": "x"
599 },
600 {
601 "kind": "OMATTR",
602 "attributes": [
603 [
604 {
605 "kind": "OMS",
606 "cdbase": "http://openmath.org/cd",
607 "cd": "nope",
608 "name": "type"
609 },
610 {
611 "kind": "OMS",
612 "cdbase": "http://openmath.org/cd",
613 "cd": "arith1",
614 "name": "real"
615 }
616 ]
617 ],
618 "object": {
619 "kind": "OMV",
620 "name": "y"
621 }
622 }
623 ],
624 "object": {
625 "kind": "OMA",
626 "applicant": {
627 "kind": "OMS",
628 "cdbase": "http://my.namespace",
629 "cd": "utils",
630 "name": "either"
631 },
632 "arguments": [
633 {
634 "kind": "OMA",
635 "applicant": {
636 "kind": "OMS",
637 "cdbase": "http://openmath.org/cd",
638 "cd": "arith1",
639 "name": "plus"
640 },
641 "arguments": [
642 {
643 "kind": "OMI",
644 "integer": 128
645 },
646 {
647 "kind": "OMATTR",
648 "attributes": [
649 [
650 {
651 "kind": "OMS",
652 "cdbase": "http://openmath.org/cd",
653 "cd": "nope",
654 "name": "type"
655 },
656 {
657 "kind": "OMFOREIGN",
658 "foreign": "<MOOT>this is an opaque OMFOREIGN</MOOT>"
659 }
660 ]
661 ],
662 "object": {
663 "kind": "OMI",
664 "integer": -1234567898765432123456789
665 }
666 },
667 {
668 "kind": "OMF",
669 "float": 3.88988
670 },
671 {
672 "kind": "OMSTR",
673 "string": "some number"
674 },
675 {
676 "kind": "OMV",
677 "name": "x"
678 }
679 ]
680 },
681 {
682 "kind": "OME",
683 "error": {
684 "kind": "OMS",
685 "cdbase": "http://openmath.org",
686 "cd": "error",
687 "name": "unhandled_arithmetics"
688 },
689 "arguments": [
690 {
691 "kind": "OMFOREIGN",
692 "foreign": "ERROAR CODE MOO",
693 "encoding": "application/nonsense"
694 }
695 ]
696 }
697 ]
698 }
699 }
700 }"#;
701
702 let om = OMBIND {
703 binder: Box::new(OMS {
704 cdbase: Some(Cow::Borrowed("http://openmath.org/cd")),
705 cd: Cow::Borrowed("fns1"),
706 name: Cow::Borrowed("lambda"),
707 attributes: Vec::new(),
708 }),
709 variables: vec![
710 BoundVariable {
711 name: Cow::Borrowed("x"),
712 attributes: Vec::new(),
713 },
714 BoundVariable {
715 name: Cow::Borrowed("y"),
716 attributes: vec![Attr {
717 cdbase: Some(Cow::Borrowed("http://openmath.org/cd")),
718 cd: Cow::Borrowed("nope"),
719 name: Cow::Borrowed("type"),
720 value: OMMaybeForeign::OM(OMS {
721 cdbase: Some(Cow::Borrowed("http://openmath.org/cd")),
722 cd: Cow::Borrowed("arith1"),
723 name: Cow::Borrowed("real"),
724 attributes: Vec::new(),
725 }),
726 }],
727 },
728 ],
729 object: Box::new(OMA {
730 applicant: Box::new(OMS {
731 cd: Cow::Borrowed("utils"),
732 name: Cow::Borrowed("either"),
733 cdbase: Some(Cow::Borrowed("http://my.namespace")),
734 attributes: Vec::new(),
735 }),
736 arguments: vec![
737 OMA {
738 applicant: Box::new(OMS {
739 cdbase: Some(Cow::Borrowed("http://openmath.org/cd")),
740 cd: Cow::Borrowed("arith1"),
741 name: Cow::Borrowed("plus"),
742 attributes: Vec::new(),
743 }),
744 arguments: vec![
745 OMI {
746 int: 128.into(),
747 attributes: Vec::new(),
748 },
749 OMI {
750 int: Int::new("-1234567898765432123456789").expect("works"),
751 attributes: vec![Attr {
752 cdbase: Some(Cow::Borrowed("http://openmath.org/cd")),
753 cd: Cow::Borrowed("nope"),
754 name: Cow::Borrowed("type"),
755 value: OMMaybeForeign::Foreign {
756 encoding: None,
757 value: Cow::Borrowed(
758 "<MOOT>this is an opaque OMFOREIGN</MOOT>",
759 ),
760 },
761 }],
762 },
763 OMF {
764 float: 3.88988.into(),
765 attributes: Vec::new(),
766 },
767 OMSTR {
768 string: Cow::Borrowed("some number"),
769 attributes: Vec::new(),
770 },
771 OMV {
772 name: Cow::Borrowed("x"),
773 attributes: Vec::new(),
774 },
775 ],
776 attributes: Vec::new(),
777 },
778 OME {
779 cdbase: Some(Cow::Borrowed("http://openmath.org")),
780 cd: Cow::Borrowed("error"),
781 name: Cow::Borrowed("unhandled_arithmetics"),
782 arguments: vec![OMMaybeForeign::Foreign {
783 encoding: Some(Cow::Borrowed("application/nonsense")),
784 value: Cow::Borrowed("ERROAR CODE MOO"),
785 }],
786 attributes: Vec::new(),
787 },
788 ],
789 attributes: Vec::new(),
790 }),
791 attributes: Vec::new(),
792 };
793
794 let json = serde_json::to_string_pretty(&ser::OMObject(&om)).expect("works");
795 assert_eq!(
796 json.replace(|c: char| c.is_ascii_whitespace(), ""),
797 JSON.replace(|c: char| c.is_ascii_whitespace(), "")
798 );
799 let nom = serde_json::from_str::<'_, de::OMObject<OpenMath<'_>>>(&json)
800 .expect("works")
801 .into_inner();
802 assert_eq!(om, nom);
803 let xml = ser::OMObject(&nom).xml(true, true).to_string();
804 assert_eq!(
805 xml.replace(|c: char| c.is_ascii_whitespace(), ""),
806 XML.replace(|c: char| c.is_ascii_whitespace(), "")
807 );
808 let nom = de::OMObject::<OpenMath<'_>>::from_openmath_xml(&xml).expect("works");
809 assert_eq!(om, nom);
810}