ooxmlsdk_build/
lib.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3use std::{fs, path::Path};
4use syn::{parse_str, Ident, ItemMod};
5
6use crate::generator::context::GenContext;
7use crate::generator::deserializer::gen_deserializers;
8use crate::generator::open_xml_part::gen_open_xml_parts;
9use crate::generator::open_xml_schema::gen_open_xml_schemas;
10use crate::generator::serializer::gen_serializer;
11use crate::generator::validator::gen_validators;
12
13pub mod generator;
14pub mod includes;
15pub mod models;
16pub mod utils;
17
18pub fn generate(data_dir: &str, out_dir: &str) {
19  let out_dir_path = Path::new(out_dir);
20
21  let mut gen_context = GenContext::new(data_dir);
22
23  for namespace in gen_context.namespaces.iter() {
24    gen_context
25      .prefix_namespace_map
26      .insert(&namespace.prefix, namespace);
27
28    gen_context
29      .uri_namespace_map
30      .insert(&namespace.uri, namespace);
31  }
32
33  for typed_namespace in gen_context.typed_namespaces.iter() {
34    gen_context
35      .namespace_typed_namespace_map
36      .insert(&typed_namespace.namespace, typed_namespace);
37  }
38
39  for typed_schema in gen_context.typed_schemas.iter() {
40    for ty in typed_schema.iter() {
41      if !ty.part_class_name.is_empty() {
42        gen_context
43          .part_name_type_name_map
44          .insert(&ty.part_class_name, &ty.name);
45      }
46    }
47  }
48
49  for schema in gen_context.schemas.iter() {
50    let namespace = gen_context
51      .uri_namespace_map
52      .get(schema.target_namespace.as_str())
53      .ok_or(format!("{:?}", schema.target_namespace))
54      .unwrap();
55
56    gen_context
57      .prefix_schema_map
58      .insert(&namespace.prefix, schema);
59
60    for e in schema.enums.iter() {
61      gen_context.enum_type_enum_map.insert(&e.r#type, e);
62
63      gen_context
64        .enum_type_namespace_map
65        .insert(&e.r#type, namespace);
66    }
67
68    for ty in schema.types.iter() {
69      gen_context.type_name_type_map.insert(&ty.name, ty);
70
71      gen_context
72        .type_name_namespace_map
73        .insert(&ty.name, namespace);
74
75      if !ty.part.is_empty() {
76        gen_context
77          .part_name_type_name_map
78          .insert(&ty.part, &ty.name);
79      }
80    }
81  }
82
83  gen_context
84    .part_name_type_name_map
85    .insert("StyleDefinitionsPart", "w:CT_Styles/w:styles");
86  gen_context
87    .part_name_type_name_map
88    .insert("StylesWithEffectsPart", "w:CT_Styles/w:styles");
89
90  write_schemas(&gen_context, out_dir_path);
91
92  write_deserializers(&gen_context, out_dir_path);
93
94  write_serializers(&gen_context, out_dir_path);
95
96  #[cfg(feature = "parts")]
97  let with_parts = true;
98  #[cfg(not(feature = "parts"))]
99  let with_parts = false;
100
101  if with_parts {
102    write_parts(&gen_context, out_dir_path);
103  }
104
105  #[cfg(feature = "validators")]
106  let with_validators = true;
107  #[cfg(not(feature = "validators"))]
108  let with_validators = false;
109
110  if with_validators {
111    write_validators(&gen_context, out_dir_path);
112  }
113}
114
115pub(crate) fn write_schemas(gen_context: &GenContext, out_dir_path: &Path) {
116  let out_schemas_dir_path = out_dir_path.join("schemas");
117  let out_common_dir_path = out_dir_path.join("common");
118
119  fs::create_dir_all(&out_schemas_dir_path).unwrap();
120  fs::create_dir_all(&out_common_dir_path).unwrap();
121
122  let mut schemas_mod_use_list: Vec<ItemMod> = vec![];
123
124  for schema in gen_context.schemas.iter() {
125    let token_stream = gen_open_xml_schemas(schema, gen_context);
126    let syntax_tree = syn::parse2(token_stream).unwrap();
127    let formatted = prettyplease::unparse(&syntax_tree);
128    let schema_path = out_schemas_dir_path.join(format!("{}.rs", &schema.module_name));
129    fs::write(schema_path, formatted).unwrap();
130
131    let schema_mod_ident: Ident = parse_str(&schema.module_name).unwrap();
132    let shcemas_mod_use: ItemMod = parse_str(
133      &quote! {
134        pub mod #schema_mod_ident;
135      }
136      .to_string(),
137    )
138    .unwrap();
139    schemas_mod_use_list.push(shcemas_mod_use);
140  }
141
142  let token_stream: TokenStream = parse_str(include_str!("includes/simple_type.rs")).unwrap();
143  let syntax_tree = syn::parse2(token_stream).unwrap();
144  let formatted = prettyplease::unparse(&syntax_tree);
145  let schemas_mod_path = out_schemas_dir_path.join("simple_type.rs");
146  fs::write(schemas_mod_path, formatted).unwrap();
147
148  let token_stream: TokenStream = parse_str(include_str!("includes/common.rs")).unwrap();
149  let syntax_tree = syn::parse2(token_stream).unwrap();
150  let formatted = prettyplease::unparse(&syntax_tree);
151  let schemas_mod_path = out_common_dir_path.join("mod.rs");
152  fs::write(schemas_mod_path, formatted).unwrap();
153
154  let token_stream: TokenStream =
155    parse_str(include_str!("includes/packages/opc_content_types.rs")).unwrap();
156  let syntax_tree = syn::parse2(token_stream).unwrap();
157  let formatted = prettyplease::unparse(&syntax_tree);
158  let schemas_mod_path = out_schemas_dir_path.join("opc_content_types.rs");
159  fs::write(schemas_mod_path, formatted).unwrap();
160
161  let token_stream: TokenStream =
162    parse_str(include_str!("includes/packages/opc_relationships.rs")).unwrap();
163  let syntax_tree = syn::parse2(token_stream).unwrap();
164  let formatted = prettyplease::unparse(&syntax_tree);
165  let schemas_mod_path = out_schemas_dir_path.join("opc_relationships.rs");
166  fs::write(schemas_mod_path, formatted).unwrap();
167
168  let token_stream: TokenStream =
169    parse_str(include_str!("includes/packages/opc_core_properties.rs")).unwrap();
170  let syntax_tree = syn::parse2(token_stream).unwrap();
171  let formatted = prettyplease::unparse(&syntax_tree);
172  let schemas_mod_path = out_schemas_dir_path.join("opc_core_properties.rs");
173  fs::write(schemas_mod_path, formatted).unwrap();
174
175  let token_stream: TokenStream = quote! {
176    pub mod simple_type;
177    pub mod opc_content_types;
178    pub mod opc_core_properties;
179    pub mod opc_relationships;
180    #( #schemas_mod_use_list )*
181  };
182  let syntax_tree = syn::parse2(token_stream).unwrap();
183  let formatted = prettyplease::unparse(&syntax_tree);
184  let schemas_mod_path = out_schemas_dir_path.join("mod.rs");
185  fs::write(schemas_mod_path, formatted).unwrap();
186}
187
188pub(crate) fn write_deserializers(gen_context: &GenContext, out_dir_path: &Path) {
189  let out_deserializers_dir_path = &out_dir_path.join("deserializers");
190
191  fs::create_dir_all(out_deserializers_dir_path).unwrap();
192
193  let mut deserializers_mod_use_list: Vec<ItemMod> = vec![];
194
195  for schema in gen_context.schemas.iter() {
196    let token_stream = gen_deserializers(schema, gen_context);
197    let syntax_tree = syn::parse2(token_stream).unwrap();
198    let formatted = prettyplease::unparse(&syntax_tree);
199    let part_path = out_deserializers_dir_path.join(format!("{}.rs", &schema.module_name));
200    fs::write(part_path, formatted).unwrap();
201
202    let deserializer_mod_ident: Ident = parse_str(&schema.module_name).unwrap();
203    let deserializer_mod_use: ItemMod = parse_str(
204      &quote! {
205        pub mod #deserializer_mod_ident;
206      }
207      .to_string(),
208    )
209    .unwrap();
210    deserializers_mod_use_list.push(deserializer_mod_use);
211  }
212
213  let token_stream: TokenStream = quote! {
214    #( #deserializers_mod_use_list )*
215  };
216  let syntax_tree = syn::parse2(token_stream).unwrap();
217  let formatted = prettyplease::unparse(&syntax_tree);
218  let deserializers_mod_path = out_deserializers_dir_path.join("mod.rs");
219  fs::write(deserializers_mod_path, formatted).unwrap();
220}
221
222pub(crate) fn write_serializers(gen_context: &GenContext, out_dir_path: &Path) {
223  let out_serializers_dir_path = &out_dir_path.join("serializers");
224
225  fs::create_dir_all(out_serializers_dir_path).unwrap();
226
227  let mut serializers_mod_use_list: Vec<ItemMod> = vec![];
228
229  for schema in gen_context.schemas.iter() {
230    let token_stream = gen_serializer(schema, gen_context);
231    let syntax_tree = syn::parse2(token_stream).unwrap();
232    let formatted = prettyplease::unparse(&syntax_tree);
233    let part_path = out_serializers_dir_path.join(format!("{}.rs", &schema.module_name));
234    fs::write(part_path, formatted).unwrap();
235
236    let serializer_mod_ident: Ident = parse_str(&schema.module_name).unwrap();
237    let serializer_mod_use: ItemMod = parse_str(
238      &quote! {
239        pub mod #serializer_mod_ident;
240      }
241      .to_string(),
242    )
243    .unwrap();
244    serializers_mod_use_list.push(serializer_mod_use);
245  }
246
247  let token_stream: TokenStream = quote! {
248    #( #serializers_mod_use_list )*
249  };
250  let syntax_tree = syn::parse2(token_stream).unwrap();
251  let formatted = prettyplease::unparse(&syntax_tree);
252  let serializers_mod_path = out_serializers_dir_path.join("mod.rs");
253  fs::write(serializers_mod_path, formatted).unwrap();
254}
255
256pub(crate) fn write_parts(gen_context: &GenContext, out_dir_path: &Path) {
257  let out_parts_dir_path = &out_dir_path.join("parts");
258
259  fs::create_dir_all(out_parts_dir_path).unwrap();
260
261  let mut parts_mod_use_list: Vec<ItemMod> = vec![];
262
263  for part in gen_context.parts.iter() {
264    let token_stream = gen_open_xml_parts(part, gen_context);
265    let syntax_tree = syn::parse2(token_stream).unwrap();
266    let formatted = prettyplease::unparse(&syntax_tree);
267    let part_path = out_parts_dir_path.join(format!("{}.rs", &part.module_name));
268    fs::write(part_path, formatted).unwrap();
269
270    let part_mod_ident: Ident = parse_str(&part.module_name).unwrap();
271    let part_mod_use: ItemMod = parse_str(
272      &quote! {
273        pub mod #part_mod_ident;
274      }
275      .to_string(),
276    )
277    .unwrap();
278    parts_mod_use_list.push(part_mod_use);
279  }
280
281  let token_stream: TokenStream = quote! {
282    #( #parts_mod_use_list )*
283  };
284
285  let syntax_tree = syn::parse2(token_stream).unwrap();
286  let formatted = prettyplease::unparse(&syntax_tree);
287
288  let parts_mod_path = out_parts_dir_path.join("mod.rs");
289
290  fs::write(parts_mod_path, formatted).unwrap();
291}
292
293pub(crate) fn write_validators(gen_context: &GenContext, out_dir_path: &Path) {
294  let out_validators_dir_path = &out_dir_path.join("validators");
295
296  fs::create_dir_all(out_validators_dir_path).unwrap();
297
298  let mut validators_mod_use_list: Vec<ItemMod> = vec![];
299
300  for part in gen_context.schemas.iter() {
301    let token_stream = gen_validators(part, gen_context);
302    let syntax_tree = syn::parse2(token_stream).unwrap();
303    let formatted = prettyplease::unparse(&syntax_tree);
304    let part_path = out_validators_dir_path.join(format!("{}.rs", &part.module_name));
305    fs::write(part_path, formatted).unwrap();
306
307    let validator_mod_ident: Ident = parse_str(&part.module_name).unwrap();
308    let validator_mod_use: ItemMod = parse_str(
309      &quote! {
310        pub mod #validator_mod_ident;
311      }
312      .to_string(),
313    )
314    .unwrap();
315    validators_mod_use_list.push(validator_mod_use);
316  }
317
318  let token_stream: TokenStream = quote! {
319    #( #validators_mod_use_list )*
320  };
321  let syntax_tree = syn::parse2(token_stream).unwrap();
322  let formatted = prettyplease::unparse(&syntax_tree);
323  let validators_mod_path = out_validators_dir_path.join("mod.rs");
324  fs::write(validators_mod_path, formatted).unwrap();
325}
326
327#[cfg(test)]
328mod tests {
329  use super::*;
330
331  #[test]
332  fn test_gen() {
333    generate("../ooxmlsdk/data", "src");
334  }
335}