Skip to content

Commit

Permalink
Add required product layout fields. (#174)
Browse files Browse the repository at this point in the history
Fixes #172
  • Loading branch information
timothee-haudebourg authored Mar 13, 2024
1 parent a6d3949 commit 3e9fe97
Show file tree
Hide file tree
Showing 31 changed files with 634 additions and 299 deletions.
12 changes: 8 additions & 4 deletions generators/rust/treeldr-rs-macros/src/generate/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,13 @@ pub fn generate(input: DeriveInput) -> Result<TokenStream, Error> {
}
});

let unwrap_fields = layout.fields.keys().map(|name| {
let unwrap_fields = layout.fields.iter().map(|(name, f)| {
let ident = syn::Ident::new(name, Span::call_site());
quote!(#ident: data.#ident.ok_or_else(|| ::treeldr::DeserializeError::MissingField(#name.to_owned()))?)
if f.required {
quote!(#ident: data.#ident.ok_or_else(|| ::treeldr::DeserializeError::MissingField(#name.to_owned()))?)
} else {
quote!(#ident: data.#ident)
}
});

quote! {
Expand Down Expand Up @@ -715,7 +719,7 @@ fn generate_data(
for l in rdf.interpretation.literals_of(&resource) {
has_literal = true;
let literal = rdf.vocabulary.literal(l).unwrap();
let ty_iri = match literal.type_() {
let ty_iri = match &literal.type_ {
::treeldr::rdf_types::LiteralType::Any(i) => {
rdf.vocabulary.iri(i).unwrap()
},
Expand All @@ -725,7 +729,7 @@ fn generate_data(
};

if ty_iri == expected_ty_iri {
if let Ok(value) = ::treeldr::de::FromRdfLiteral::from_rdf_literal(literal.value().as_str()) {
if let Ok(value) = ::treeldr::de::FromRdfLiteral::from_rdf_literal(&literal.value) {
if result.replace(value).is_some() {
return Err(::treeldr::DeserializeError::AmbiguousLiteralValue)
}
Expand Down
49 changes: 34 additions & 15 deletions generators/rust/treeldr-rs-macros/src/generate/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ pub fn generate(input: DeriveInput) -> Result<TokenStream, Error> {
match ::treeldr::AsId::as_id(self) {
::treeldr::rdf_types::Id::Iri(value) => {
let id = rdf.vocabulary.insert(value);
rdf.interpretation.assign_iri(inputs[0].clone(), id);
rdf.interpretation.assign_iri(&inputs[0], id);
Ok(())
}
::treeldr::rdf_types::Id::Blank(value) => {
let id = rdf.vocabulary.insert_blank_id(value);
rdf.interpretation.assign_blank_id(inputs[0].clone(), id);
rdf.interpretation.assign_blank_id(&inputs[0], id);
Ok(())
}
}
Expand Down Expand Up @@ -179,17 +179,33 @@ pub fn generate(input: DeriveInput) -> Result<TokenStream, Error> {

let m = field.value.input.len();

quote! {
{
let env = env.intro(rdf, #field_intro);
env.instantiate_dataset(&#field_dataset, output);
<#field_layout as ::treeldr::SerializeLd<#m, V, I>>::serialize_ld_with(
&self.#field_ident,
rdf,
&env.instantiate_patterns(&#field_inputs),
#field_graph.as_ref(),
output
)?;
if field.required {
quote! {
{
let env = env.intro(rdf, #field_intro);
env.instantiate_dataset(&#field_dataset, output);
<#field_layout as ::treeldr::SerializeLd<#m, V, I>>::serialize_ld_with(
&self.#field_ident,
rdf,
&env.instantiate_patterns(&#field_inputs),
#field_graph.as_ref(),
output
)?;
}
}
} else {
quote! {
if let Some(value) = &self.#field_ident {
let env = env.intro(rdf, #field_intro);
env.instantiate_dataset(&#field_dataset, output);
<#field_layout as ::treeldr::SerializeLd<#m, V, I>>::serialize_ld_with(
value,
rdf,
&env.instantiate_patterns(&#field_inputs),
#field_graph.as_ref(),
output
)?;
}
}
}
});
Expand Down Expand Up @@ -457,9 +473,12 @@ fn pattern_interpretation(pattern: &Pattern<Term>) -> TokenStream {
match pattern {
Pattern::Var(i) => {
let i = *i as usize;
quote!(inputs[#i].clone())
quote!(&inputs[#i])
}
Pattern::Resource(term) => {
let term = term_interpretation(term);
quote!(&#term)
}
Pattern::Resource(term) => term_interpretation(term),
}
}

Expand Down
109 changes: 59 additions & 50 deletions generators/rust/treeldr-rs-macros/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,18 @@ pub fn parse(input: syn::DeriveInput) -> Result<ParsedInput, Error> {
.map(|f| {
let name = f.ident.unwrap().to_string();
let attrs = ComponentAttributes::parse(f.attrs)?;
let ty = FieldType::new(f.ty);
let required = ty.is_required();
let field = Field {
intro: attrs.intro.map(Into::into).unwrap_or_default(),
dataset: attrs.dataset.unwrap_or_default(),
property: attrs.property,
value: ValueFormatOrLayout::Format(ValueFormat {
layout: type_map.insert(f.ty).into(),
layout: type_map.insert(ty.into_type()).into(),
input: attrs.input.map(Into::into).unwrap_or_default(),
graph: attrs.graph.unwrap_or_default().into(),
}),
required,
};

Ok((name, field))
Expand Down Expand Up @@ -637,55 +640,61 @@ impl ComponentAttributes {
}
}

// pub enum FieldType {
// Optional(syn::Type),
// Required(syn::Type)
// }

// impl FieldType {
// pub fn new(ty: syn::Type) -> Self {
// if is_option_type(&ty) {
// let syn::Type::Path(path) = ty else { unreachable!() };
// let syn::PathArguments::AngleBracketed(args) = path.path.segments.into_iter().next().unwrap().arguments else { unreachable!() };
// let syn::GenericArgument::Type(item) = args.args.into_iter().next().unwrap() else { unreachable!() };
// Self::Optional(item)
// } else {
// Self::Required(ty)
// }
// }

// pub fn is_required(&self) -> bool {
// matches!(self, Self::Required(_))
// }

// pub fn into_type(self) -> syn::Type {
// match self {
// Self::Required(ty) => ty,
// Self::Optional(ty) => ty
// }
// }
// }

// fn is_option_type(ty: &syn::Type) -> bool {
// if let syn::Type::Path(path) = ty {
// if path.qself.is_none() {
// if path.path.segments.len() == 1 {
// let segment = path.path.segments.iter().next().unwrap();
// if segment.ident == "Option" {
// if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
// if args.args.len() == 1 {
// if let syn::GenericArgument::Type(_) = args.args.iter().next().unwrap() {
// return true
// }
// }
// }
// }
// }
// }
// }

// false
// }
pub enum FieldType {
Optional(syn::Type),
Required(syn::Type),
}

impl FieldType {
pub fn new(ty: syn::Type) -> Self {
if is_option_type(&ty) {
let syn::Type::Path(path) = ty else {
unreachable!()
};
let syn::PathArguments::AngleBracketed(args) =
path.path.segments.into_iter().next().unwrap().arguments
else {
unreachable!()
};
let syn::GenericArgument::Type(item) = args.args.into_iter().next().unwrap() else {
unreachable!()
};
Self::Optional(item)
} else {
Self::Required(ty)
}
}

pub fn is_required(&self) -> bool {
matches!(self, Self::Required(_))
}

pub fn into_type(self) -> syn::Type {
match self {
Self::Required(ty) => ty,
Self::Optional(ty) => ty,
}
}
}

fn is_option_type(ty: &syn::Type) -> bool {
if let syn::Type::Path(path) = ty {
if path.qself.is_none() && path.path.segments.len() == 1 {
let segment = path.path.segments.iter().next().unwrap();
if segment.ident == "Option" {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
if args.args.len() == 1 {
if let syn::GenericArgument::Type(_) = args.args.iter().next().unwrap() {
return true;
}
}
}
}
}
}

false
}

fn extract_vec_item(ty: syn::Type) -> Result<syn::Type, Error> {
let span = ty.span();
Expand Down
4 changes: 2 additions & 2 deletions generators/rust/treeldr-rs/tests/id.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn id() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
Expand All @@ -7,7 +7,7 @@ fn id() {

impl treeldr::AsId for Id {
fn as_id(&self) -> rdf_types::Id<&iref::Iri, &rdf_types::BlankId> {
self.0.as_id_ref()
self.0.as_lexical_id_ref()
}
}

Expand Down
6 changes: 3 additions & 3 deletions generators/rust/treeldr-rs/tests/list.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn list_unordered() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
#[tldr(set)]
pub struct UnorderedList(Vec<String>);
}

#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn list_ordered() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
#[tldr(list)]
pub struct UnorderedList(Vec<String>);
}

#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn list_sized() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
Expand Down
8 changes: 4 additions & 4 deletions generators/rust/treeldr-rs/tests/literal.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn literal_unit() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
pub struct Unit;
}

#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn literal_boolean() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
#[tldr(boolean)]
pub struct Boolean(bool);
}

#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn literal_i32() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
Expand All @@ -22,7 +22,7 @@ fn literal_i32() {
pub struct I32(i32);
}

#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn literal_string() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
Expand Down
5 changes: 4 additions & 1 deletion generators/rust/treeldr-rs/tests/record.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn record() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
#[tldr(prefix("ex" = "http://example.org/#"))]
pub struct Record {
#[tldr("ex:foo")]
foo: String,

#[tldr("ex:bar")]
optional: Option<String>,
}
}
2 changes: 1 addition & 1 deletion generators/rust/treeldr-rs/tests/sum.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[cfg(feature = "derive")]
#[cfg(feature = "macros")]
#[test]
fn sum() {
#[derive(treeldr::SerializeLd, treeldr::DeserializeLd)]
Expand Down
5 changes: 4 additions & 1 deletion layouts/book/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
title = "TreeLDR Layouts"

[build]
build-dir = "target"
build-dir = "target"

[preprocessor.graphviz]
command = "mdbook-graphviz"
4 changes: 2 additions & 2 deletions layouts/book/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

TreeLDR Layouts are a data serialization and deserialization tool for the
Resource Description Framework (RDF).
It can be used to convert RDF datasets into tree-like values (such as JSON),
It can be used to convert RDF graphs into tree-like values (such as JSON),
and back.
The idea behind layouts is simple: each layout describes the expected shape of a
tree value.
This shape can be either a record (sometimes called object, in JSON for
instance), a list, a number, etc. Each part of this shape is then associated to
a fraction of the represented RDF dataset.
a subset of the represented RDF dataset.

## Basic layout

Expand Down
16 changes: 9 additions & 7 deletions layouts/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@

- [RDF Basics](rdf-basics.md)
- [Data Model](data-model/README.md)
- [Values](data-model/values.md)
- [Literals](data-model/literals.md)
- [Records (Objects)](data-model/records.md)
- [Lists](data-model/lists.md)
- [Types](data-model/types.md)
- [Paths](data-model/paths.md)
<!-- - [Paths](data-model/paths.md) -->
- [Layouts](layouts/README.md)
- [Literals](layouts/literals.md)
- [Product (Record)](layouts/record.md)
- [Sum](layouts/sum.md)
- [Literals](layouts/literals.md)
- [Lists](layouts/lists.md)
- [Functional Layouts](layouts/functional-layouts.md)
- [Abstract Layouts](abstract-layouts/README.md)
<!-- - [Functional Layouts](layouts/functional-layouts.md) -->
<!-- - [Abstract Layouts](abstract-layouts/README.md)
- [Intersection](abstract-layouts/intersection.md)
- [Union](abstract-layouts/union.md)
- [Abstract Syntax](abstract-syntax/README.md)
- [Union](abstract-layouts/union.md) -->
<!-- - [Abstract Syntax](abstract-syntax/README.md) -->
- [Algorithms](algorithms/README.md)
- [Serialization](algorithms/serialization.md)
- [Deserialization](algorithms/deserialization.md)
Expand Down
Loading

0 comments on commit 3e9fe97

Please sign in to comment.