linked_data_schema_derive/
lib.rs

1use linked_data_core::{RdfEnum, RdfField, RdfStruct, RdfType, RdfVariant, TokenGenerator};
2use proc_macro_error::proc_macro_error;
3use proc_macro2::{Literal, TokenStream};
4use quote::ToTokens;
5use syn::DeriveInput;
6use uuid::Uuid;
7
8#[proc_macro_error]
9#[proc_macro_derive(LinkedDataSchema, attributes(ld))]
10pub fn derive_serialize(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
11  let raw_input = syn::parse_macro_input!(item as DeriveInput);
12  let linked_data_type: RdfType<Schema> = RdfType::from_derive(raw_input);
13
14  let mut output = TokenStream::new();
15  linked_data_type.to_tokens(&mut output);
16  output.into()
17}
18
19#[derive(Debug)]
20struct Schema;
21
22impl TokenGenerator for Schema {
23  fn generate_type_tokens(linked_data_type: &RdfType<Self>, tokens: &mut TokenStream) {
24    let implementations = match linked_data_type {
25      RdfType::Enum(rdf_enum) => quote::quote! {#rdf_enum},
26      RdfType::Struct(rdf_struct) => quote::quote! {#rdf_struct},
27    };
28
29    tokens.extend(implementations)
30  }
31
32  fn generate_struct_tokens(rdf_struct: &RdfStruct<Self>, tokens: &mut TokenStream) {
33    let type_iri = rdf_struct.type_iri().unwrap();
34
35    let type_iri_shape = Literal::string(&format!("{}Shape", type_iri.as_str()));
36    let type_iri = Literal::string(type_iri.as_str());
37
38    let prefix_mapping = rdf_struct.prefix_mappings().clone();
39
40    let insert_all_prefix_mapping = prefix_mapping
41      .into_iter()
42      .map(|(prefix, iri)| {
43        let prefix = Literal::string(&prefix.to_string());
44        let iri = Literal::string(&iri);
45
46        quote::quote! {
47          prefix_map.insert(#prefix, &iri!(#iri)).unwrap();
48        }
49      })
50      .collect::<TokenStream>();
51
52    let ident = &rdf_struct.ident;
53    let fields = &rdf_struct.fields;
54
55    let property_shapes_iris = fields
56      .iter()
57      .map(|field| {
58        if let Some(predicate) = field.predicate() {
59          let identifier = Literal::string(&format!("{}Field", predicate.as_str()));
60
61          quote::quote! {
62            RDFNode::iri(IriS::from_str(#identifier).unwrap()),
63          }
64        } else {
65          quote::quote! {}
66        }
67      })
68      .collect::<TokenStream>();
69
70    let struct_blank_node = Literal::string(Uuid::new_v4().to_string().as_str());
71
72    tokens.extend(quote::quote! {
73      impl ::linked_data_schema::LinkedDataSchemaFieldVisitor for #ident {
74        fn field_components() -> Vec<::linked_data_schema::reexports::shacl_ast::ast::component::Component> {
75          Self::components()
76        }
77
78        fn type_iri_ref() -> Option<::linked_data_schema::reexports::prefixmap::IriRef> {
79          use ::linked_data_schema::reexports::prefixmap::IriRef;
80          use ::linked_data_schema::reexports::iri_s::iri;
81
82          Some(IriRef::iri(iri!(#type_iri_shape)))
83        }
84      }
85
86      impl ::linked_data_schema::LinkedDataSchema for #ident {
87        fn shacl() -> ::linked_data_schema::reexports::shacl_ast::Schema<::linked_data_schema::reexports::srdf::SRDFGraph> {
88          use ::linked_data_schema::{
89            reexports::{
90              iri_s::{iris::IriS, iri},
91              prefixmap::{PrefixMap, IriRef},
92              shacl_ast::{
93                ast::{
94                  component::Component,
95                  shape::Shape,
96                  node_shape::NodeShape,
97                  property_shape::PropertyShape,
98                  target::Target,
99                },
100                Schema,
101              },
102              srdf::{
103                RDFNode,
104                SHACLPath,
105              },
106            },
107            LinkedDataSchemaFieldVisitor,
108          };
109          use std::str::FromStr;
110          use std::collections::HashMap;
111
112          let mut prefix_map = PrefixMap::new();
113          #insert_all_prefix_mapping
114
115          let mut shapes = HashMap::default();
116
117          let rdf_node_type_iri = RDFNode::iri(IriS::from_str(#type_iri_shape).unwrap());
118
119          let property_shapes = vec![
120            #property_shapes_iris
121          ];
122
123          let node_shape = NodeShape::new(rdf_node_type_iri.clone())
124            .with_targets(vec![Target::TargetClass(RDFNode::iri(IriS::from_str(#type_iri).unwrap()))])
125            .with_closed(true)
126            .with_property_shapes(property_shapes);
127
128          let _ = shapes.insert(RDFNode::BlankNode(#struct_blank_node.to_string()), Shape::NodeShape(Box::new(node_shape)));
129
130          #(#fields)*
131
132          Schema::default()
133            .with_prefixmap(prefix_map)
134            .with_shapes(shapes)
135        }
136
137        fn components() -> Vec<::linked_data_schema::reexports::shacl_ast::ast::component::Component> {
138          use ::linked_data_schema::{
139            reexports::{
140              iri_s::iri,
141              prefixmap::IriRef,
142              shacl_ast::ast::component::Component,
143            }
144          };
145
146          vec![
147            Component::Datatype(IriRef::iri(iri!(#type_iri_shape))),
148          ]
149        }
150      }
151    })
152  }
153
154  fn generate_enum_tokens(r#enum: &RdfEnum<Self>, tokens: &mut TokenStream) {
155    let _variants = &r#enum.variants;
156    let ident = &r#enum.ident;
157
158    tokens.extend(quote::quote! {
159      impl ::linked_data_schema::LinkedDataSchema for #ident {
160        fn shacl() -> ::linked_data_schema::reexports::shacl_ast::Schema<::linked_data_schema::reexports::srdf::SRDFGraph> {
161          use ::linked_data_schema::reexports::{
162            prefixmap::PrefixMap,
163            shacl_ast::{
164              ast::shape::Shape,
165              Schema,
166            },
167            srdf::RDFNode,
168          };
169          use std::collections::HashMap;
170
171          let prefix_map = PrefixMap::new();
172          let shapes = HashMap::default();
173
174          Schema::default()
175            .with_prefixmap(prefix_map)
176            .with_shapes(shapes)
177        }
178      }
179    })
180  }
181
182  fn generate_variant_tokens(_variant: &RdfVariant<Self>, _tokens: &mut TokenStream) {
183    todo!()
184  }
185
186  fn generate_field_tokens(field: &RdfField<Self>, tokens: &mut TokenStream) {
187    if field.is_ignored() {
188      return;
189    }
190
191    //if field.is_flattened() {
192
193    //}
194
195    if let Some(predicate) = field.predicate() {
196      let identifier = Literal::string(&format!("{}Field", predicate.as_str()));
197      let predicate = Literal::string(predicate.as_str());
198
199      let field_type = &field.ty;
200
201      tokens.extend(quote::quote! {
202        let node = RDFNode::BlankNode(::linked_data_schema::reexports::uuid::Uuid::new_v4().to_string());
203
204        let rdf_node_type_iri = RDFNode::iri(IriS::from_str(#identifier).unwrap());
205
206        let property_shape = PropertyShape::new(
207          rdf_node_type_iri,
208          SHACLPath::iri(IriS::from_str(#predicate).unwrap()),
209        ).with_components(<#field_type>::field_components());
210
211        let _ = shapes.insert(node, Shape::PropertyShape(Box::new(property_shape)));
212      })
213    }
214  }
215}