Skip to main content

linked_data_core/
rdf_metadata.rs

1use std::marker::PhantomData;
2
3use oxiri::Iri;
4use proc_macro_error3::abort;
5use syn::DeriveInput;
6use syn::spanned::Spanned;
7use syn::visit::Visit;
8
9use crate::attributes::field::RdfFieldAttributes;
10use crate::attributes::r#type::{RdfEnumAttributes, RdfStructAttributes};
11use crate::attributes::variant::{PredicatePath, RdfVariantAttributes};
12use crate::prefix_mappings::PrefixMappings;
13use crate::{Error, TokenGenerator};
14
15pub enum RdfType<F> {
16  Enum(RdfEnum<F>),
17  Struct(RdfStruct<F>),
18}
19
20pub struct RdfEnum<G> {
21  attributes: RdfEnumAttributes,
22  pub ident: syn::Ident,
23  pub variants: Vec<RdfVariant<G>>,
24}
25
26pub struct RdfVariant<G> {
27  attributes: RdfVariantAttributes,
28  pub index: usize,
29  /// The variant's single field type, or `None` for a unit variant.
30  ///
31  /// A unit variant marks the enum as a closed list of RDF resources (see
32  /// [`RdfEnum::is_closed_list`]): the variant itself, rather than a wrapped value, is the leaf
33  /// RDF term, identified by its own IRI.
34  pub ty: Option<syn::Type>,
35  _generator: PhantomData<G>,
36}
37
38pub struct RdfStruct<G> {
39  attributes: RdfStructAttributes,
40  pub ident: syn::Ident,
41  pub fields: Vec<RdfField<G>>,
42}
43
44#[derive(Debug)]
45pub struct RdfField<G> {
46  attributes: RdfFieldAttributes,
47  pub ty: syn::Type,
48  _generator: PhantomData<G>,
49}
50
51pub fn unwrap_or_abort<T>(result: Result<T, Error>) -> T {
52  match result {
53    Ok(value) => value,
54    Err(error) => abort!(error.span(), error),
55  }
56}
57
58impl<G: TokenGenerator> RdfType<G> {
59  pub fn from_derive(derive_input: DeriveInput) -> Self {
60    unwrap_or_abort(Self::try_from_derive(derive_input))
61  }
62
63  fn try_from_derive(derive_input: DeriveInput) -> Result<Self, Error> {
64    match derive_input.data {
65      syn::Data::Struct(data) => {
66        let mut r#struct = RdfStruct {
67          ident: derive_input.ident,
68          attributes: derive_input.attrs.try_into()?,
69          fields: vec![],
70        };
71        r#struct.visit_data_struct(&data);
72        Ok(RdfType::Struct(r#struct))
73      }
74      syn::Data::Enum(data) => {
75        let mut r#enum = RdfEnum {
76          ident: derive_input.ident,
77          attributes: derive_input.attrs.try_into()?,
78          variants: vec![],
79        };
80        r#enum.visit_data_enum(&data);
81
82        let has_unit_variant = r#enum.variants.iter().any(|variant| variant.ty.is_none());
83        let has_data_variant = r#enum.variants.iter().any(|variant| variant.ty.is_some());
84        if has_unit_variant && has_data_variant {
85          return Err(Error::MixedVariantKinds {
86            span: data.enum_token.span(),
87          });
88        }
89
90        Ok(RdfType::Enum(r#enum))
91      }
92      syn::Data::Union(data_union) => Err(Error::UnionType {
93        span: data_union.union_token.span(),
94      }),
95    }
96  }
97}
98
99impl<G> RdfEnum<G> {
100  /// A closed list of RDF resources: every variant is a unit variant, and each one is a leaf
101  /// term identified by its own IRI (e.g. a controlled vocabulary), rather than a tagged union
102  /// wrapping heterogeneous inner values. `try_from_derive` rejects mixing unit and non-unit
103  /// variants, so checking the first variant reflects every variant.
104  pub fn is_closed_list(&self) -> bool {
105    self.variants.first().is_none_or(RdfVariant::is_unit)
106  }
107
108  pub fn prefix_mappings(&self) -> &PrefixMappings {
109    &self.attributes.prefix_mappings
110  }
111}
112
113impl<'ast, F> Visit<'ast> for RdfEnum<F> {
114  fn visit_variant(&mut self, variant: &'ast syn::Variant) {
115    let variant = unwrap_or_abort(RdfVariant::from_variant(
116      variant.clone(),
117      self.variants.len(),
118      &self.attributes.prefix_mappings,
119    ));
120    self.variants.push(variant);
121  }
122}
123
124impl<F> RdfVariant<F> {
125  fn from_variant(
126    variant: syn::Variant,
127    index: usize,
128    prefix_mappings: &PrefixMappings,
129  ) -> Result<Self, Error> {
130    let mut fields = variant.fields.iter();
131    let field = fields.next();
132
133    if let Some(extra_field) = fields.next() {
134      return Err(Error::StructVariant {
135        span: extra_field.span(),
136      });
137    }
138
139    let (inner_attrs, ty) = match field {
140      Some(field) => (field.attrs.clone(), Some(field.ty.clone())),
141      None => (vec![], None),
142    };
143
144    Ok(RdfVariant {
145      attributes: RdfVariantAttributes::try_from_attrs(
146        &variant,
147        inner_attrs,
148        variant.attrs.clone(),
149        prefix_mappings,
150      )?,
151      index,
152      ty,
153      _generator: PhantomData,
154    })
155  }
156
157  pub fn predicate_path(&self) -> &PredicatePath {
158    &self.attributes.predicate_path
159  }
160
161  /// `true` for a unit variant: a leaf member of a closed list of RDF resources, identified by
162  /// its own IRI rather than wrapping a value. See [`RdfEnum::is_closed_list`].
163  pub fn is_unit(&self) -> bool {
164    self.ty.is_none()
165  }
166}
167
168impl<G> RdfStruct<G> {
169  pub fn type_iri(&self) -> Option<&Iri<String>> {
170    self.attributes.r#type.as_ref()
171  }
172
173  pub fn prefix_mappings(&self) -> &PrefixMappings {
174    &self.attributes.prefix_mappings
175  }
176}
177
178impl<'ast, F> Visit<'ast> for RdfStruct<F> {
179  fn visit_field(&mut self, field: &'ast syn::Field) {
180    let rdf_field = unwrap_or_abort(RdfField::try_from_field(
181      field.clone(),
182      &self.attributes.prefix_mappings,
183    ));
184    self.fields.push(rdf_field);
185  }
186}
187
188impl<F> RdfField<F> {
189  fn try_from_field(field: syn::Field, prefix_mappings: &PrefixMappings) -> Result<Self, Error> {
190    let attributes = RdfFieldAttributes::try_from_attrs(field.attrs, prefix_mappings)?;
191
192    Ok(RdfField {
193      attributes,
194      ty: field.ty,
195      _generator: PhantomData,
196    })
197  }
198
199  pub fn is_flattened(&self) -> bool {
200    self.attributes.flatten
201  }
202
203  pub fn is_graph(&self) -> bool {
204    self.attributes.is_graph
205  }
206
207  pub fn is_ignored(&self) -> bool {
208    self.attributes.ignore
209  }
210
211  pub fn predicate(&self) -> Option<&Iri<String>> {
212    self.attributes.predicate.as_ref()
213  }
214
215  pub fn is_id(&self) -> bool {
216    self.attributes.is_id
217  }
218}