Skip to content

Commit

Permalink
sophia_resource now supports more formats
Browse files Browse the repository at this point in the history
  • Loading branch information
pchampin committed Oct 24, 2023
1 parent 1ca0a5d commit 26fc597
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 7 deletions.
9 changes: 9 additions & 0 deletions resource/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,20 @@ keywords.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
# This feature enables the JSON-LD parser and serializer
jsonld = ["sophia_jsonld"]
# This feature enables the RDF/XML parser and serializer
xml = ["sophia_xml"]
# This feature enables the HTTP client in dependencies
http_client = []


[dependencies]
sophia_api.workspace = true
sophia_iri.workspace = true
sophia_jsonld = { workspace = true, optional = true }
sophia_turtle.workspace = true
sophia_xml = { workspace = true, optional = true }
thiserror.workspace = true

[dev-dependencies]
18 changes: 18 additions & 0 deletions resource/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,20 @@ mod test {
pub const F2R1: Iri<&str> = Iri::new_unchecked_const("http://example.org/file2.ttl#res1");
pub const F2R2: Iri<&str> = Iri::new_unchecked_const("http://example.org/file2.ttl#res2");
pub const FAIL: Iri<&str> = Iri::new_unchecked_const("http://example.org/not_there");
pub const F3: Iri<&str> = Iri::new_unchecked_const("http://example.org/file3.nt");
#[cfg(feature = "jsonld")]
pub const F4: Iri<&str> = Iri::new_unchecked_const("http://example.org/file4.jsonld");
#[cfg(feature = "xml")]
pub const F5: Iri<&str> = Iri::new_unchecked_const("http://example.org/file5.rdf");
pub const SUBDIR: Iri<&str> = Iri::new_unchecked_const("http://example.org/subdir");
// test with no extension (conneg emulation)
pub const F1X: Iri<&str> = Iri::new_unchecked_const("http://example.org/file1");
pub const F1XR1: Iri<&str> = Iri::new_unchecked_const("http://example.org/file1#res1");
pub const F3X: Iri<&str> = Iri::new_unchecked_const("http://example.org/file3");
#[cfg(feature = "jsonld")]
pub const F4X: Iri<&str> = Iri::new_unchecked_const("http://example.org/file4");
#[cfg(feature = "xml")]
pub const F5X: Iri<&str> = Iri::new_unchecked_const("http://example.org/file5");

pub const EX_ID: Iri<&str> = Iri::new_unchecked_const("http://example.org/ns#id");
pub const EX_LIST: Iri<&str> = Iri::new_unchecked_const("http://example.org/ns#list");
Expand All @@ -52,6 +62,14 @@ mod test {
pub const F1_LEN: usize = 20;
/// Number of triples in F2
pub const F2_LEN: usize = 2;
/// Number of triples in F3
pub const F3_LEN: usize = 20;
/// Number of triples in F4
#[cfg(feature = "jsonld")]
pub const F4_LEN: usize = 20;
/// Number of triples in F5
#[cfg(feature = "xml")]
pub const F5_LEN: usize = 20;

pub type MyGraph = Vec<[SimpleTerm<'static>; 3]>;
pub type TestResult = Result<(), Box<dyn std::error::Error>>;
Expand Down
15 changes: 14 additions & 1 deletion resource/src/loader/_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ impl LocalLoader {
fn ctype(&self, iri: &str) -> String {
if iri.ends_with(".ttl") {
"text/turtle".into()
} else if iri.ends_with(".nt") {
"application/n-triples".into()
} else if cfg!(feature = "jsonld") && iri.ends_with(".jsonld") {
"application/ld+json".into()
} else if cfg!(feature = "xml") && iri.ends_with(".rdf") {
"application/rdf+xml".into()
} else {
"application/octet-stream".into()
}
Expand All @@ -80,7 +86,14 @@ impl Loader for LocalLoader {
// emulate conneg if there is no extension
let no_ext = iri.as_bytes()[iri.rfind(['.', '/']).unwrap_or(0)] != b'.';
if no_ext {
for ext in ["ttl", "nt"] {
for ext in [
"ttl",
"nt",
#[cfg(feature = "jsonld")]
"jsonld",
#[cfg(feature = "xml")]
"rdf",
] {
let alt = Iri::new_unchecked(format!("{}.{}", iri, ext));
if let Ok(res) = self.get(alt) {
return Ok(res);
Expand Down
32 changes: 31 additions & 1 deletion resource/src/loader/_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use sophia_api::parser::TripleParser;
use sophia_api::source::TripleSource;
use sophia_api::term::Term;
use sophia_iri::Iri;
use sophia_turtle::parser::turtle;
use sophia_turtle::parser::{nt, turtle};
use std::borrow::Borrow;
use std::fmt::Debug;
use std::io;
Expand Down Expand Up @@ -40,6 +40,36 @@ pub trait Loader: Sized {
.parse(bufread)
.collect_triples()
.map_err(|err| LoaderError::ParseError(iri_buf(iri_str), Box::new(err))),

"application/n-triples" => nt::NTriplesParser {}
.parse(bufread)
.collect_triples()
.map_err(|err| LoaderError::ParseError(iri_buf(iri_str), Box::new(err))),

#[cfg(feature = "jsonld")]
"application/ld+json" => {
use sophia_api::prelude::{Quad, QuadParser, QuadSource};
use sophia_jsonld::{JsonLdOptions, JsonLdParser};
let options =
JsonLdOptions::new().with_base(iri.as_ref().map_unchecked(|t| t.into()));
// TODO use this loader as the document loader for the JSON-LD parser
// (requires to provide an adaptater)
JsonLdParser::new_with_options(options)
.parse(bufread)
.filter_quads(|q| q.g().is_none())
.map_quads(Quad::into_triple)
.collect_triples()
.map_err(|err| LoaderError::ParseError(iri_buf(iri_str), Box::new(err)))
}

#[cfg(feature = "xml")]
"application/rdf+xml" => sophia_xml::parser::RdfXmlParser {
base: Some(iri.as_ref().map_unchecked(|t| t.borrow().to_string())),
}
.parse(bufread)
.collect_triples()
.map_err(|err| LoaderError::ParseError(iri_buf(iri_str), Box::new(err))),

_ => Err(LoaderError::CantGuessSyntax(iri_buf(iri_str))),
}
}
Expand Down
92 changes: 91 additions & 1 deletion resource/src/loader/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,70 @@ fn get_file1_with_add() -> TestResult {
Ok(())
}

#[test]
fn get_file3() -> TestResult {
let ldr = make_loader();
assert_eq!(
ldr.get(F3)?,
(read("test/file3.nt")?, "application/n-triples".into()),
);
Ok(())
}

#[test]
fn get_file3_no_ext() -> TestResult {
let ldr = make_loader();
assert_eq!(
ldr.get(F3X)?,
(read("test/file3.nt")?, "application/n-triples".into()),
);
Ok(())
}

#[cfg(feature = "jsonld")]
#[test]
fn get_file4() -> TestResult {
let ldr = make_loader();
assert_eq!(
ldr.get(F4)?,
(read("test/file4.jsonld")?, "application/ld+json".into()),
);
Ok(())
}

#[cfg(feature = "jsonld")]
#[test]
fn get_file4_no_ext() -> TestResult {
let ldr = make_loader();
assert_eq!(
ldr.get(F4X)?,
(read("test/file4.jsonld")?, "application/ld+json".into()),
);
Ok(())
}

#[cfg(feature = "xml")]
#[test]
fn get_file5() -> TestResult {
let ldr = make_loader();
assert_eq!(
ldr.get(F5)?,
(read("test/file5.rdf")?, "application/rdf+xml".into()),
);
Ok(())
}

#[cfg(feature = "xml")]
#[test]
fn get_file5_no_ext() -> TestResult {
let ldr = make_loader();
assert_eq!(
ldr.get(F5X)?,
(read("test/file5.rdf")?, "application/rdf+xml".into()),
);
Ok(())
}

#[test]
fn file_not_found() -> TestResult {
let ldr = make_loader();
Expand All @@ -73,13 +137,39 @@ fn io_error() -> TestResult {
}

#[test]
fn graph() -> TestResult {
fn graph_from_ttl() -> TestResult {
let ldr = make_loader();
let g: MyGraph = ldr.get_graph(F1)?;
assert_eq!(g.len(), F1_LEN);
Ok(())
}

#[test]
fn graph_from_nt() -> TestResult {
let ldr = make_loader();
let g: MyGraph = ldr.get_graph(F3)?;
assert_eq!(g.len(), F3_LEN);
Ok(())
}

#[cfg(feature = "jsonld")]
#[test]
fn graph_from_jsonld() -> TestResult {
let ldr = make_loader();
let g: MyGraph = ldr.get_graph(F4)?;
assert_eq!(g.len(), F4_LEN);
Ok(())
}

#[cfg(feature = "xml")]
#[test]
fn graph_from_rdfxml() -> TestResult {
let ldr = make_loader();
let g: MyGraph = ldr.get_graph(F5)?;
assert_eq!(g.len(), F5_LEN);
Ok(())
}

#[test]
fn resource() -> TestResult {
let ldr = make_loader().arced();
Expand Down
20 changes: 20 additions & 0 deletions resource/test/file3.nt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#id> "res1".
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#next> <file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2>.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#related> <file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2>.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#related> <file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3>.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#related> _:b.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#foreign1> <file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res1>.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#foreign2> <file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res2>.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#unreachable> <http://somewhere.else/>.
_:riog00000001 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3>.
_:riog00000001 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:riog00000002.
_:riog00000002 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2>.
_:riog00000002 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:riog00000003.
_:riog00000003 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res1>.
_:riog00000003 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil>.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1> <http://example.org/ns#list> _:riog00000001.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2> <http://example.org/ns#id> "res2".
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2> <http://example.org/ns#list> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil>.
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3> <http://example.org/ns#id> "res3".
<file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3> <http://example.org/ns#related> <file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2>.
_:b <http://example.org/ns#id> "res4".
88 changes: 88 additions & 0 deletions resource/test/file4.jsonld
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
[
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1",
"http://example.org/ns#next": [
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"
}
],
"http://example.org/ns#id": [
{
"@value": "res1"
}
],
"http://example.org/ns#list": [
{
"@list": [
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3"
},
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"
},
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res1"
}
]
}
],
"http://example.org/ns#foreign1": [
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res1"
}
],
"http://example.org/ns#related": [
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"
},
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3"
},
{ "@id": "_:b" }
],
"http://example.org/ns#foreign2": [
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res2"
}
],
"http://example.org/ns#unreachable": [
{
"@id": "http://somewhere.else/"
}
]
},
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2",
"http://example.org/ns#list": [
{
"@list": []
}
],
"http://example.org/ns#id": [
{
"@value": "res2"
}
]
},
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3",
"http://example.org/ns#id": [
{
"@value": "res3"
}
],
"http://example.org/ns#related": [
{
"@id": "file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"
}
]
},
{
"@id": "_:b",
"http://example.org/ns#id": [
{
"@value": "res4"
}
]
}
]
1 change: 1 addition & 0 deletions resource/test/file5.rdf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1"><id xmlns="http://example.org/ns#">res1</id><next xmlns="http://example.org/ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"/><related xmlns="http://example.org/ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"/><related xmlns="http://example.org/ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3"/><related xmlns="http://example.org/ns#" rdf:nodeID="b"/><foreign1 xmlns="http://example.org/ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res1"/><foreign2 xmlns="http://example.org/ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res2"/><unreachable xmlns="http://example.org/ns#" rdf:resource="http://somewhere.else/"/></rdf:Description><rdf:Description rdf:nodeID="riog00000001"><first xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3"/><rest xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:nodeID="riog00000002"/></rdf:Description><rdf:Description rdf:nodeID="riog00000002"><first xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"/><rest xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:nodeID="riog00000003"/></rdf:Description><rdf:Description rdf:nodeID="riog00000003"><first xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file2.ttl#res1"/><rest xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/></rdf:Description><rdf:Description rdf:about="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res1"><list xmlns="http://example.org/ns#" rdf:nodeID="riog00000001"/></rdf:Description><rdf:Description rdf:about="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"><id xmlns="http://example.org/ns#">res2</id><list xmlns="http://example.org/ns#" rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/></rdf:Description><rdf:Description rdf:about="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res3"><id xmlns="http://example.org/ns#">res3</id><related xmlns="http://example.org/ns#" rdf:resource="file:///home/pa/dev/sophia_rs/resource/test/file1.ttl#res2"/></rdf:Description><rdf:Description rdf:nodeID="b"><id xmlns="http://example.org/ns#">res4</id></rdf:Description></rdf:RDF>
8 changes: 4 additions & 4 deletions sophia/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ all-features = true
[features]
default = []
# This feature enables the JSON-LD parser and serializer
jsonld = ["sophia_jsonld"]
jsonld = ["sophia_jsonld", "sophia_resource/jsonld"]
# This feature enables the RDF/XML parser and serializer
xml = ["sophia_xml"]
xml = ["sophia_xml", "sophia_resource/xml"]
# This feature enables to use the graph and dataset test macros in other crates
test_macro = ["sophia_api/test_macro"]
# This feature enables the HTTP client crate in dependencies
http_client = ["sophia_jsonld/http_client"]
# This feature enables the HTTP client in dependencies
http_client = ["sophia_jsonld/http_client", "sophia_resource/http_client"]

[dependencies]
sophia_iri.workspace = true
Expand Down

0 comments on commit 26fc597

Please sign in to comment.