Skip to content

Commit d50c79e

Browse files
GordianDziwisMarcAntoine-Arnaud
authored andcommitted
feat: initial version
1 parent 094c537 commit d50c79e

File tree

13 files changed

+1258
-0
lines changed

13 files changed

+1258
-0
lines changed

.github/workflows/ci.yml

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
10+
jobs:
11+
12+
test:
13+
name: Test Suite
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
- name: Install Rust toolchain
19+
uses: actions-rs/toolchain@v1
20+
with:
21+
toolchain: 1.86.0
22+
profile: minimal
23+
override: true
24+
- uses: Swatinem/rust-cache@v2
25+
- uses: actions-rs/cargo@v1
26+
with:
27+
command: test
28+
args: --all-features --workspace
29+
30+
rustfmt:
31+
name: Rustfmt
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Checkout repository
35+
uses: actions/checkout@v4
36+
- name: Install Rust toolchain
37+
uses: actions-rs/toolchain@v1
38+
with:
39+
toolchain: 1.86.0
40+
profile: minimal
41+
override: true
42+
components: rustfmt
43+
- uses: Swatinem/rust-cache@v2
44+
- name: Check formatting
45+
uses: actions-rs/cargo@v1
46+
with:
47+
command: fmt
48+
args: --all -- --check
49+
50+
clippy:
51+
name: Clippy
52+
runs-on: ubuntu-latest
53+
steps:
54+
- name: Checkout repository
55+
uses: actions/checkout@v4
56+
- name: Install Rust toolchain
57+
uses: actions-rs/toolchain@v1
58+
with:
59+
toolchain: 1.86.0
60+
profile: minimal
61+
override: true
62+
components: clippy
63+
- uses: Swatinem/rust-cache@v2
64+
- name: Clippy check
65+
uses: actions-rs/cargo@v1
66+
with:
67+
command: clippy
68+
args: --all-targets --all-features --workspace -- -D warnings
69+
70+
docs:
71+
name: Docs
72+
runs-on: ubuntu-latest
73+
steps:
74+
- name: Checkout repository
75+
uses: actions/checkout@v4
76+
- name: Install Rust toolchain
77+
uses: actions-rs/toolchain@v1
78+
with:
79+
toolchain: 1.86.0
80+
profile: minimal
81+
override: true
82+
- uses: Swatinem/rust-cache@v2
83+
- name: Check documentation
84+
env:
85+
RUSTDOCFLAGS: -D warnings
86+
uses: actions-rs/cargo@v1
87+
with:
88+
command: doc
89+
args: --no-deps --document-private-items --all-features --workspace --examples
90+

.gitignore

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Cargo
2+
# will have compiled files and executables
3+
debug/
4+
target/
5+
6+
Cargo.lock
7+
8+
# These are backup files generated by rustfmt
9+
**/*.rs.bk
10+
11+
# MSVC Windows builds of rustc generate these, which store debugging information
12+
*.pdb
13+
14+
# RustRover
15+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
16+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
17+
# and can be added to the global gitignore or merged into this file. For a more nuclear
18+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
19+
#.idea/

Cargo.toml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "linked-data-core"
3+
description = "Core library for Linked Data crates"
4+
homepage = "https://github.com/luminvent/linked-data-core"
5+
repository = "https://github.com/luminvent/linked-data-core"
6+
version = "0.0.1"
7+
edition = "2024"
8+
license = "MIT"
9+
10+
[dependencies]
11+
iref = { version = "3", features = ["serde"] }
12+
proc-macro-error = "1"
13+
proc-macro2 = "1"
14+
quote = "1"
15+
snafu = "0.8"
16+
syn = { version = "2", features = ["visit", "extra-traits"] }

rustfmt.toml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tab_spaces = 2

src/attributes.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use iref::IriBuf;
2+
use snafu::ResultExt;
3+
use syn::{Attribute, LitStr};
4+
5+
use crate::{Error, InvalidIriSnafu};
6+
7+
mod ast;
8+
pub mod field;
9+
mod parse;
10+
pub mod r#type;
11+
pub mod variant;
12+
13+
fn parse_ld_attributes<T: syn::parse::Parse>(attrs: &[Attribute]) -> Result<Vec<T>, Error> {
14+
Ok(
15+
attrs
16+
.iter()
17+
.filter(|attr| attr.path().is_ident("ld"))
18+
.map(|attr| attr.parse_args::<T>())
19+
.collect::<Result<_, _>>()?,
20+
)
21+
}
22+
23+
fn parse_iri(lit_iri: LitStr) -> Result<IriBuf, Error> {
24+
IriBuf::new(lit_iri.value()).context(InvalidIriSnafu {
25+
span: lit_iri.span(),
26+
})
27+
}

src/attributes/ast.rs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use syn::LitStr;
2+
3+
/// Represents attribute contents that can be parsed from #[ld(...)] on structs.
4+
///
5+
/// Possible formats:
6+
/// - type = "http://example.org/Person"
7+
/// - prefix("ex" = "http://example.org/")
8+
#[derive(Debug)]
9+
pub enum StructAttribute {
10+
Type(TypeAttribute),
11+
Prefix(PrefixAttribute),
12+
}
13+
14+
/// Represents attribute contents that can be parsed from #[ld(...)] on enums.
15+
///
16+
/// Possible formats:
17+
/// - prefix("ex" = "http://example.org/")
18+
#[derive(Debug)]
19+
pub enum EnumAttribute {
20+
Prefix(PrefixAttribute),
21+
}
22+
23+
/// Represents attribute contents that can be parsed from #[ld(...)] on enum variants.
24+
///
25+
/// Possible formats:
26+
/// - "http://example.org/property"
27+
#[derive(Debug)]
28+
pub enum VariantAttribute {
29+
Iri(LitStr),
30+
}
31+
32+
/// Represents attribute contents that can be parsed from #[ld(...)] on struct fields.
33+
///
34+
/// Possible formats:
35+
/// - ignore
36+
/// - "http://example.org/property"
37+
/// - flatten
38+
/// - id
39+
/// - type
40+
/// - graph
41+
#[derive(Debug)]
42+
pub enum FieldAttribute {
43+
/// Marks the field to be ignored during serialization/deserialization
44+
Ignore,
45+
/// Specifies the IRI for the field
46+
Iri(LitStr),
47+
/// Indicates that field's contents should be flattened
48+
Flatten,
49+
/// Marks the field as an ID field
50+
Id,
51+
/// Marks the field as a graph value
52+
Graph,
53+
}
54+
55+
/// Represents a type attribute value.
56+
///
57+
/// Format: type = "http://example.org/Type" or type = "prefix:Type"
58+
#[derive(Debug)]
59+
pub struct TypeAttribute {
60+
pub identifier: LitStr,
61+
}
62+
63+
/// Represents a prefix attribute value.
64+
///
65+
/// Format: prefix("ex" = "http://example.org/")
66+
#[derive(Debug)]
67+
pub struct PrefixAttribute {
68+
pub mapping: PrefixMapping,
69+
}
70+
71+
/// Represents a prefix mapping with a prefix and an IRI.
72+
///
73+
/// Format: "ex" = "http://example.org/"
74+
#[derive(Debug)]
75+
pub struct PrefixMapping {
76+
pub prefix: LitStr,
77+
pub iri: LitStr,
78+
}

src/attributes/field.rs

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use iref::IriBuf;
2+
use snafu::ResultExt;
3+
4+
use crate::attributes::ast::FieldAttribute;
5+
use crate::attributes::parse_ld_attributes;
6+
use crate::prefix_mappings::PrefixMappings;
7+
use crate::{Error, InvalidMappingSnafu};
8+
9+
#[derive(Debug, Default)]
10+
pub struct RdfFieldAttributes {
11+
pub flatten: bool,
12+
pub is_graph: bool,
13+
pub ignore: bool,
14+
pub predicate: Option<IriBuf>,
15+
pub is_id: bool,
16+
}
17+
18+
impl RdfFieldAttributes {
19+
pub fn try_from_attrs(
20+
attrs: Vec<syn::Attribute>,
21+
prefix_mappings: &PrefixMappings,
22+
) -> Result<Self, Error> {
23+
let field_attrs = parse_ld_attributes(&attrs)?;
24+
25+
let mut attributes = RdfFieldAttributes::default();
26+
27+
for attr in field_attrs {
28+
match attr {
29+
FieldAttribute::Ignore => {
30+
attributes.ignore = true;
31+
}
32+
FieldAttribute::Iri(lit_str) => {
33+
if attributes.predicate.is_some() {
34+
return Err(Error::MultipleIris {
35+
span: lit_str.span(),
36+
});
37+
}
38+
let iri = prefix_mappings
39+
.expand(lit_str.value())
40+
.context(InvalidMappingSnafu {
41+
span: lit_str.span(),
42+
})?;
43+
attributes.predicate = Some(iri);
44+
}
45+
FieldAttribute::Flatten => {
46+
attributes.flatten = true;
47+
}
48+
FieldAttribute::Id => {
49+
attributes.is_id = true;
50+
}
51+
52+
FieldAttribute::Graph => {
53+
attributes.is_graph = true;
54+
}
55+
}
56+
}
57+
58+
Ok(attributes)
59+
}
60+
}

0 commit comments

Comments
 (0)