Skip to content

Commit

Permalink
Allow arbitrary map keys. (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
timothee-haudebourg authored Apr 5, 2024
1 parent ef13480 commit 90d53f7
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 134 deletions.
31 changes: 17 additions & 14 deletions generators/rust/generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use syn::spanned::Spanned;
use treeldr_layouts::{
distill::RdfContext,
layout::{DataLayout, LayoutType, ListLayout, LiteralLayout},
Layout, Layouts, Pattern, Ref,
Layout, Layouts, Literal, Pattern, Ref, Value,
};
use utils::ident_from_iri;

Expand All @@ -27,7 +27,7 @@ pub enum Error<R = Term> {
MissingTypeIdentifier(R),

#[error("invalid field identifier `{0}`")]
InvalidFieldIdent(String),
InvalidFieldIdent(Value),

#[error("invalid variant identifier `{0}`")]
InvalidVariantIdent(String),
Expand Down Expand Up @@ -364,20 +364,23 @@ where
let fields = layout
.fields
.iter()
.map(|(name, f)| {
let f_ident = syn::parse_str::<syn::Ident>(name.as_str())
.map_err(|_| Error::InvalidFieldIdent(name.clone()))?;
.map(|(key, f)| match key {
Value::Literal(Literal::TextString(name)) => {
let f_ident = syn::parse_str::<syn::Ident>(name.as_str())
.map_err(|_| Error::InvalidFieldIdent(key.clone()))?;

let intro = generate_intro_attribute(f.intro, layout.input + layout.intro);
let dataset = generate_dataset_attribute(rdf, &f.dataset)?;
let input = generate_value_input_attribute(rdf, &f.value.input)?;
let graph = generate_value_graph_attribute(rdf, &f.value.graph)?;
let layout = options.layout_ref(rdf, &f.value.layout)?;
let intro = generate_intro_attribute(f.intro, layout.input + layout.intro);
let dataset = generate_dataset_attribute(rdf, &f.dataset)?;
let input = generate_value_input_attribute(rdf, &f.value.input)?;
let graph = generate_value_graph_attribute(rdf, &f.value.graph)?;
let layout = options.layout_ref(rdf, &f.value.layout)?;

Ok(quote! {
#[tldr(#intro, #dataset, #input, #graph)]
#f_ident : #layout
})
Ok(quote! {
#[tldr(#intro, #dataset, #input, #graph)]
#f_ident : #layout
})
}
other => Err(Error::InvalidFieldIdent(other.clone())),
})
.collect::<Result<Vec<_>, _>>()?;

Expand Down
21 changes: 17 additions & 4 deletions generators/rust/treeldr-rs-macros/src/generate/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rdf_types::{dataset::TraversableDataset, Id, LiteralType, Term};
use syn::DeriveInput;
use treeldr_layouts::{
layout::{DataLayout, ListLayout, LiteralLayout},
Dataset, Layout, Pattern,
Dataset, Layout, Literal, Pattern, Value,
};

use crate::parse::parse;
Expand All @@ -17,6 +17,9 @@ pub enum Error {
#[error(transparent)]
Build(#[from] treeldr_layouts::abs::syntax::BuildError),

#[error("invalid field ident `{0}`")]
InvalidFieldIdent(Value),

#[error("invalid datatype `{0}`")]
InvalidDatatype(String),
}
Expand All @@ -26,6 +29,7 @@ impl Error {
match self {
Self::Parse(e) => e.span(),
Self::Build(_) => Span::call_site(),
Self::InvalidFieldIdent(_) => Span::call_site(),
Self::InvalidDatatype(_) => Span::call_site(),
}
}
Expand Down Expand Up @@ -157,7 +161,16 @@ pub fn generate(input: DeriveInput) -> Result<TokenStream, Error> {
let intro = layout.intro;
let dataset = dataset_to_array(&layout.dataset);

let opt_fields = layout.fields.iter().map(|(name, field)| {
let layout_fields: Vec<_> = layout
.fields
.iter()
.map(|(key, field)| match key {
Value::Literal(Literal::TextString(name)) => Ok((name, field)),
_ => Err(Error::InvalidFieldIdent(key.clone())),
})
.collect::<Result<_, _>>()?;

let opt_fields = layout_fields.iter().copied().map(|(name, field)| {
let ident = syn::Ident::new(name, Span::call_site());
let ty = input
.type_map
Expand All @@ -166,7 +179,7 @@ pub fn generate(input: DeriveInput) -> Result<TokenStream, Error> {
quote!(#ident : Option<#ty>)
});

let deserialize_fields = layout.fields.iter().map(|(name, field)| {
let deserialize_fields = layout_fields.iter().copied().map(|(name, field)| {
let field_ident = syn::Ident::new(name, Span::call_site());
let field_ty = input
.type_map
Expand Down Expand Up @@ -214,7 +227,7 @@ pub fn generate(input: DeriveInput) -> Result<TokenStream, Error> {
}
});

let unwrap_fields = layout.fields.iter().map(|(name, f)| {
let unwrap_fields = layout_fields.iter().copied().map(|(name, f)| {
let ident = syn::Ident::new(name, Span::call_site());
if f.required {
quote!(#ident: data.#ident.ok_or_else(|| ::treeldr::DeserializeError::MissingField(#name.to_owned()))?)
Expand Down
109 changes: 60 additions & 49 deletions generators/rust/treeldr-rs-macros/src/generate/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rdf_types::{dataset::TraversableDataset, Id, LiteralType, Term};
use syn::DeriveInput;
use treeldr_layouts::{
layout::{DataLayout, ListLayout, LiteralLayout},
Dataset, Layout, Pattern,
Dataset, Layout, Pattern, Value,
};

use crate::parse::parse;
Expand All @@ -17,6 +17,9 @@ pub enum Error {
#[error(transparent)]
Build(#[from] treeldr_layouts::abs::syntax::BuildError),

#[error("invalid field ident `{0}`")]
InvalidFieldIdent(Value),

#[error("invalid datatype `{0}`")]
InvalidDatatype(String),
}
Expand All @@ -26,6 +29,7 @@ impl Error {
match self {
Self::Parse(e) => e.span(),
Self::Build(_) => Span::call_site(),
Self::InvalidFieldIdent(_) => Span::call_site(),
Self::InvalidDatatype(_) => Span::call_site(),
}
}
Expand Down Expand Up @@ -159,56 +163,63 @@ pub fn generate(input: DeriveInput) -> Result<TokenStream, Error> {
let intro = layout.intro;
let dataset = dataset_to_array(&layout.dataset);

let fields = layout.fields.iter().map(|(name, field)| {
let field_ident = syn::Ident::new(name, Span::call_site());
let field_intro = field.intro;
let field_dataset = dataset_to_array(&field.dataset);
let field_layout = input
.type_map
.get(field.value.layout.id().as_iri().unwrap())
.unwrap();
let field_inputs = inputs_to_array(&field.value.input);
let field_graph = match &field.value.graph {
Some(None) => quote!(None),
Some(Some(g)) => {
let g = generate_pattern(g);
quote!(Some(env.instantiate_pattern(#g)))
}
None => quote!(current_graph.cloned()),
};

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

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
)?;
let fields: Vec<_> = layout
.fields
.iter()
.map(|(key, field)| {
let name = key
.as_str()
.ok_or_else(|| Error::InvalidFieldIdent(key.clone()))?;
let field_ident = syn::Ident::new(name, Span::call_site());
let field_intro = field.intro;
let field_dataset = dataset_to_array(&field.dataset);
let field_layout = input
.type_map
.get(field.value.layout.id().as_iri().unwrap())
.unwrap();
let field_inputs = inputs_to_array(&field.value.input);
let field_graph = match &field.value.graph {
Some(None) => quote!(None),
Some(Some(g)) => {
let g = generate_pattern(g);
quote!(Some(env.instantiate_pattern(#g)))
}
None => quote!(current_graph.cloned()),
};

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

if field.required {
Ok(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 {
Ok(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
)?;
}
})
}
}
});
})
.collect::<Result<_, Error>>()?;

quote! {
let env = env.intro(rdf, #intro);
Expand Down
21 changes: 12 additions & 9 deletions generators/rust/treeldr-rs-macros/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ use iref::{Iri, IriBuf, IriRefBuf};
use proc_macro2::{Span, TokenStream, TokenTree};
use rdf_types::BlankIdBuf;
use syn::spanned::Spanned;
use treeldr_layouts::abs::syntax::{
BooleanLayout, ByteStringLayout, CompactIri, DataLayout, Dataset, ExtraProperties, Field,
IdLayout, Layout, LayoutHeader, ListItem, ListLayout, ListNode, ListNodeOrLayout,
LiteralLayout, NumberLayout, OrderedListLayout, Pattern, ProductLayout, Quad, SizedListLayout,
SumLayout, TextStringLayout, UnitLayout, UnorderedListLayout, ValueFormat, ValueFormatOrLayout,
VariableNameBuf, Variant, VariantFormat, VariantFormatOrLayout,
use treeldr_layouts::{
abs::syntax::{
BooleanLayout, ByteStringLayout, CompactIri, DataLayout, Dataset, ExtraProperties, Field,
IdLayout, Layout, LayoutHeader, ListItem, ListLayout, ListNode, ListNodeOrLayout,
LiteralLayout, NumberLayout, OrderedListLayout, Pattern, ProductLayout, Quad,
SizedListLayout, SumLayout, TextStringLayout, UnitLayout, UnorderedListLayout, ValueFormat,
ValueFormatOrLayout, VariableNameBuf, Variant, VariantFormat, VariantFormatOrLayout,
},
Value,
};

#[derive(Default)]
Expand Down Expand Up @@ -205,7 +208,7 @@ pub fn parse(input: syn::DeriveInput) -> Result<ParsedInput, Error> {
},
))),
Kind::Record => {
let fields = match input.data {
let entries = match input.data {
syn::Data::Struct(s) => match s.fields {
syn::Fields::Named(fields) => fields
.named
Expand All @@ -227,7 +230,7 @@ pub fn parse(input: syn::DeriveInput) -> Result<ParsedInput, Error> {
required,
};

Ok((name, field))
Ok((Value::string(name), field))
})
.collect::<Result<BTreeMap<_, _>, _>>()?,
f => return Err(Error::ExpectedNamedFields(f.span())),
Expand All @@ -246,7 +249,7 @@ pub fn parse(input: syn::DeriveInput) -> Result<ParsedInput, Error> {
dataset: type_attrs.dataset.unwrap_or_default(),
extra: type_attrs.extra.unwrap_or_default(),
},
fields,
fields: entries,
})
}
Kind::Sum => {
Expand Down
23 changes: 15 additions & 8 deletions layouts/src/abs/syntax/layout/product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ use std::collections::BTreeMap;
use json_syntax::{TryFromJson, TryFromJsonObject};
use serde::{Deserialize, Serialize};

use crate::abs::{
self,
syntax::{
check_type, expect_object, get_entry, require_entry, Build, BuildError, Context, Dataset,
Error, ObjectUnusedEntries, Pattern, Scope, ValueFormatOrLayout, ValueIntro,
use crate::{
abs::{
self,
syntax::{
check_type, expect_object, get_entry, require_entry, Build, BuildError, Context,
Dataset, Error, ObjectUnusedEntries, Pattern, Scope, ValueFormatOrLayout, ValueIntro,
},
},
Value,
};

use super::{LayoutHeader, ProductLayoutType};
Expand All @@ -23,7 +26,7 @@ pub struct ProductLayout {
pub header: LayoutHeader,

#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub fields: BTreeMap<String, Field>,
pub fields: BTreeMap<Value, Field>,
}

impl TryFromJson for ProductLayout {
Expand Down Expand Up @@ -55,6 +58,8 @@ impl TryFromJsonObject for ProductLayout {
code_map,
offset,
)?;
let fields: BTreeMap<String, Field> =
get_entry(object, "fields", &mut unused_entries, code_map, offset)?.unwrap_or_default();
let result = Self {
type_: ProductLayoutType,
header: LayoutHeader::try_from_json_object_at(
Expand All @@ -63,8 +68,10 @@ impl TryFromJsonObject for ProductLayout {
code_map,
offset,
)?,
fields: get_entry(object, "fields", &mut unused_entries, code_map, offset)?
.unwrap_or_default(),
fields: fields
.into_iter()
.map(|(k, v)| (Value::string(k), v))
.collect(),
};
unused_entries.check()?;
Ok(result)
Expand Down
Loading

0 comments on commit 90d53f7

Please sign in to comment.