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}