linked_data_schema_derive/
lib.rs1use 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.add_prefix(#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 Object::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<RDF: ::linked_data_schema::reexports::rudof_rdf::rdf_core::Rdf>() -> ::linked_data_schema::reexports::shacl_ast::ast::schema::ShaclSchema<RDF> {
88 use ::linked_data_schema::{
89 reexports::{
90 iri_s::{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 schema::ShaclSchema,
100 },
101 },
102 rudof_rdf::rdf_core::{SHACLPath, term::Object},
103 },
104 LinkedDataSchemaFieldVisitor,
105 };
106 use std::str::FromStr;
107 use std::collections::HashMap;
108
109 let mut prefix_map = PrefixMap::new();
110 #insert_all_prefix_mapping
111
112 let mut shapes = HashMap::default();
113
114 let rdf_node_type_iri = Object::Iri(IriS::from_str(#type_iri_shape).unwrap());
115
116 let property_shapes = vec![
117 #property_shapes_iris
118 ];
119
120 let node_shape = NodeShape::new(rdf_node_type_iri.clone())
121 .with_targets(vec![Target::Class(Object::Iri(IriS::from_str(#type_iri).unwrap()))])
122 .with_property_shapes(property_shapes);
123
124 let _ = shapes.insert(Object::BlankNode(#struct_blank_node.to_string()), Shape::NodeShape(Box::new(node_shape)));
125
126 #(#fields)*
127
128 ShaclSchema::new()
129 .with_prefixmap(prefix_map)
130 .with_shapes(shapes)
131 }
132
133 fn components() -> Vec<::linked_data_schema::reexports::shacl_ast::ast::component::Component> {
134 use ::linked_data_schema::{
135 reexports::{
136 iri_s::iri,
137 prefixmap::IriRef,
138 shacl_ast::ast::component::Component,
139 }
140 };
141
142 vec![
143 Component::Datatype(IriRef::iri(iri!(#type_iri_shape))),
144 ]
145 }
146 }
147 })
148 }
149
150 fn generate_enum_tokens(r#enum: &RdfEnum<Self>, tokens: &mut TokenStream) {
151 let _variants = &r#enum.variants;
152 let ident = &r#enum.ident;
153
154 tokens.extend(quote::quote! {
155 impl ::linked_data_schema::LinkedDataSchema for #ident {
156 fn shacl<RDF: ::linked_data_schema::reexports::rudof_rdf::rdf_core::Rdf>() -> ::linked_data_schema::reexports::shacl_ast::ast::schema::ShaclSchema<RDF> {
157 use ::linked_data_schema::reexports::{
158 prefixmap::PrefixMap,
159 shacl_ast::ast::{
160 shape::Shape,
161 schema::ShaclSchema,
162 },
163 rudof_rdf::rdf_core::term::Object,
164 };
165 use std::collections::HashMap;
166
167 let prefix_map = PrefixMap::new();
168 let shapes = HashMap::default();
169
170 ShaclSchema::new()
171 .with_prefixmap(prefix_map)
172 .with_shapes(shapes)
173 }
174 }
175 })
176 }
177
178 fn generate_variant_tokens(_variant: &RdfVariant<Self>, _tokens: &mut TokenStream) {
179 todo!()
180 }
181
182 fn generate_field_tokens(field: &RdfField<Self>, tokens: &mut TokenStream) {
183 if field.is_ignored() {
184 return;
185 }
186
187 if let Some(predicate) = field.predicate() {
192 let identifier = Literal::string(&format!("{}Field", predicate.as_str()));
193 let predicate = Literal::string(predicate.as_str());
194
195 let field_type = &field.ty;
196
197 tokens.extend(quote::quote! {
198 let node = Object::BlankNode(::linked_data_schema::reexports::uuid::Uuid::new_v4().to_string());
199
200 let rdf_node_type_iri = Object::Iri(IriS::from_str(#identifier).unwrap());
201
202 let property_shape = PropertyShape::new(
203 rdf_node_type_iri,
204 SHACLPath::iri(IriS::from_str(#predicate).unwrap()),
205 ).with_components(<#field_type>::field_components());
206
207 let _ = shapes.insert(node, Shape::PropertyShape(Box::new(property_shape)));
208 })
209 }
210 }
211}