odata_parser_rs/
lib.rs

1use std::{str::FromStr, time::Duration};
2
3use chrono::NaiveDateTime;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Serialize, Deserialize)]
7pub enum Edm {
8    #[serde(rename = "Edm.Binary")]
9    Binary,
10    #[serde(rename = "Edm.Boolean")]
11    Boolean,
12    #[serde(rename = "Edm.Byte")]
13    Byte,
14    #[serde(rename = "Edm.DateTime")]
15    DateTime,
16    #[serde(rename = "Edm.DateTimeOffset")]
17    DateTimeOffset,
18    #[serde(rename = "Edm.Decimal")]
19    Decimal,
20    #[serde(rename = "Edm.Double")]
21    Double,
22    #[serde(rename = "Edm.Int16")]
23    Int16,
24    #[serde(rename = "Edm.Int32")]
25    Int32,
26    #[serde(rename = "Edm.String")]
27    String,
28}
29
30fn default_true() -> bool {
31    true
32}
33
34#[derive(Debug, Serialize, Deserialize)]
35pub enum EdmxVersion {
36    #[serde(rename = "1.0")]
37    V1_0,
38}
39
40#[derive(Debug, Serialize, Deserialize)]
41#[serde(rename_all = "PascalCase")]
42pub struct Edmx {
43    pub version: EdmxVersion,
44    pub data_services: DataServices,
45}
46
47impl Edmx {
48    pub fn default_schema(&self) -> Option<&Schema> {
49        self.data_services.default_schema()
50    }
51}
52
53impl FromStr for Edmx {
54    type Err = quick_xml::DeError;
55
56    fn from_str(s: &str) -> Result<Self, Self::Err> {
57        quick_xml::de::from_str(s)
58    }
59}
60
61#[derive(Debug, Serialize, Deserialize)]
62#[serde(rename_all = "PascalCase")]
63pub struct DataServices {
64    #[serde(rename = "Schema", default)]
65    pub schemas: Vec<Schema>,
66}
67
68impl DataServices {
69    pub fn default_schema(&self) -> Option<&Schema> {
70        self.schemas
71            .iter()
72            .find(|schema| schema.namespace == "Default")
73    }
74}
75
76#[derive(Debug, Serialize, Deserialize)]
77#[serde(rename_all = "PascalCase")]
78pub struct Schema {
79    pub namespace: String,
80    #[serde(rename = "EntityType", default)]
81    pub entities: Vec<EntityType>,
82    #[serde(rename = "Association", default)]
83    pub associations: Vec<Association>,
84    pub entity_container: Option<EntityContainer>,
85}
86
87impl Schema {
88    pub fn entity_sets(&self) -> Option<&Vec<EntitySet>> {
89        self.entity_container
90            .as_ref()
91            .map(|container| &container.entity_sets)
92    }
93
94    pub fn association_sets(&self) -> Option<&Vec<AssociationSet>> {
95        self.entity_container
96            .as_ref()
97            .map(|container| &container.association_sets)
98    }
99}
100
101#[derive(Debug, Serialize, Deserialize)]
102#[serde(rename_all = "PascalCase")]
103pub struct EntityContainer {
104    pub name: String,
105    #[serde(rename = "EntitySet", default)]
106    pub entity_sets: Vec<EntitySet>,
107    #[serde(rename = "AssociationSet", default)]
108    pub association_sets: Vec<AssociationSet>,
109}
110
111#[derive(Debug, Serialize, Deserialize)]
112#[serde(rename_all = "PascalCase")]
113pub struct Association {
114    pub name: String,
115
116    #[serde(rename = "End")]
117    pub ends: [End; 2],
118}
119
120#[derive(Debug, Serialize, Deserialize)]
121#[serde(rename_all = "PascalCase")]
122pub struct AssociationSet {
123    pub name: String,
124    pub association: String,
125
126    #[serde(rename = "End")]
127    pub ends: [End; 2],
128}
129
130#[derive(Debug, Serialize, Deserialize)]
131#[serde(rename_all = "PascalCase")]
132pub struct End {
133    pub role: Option<String>,
134    pub entity_set: Option<String>,
135    #[serde(rename = "Type")]
136    pub entity_type: Option<String>,
137    pub multiplicity: Option<String>,
138}
139
140#[derive(Debug, Serialize, Deserialize)]
141#[serde(rename_all = "PascalCase")]
142pub struct EntitySet {
143    pub name: String,
144    pub entity_type: String,
145}
146
147#[derive(Debug, Serialize, Deserialize)]
148#[serde(rename_all = "PascalCase")]
149pub struct EntityType {
150    pub name: String,
151    pub key: Key,
152    #[serde(rename = "Property", default)]
153    pub properties: Vec<Property>,
154    #[serde(rename = "NavigationProperty", default)]
155    pub navigations: Vec<NavigationProperty>,
156}
157
158impl EntityType {
159    pub fn key_property(&self) -> Option<&Property> {
160        self.properties
161            .iter()
162            .find(|property| property.name == self.key.property_ref.name)
163    }
164}
165
166#[derive(Debug, Serialize, Deserialize)]
167#[serde(rename_all = "PascalCase")]
168pub struct NavigationProperty {
169    pub name: String,
170    pub relationship: String,
171    pub to_role: String,
172    pub from_role: String,
173}
174
175#[derive(Debug, Serialize, Deserialize)]
176#[serde(rename_all = "PascalCase")]
177pub struct Key {
178    pub property_ref: PropertyRef,
179}
180
181#[derive(Debug, Serialize, Deserialize)]
182#[serde(rename_all = "PascalCase")]
183pub struct PropertyRef {
184    pub name: String,
185}
186
187#[derive(Debug, Serialize, Deserialize)]
188#[serde(rename_all = "PascalCase")]
189pub struct Property {
190    pub name: String,
191    #[serde(flatten)]
192    pub inner: PropertyType,
193    #[serde(default = "default_true")]
194    pub nullable: bool,
195}
196
197#[derive(Debug, Serialize, Deserialize)]
198#[serde(rename_all = "PascalCase", tag = "Type")]
199pub enum PropertyType {
200    #[serde(rename = "Edm.Binary")]
201    Binary {
202        max_length: Option<u32>,
203        fixed_length: Option<u32>,
204        default: Option<Vec<u8>>,
205    },
206    #[serde(rename = "Edm.Boolean")]
207    Boolean { default: Option<bool> },
208    #[serde(rename = "Edm.Byte")]
209    Byte {
210        precision: Option<u8>,
211        default: Option<Vec<u8>>,
212    },
213    #[serde(rename = "Edm.DateTime")]
214    DateTime {
215        precision: Option<u8>,
216        default: Option<NaiveDateTime>,
217    },
218    #[serde(rename = "Edm.DateTimeOffset")]
219    DateTimeOffset {
220        precision: Option<u8>,
221        default: Option<Duration>,
222    },
223    #[serde(rename = "Edm.Decimal")]
224    Decimal {
225        precision: Option<u8>,
226        default: Option<f64>,
227    },
228    #[serde(rename = "Edm.Double")]
229    Double {
230        precision: Option<u8>,
231        default: Option<f64>,
232    },
233    #[serde(rename = "Edm.Int16")]
234    Int16 {
235        precision: Option<u8>,
236        default: Option<i16>,
237    },
238    #[serde(rename = "Edm.Int32")]
239    Int32 {
240        precision: Option<u8>,
241        default: Option<Vec<u8>>,
242    },
243    #[serde(rename = "Edm.String")]
244    String {
245        precision: Option<u8>,
246        max_length: Option<u32>,
247        fixed_length: Option<u32>,
248    },
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254
255    #[test]
256    pub fn test_parse_folketinget_metadata() {
257        let edmx = Edmx::from_str(include_str!("../tests/folketinget.xml")).unwrap();
258
259        for set in edmx.default_schema().unwrap().entity_sets().unwrap() {
260            println!("{:#?}", set);
261        }
262
263        assert_eq!(
264            50,
265            edmx.default_schema().unwrap().entity_sets().unwrap().len()
266        );
267    }
268}