From 88d31df4d5145a6ae9820eed0f39d3637a695df5 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Champin Date: Wed, 15 Nov 2023 13:38:13 +0100 Subject: [PATCH] improve documentation and book also improve the API by allowing &str * NsTerm to generate datatype literals. --- .gitmodules | 3 - README.md | 14 +- api/src/lib.rs | 1 + api/src/ns.rs | 24 ++- api/src/ns/_term.rs | 17 ++ api/src/term.rs | 63 ++++--- api/src/term/language_tag.rs | 27 +-- book/src/00_introduction.md | 6 - book/src/SUMMARY.md | 7 +- book/src/ch00_introduction.md | 34 ++++ ...ing_started.md => ch01_getting_started.md} | 3 +- book/src/ch02_rdf_terms.md | 169 ++++++++++++++++++ ...s_since_07.md => ch90_changes_since_07.md} | 21 ++- json-ld-api | 1 - sophia/src/lib.rs | 49 ++--- 15 files changed, 334 insertions(+), 105 deletions(-) delete mode 100644 book/src/00_introduction.md create mode 100644 book/src/ch00_introduction.md rename book/src/{01_getting_started.md => ch01_getting_started.md} (93%) create mode 100644 book/src/ch02_rdf_terms.md rename book/src/{90_changes_since_07.md => ch90_changes_since_07.md} (84%) delete mode 160000 json-ld-api diff --git a/.gitmodules b/.gitmodules index 7b6ca23c..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "json-ld-api"] - path = json-ld-api - url = https://github.com/w3c/json-ld-api diff --git a/README.md b/README.md index fd99b9d4..c71c8352 100644 --- a/README.md +++ b/README.md @@ -28,21 +28,14 @@ and finally: * [`sophia`] is the “all-inclusive” crate, re-exporting symbols from all the crates above. (actually, `sophia_xml` is only available if the `xml` feature is enabled) + +In addition to the [API documentation](https://docs.rs/sophia/), +a high-level [user documentation](https://pchampin.github.io/sophia_rs/) is available (although not quite complete yet). ## Licence [CECILL-B] (compatible with BSD) -## Testing - -The test suite depends on the [the [JSON-LD test-suite] -which is included as a `git` submodule. -In order to run all the tests, you need to execute the following commands: -```bash -$ git submodule init -$ git submodule update -``` - ## Citation When using Sophia, please use the following citation: @@ -69,6 +62,7 @@ Bibtex: The following third-party crates are using or extending Sophia * [`hdt`](https://crates.io/crates/hdt) provides an implementation of Sophia's traits based on the [HDT](https://www.rdfhdt.org/) format. +* [`manas`](https://crates.io/crates/manas) is a modular framework for implementing [Solid](https://solidproject.org/) compatible servers ## History diff --git a/api/src/lib.rs b/api/src/lib.rs index e691c4ad..461428b1 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -63,4 +63,5 @@ pub mod term; pub mod triple; /// Re-export MownStr to avoid dependency version mismatch. +/// pub use mownstr::MownStr; diff --git a/api/src/ns.rs b/api/src/ns.rs index 71e48226..39e8c679 100644 --- a/api/src/ns.rs +++ b/api/src/ns.rs @@ -1,12 +1,14 @@ //! Standard and custom namespaces. //! //! This module provides: -//! * the [`Namespace`](struct.Namespace.html) type for defining custom namespace; -//! * modules corresponding to the most common namespaces. +//! * the [`Namespace`](struct.Namespace.html) type for defining custom dynamic namespace; +//! * the [`namespace`] macro, for defning custom static namespaces; +//! * modules corresponding to the most common namespaces +//! (generated via the [`namespace`] macro). //! -//! # Example +//! # Example use //! ``` -//! use sophia_api::ns::{Namespace, NsTerm, rdf, rdfs, xsd}; +//! use sophia_api::ns::{Namespace, rdf, rdfs, xsd}; //! //! let schema = Namespace::new("http://schema.org/").unwrap(); //! let s_name = schema.get("name").unwrap(); @@ -16,6 +18,20 @@ //! g.push([&s_name, &rdf::type_, &rdf::Property]); //! g.push([&s_name, &rdfs::range, &xsd::string]); //! ``` +//! +//! # Datatyped literals +//! +//! Note also that the terms generated via the [`namespace`] macro +//! can be used to easily produced datatyped literals, +//! by simply "multiplying" a string by its datatype: +//! +//! ``` +//! # use sophia_api::{term::Term, ns::xsd}; +//! let date = "2023-11-15" * xsd::date ; +//! assert!(date.is_literal()); +//! assert_eq!(date.lexical_form().unwrap(), "2023-11-15"); +//! assert_eq!(date.datatype().unwrap(), xsd::date.iri().unwrap()); +//! ``` use mownstr::MownStr; use sophia_iri::InvalidIri; use std::borrow::Borrow; diff --git a/api/src/ns/_term.rs b/api/src/ns/_term.rs index c7c27231..6ca0274b 100644 --- a/api/src/ns/_term.rs +++ b/api/src/ns/_term.rs @@ -35,6 +35,15 @@ impl<'a> NsTerm<'a> { self.to_string().into() }) } + + /// Return an [`IriRef`] representing this term. + pub fn to_iriref(self) -> IriRef> { + if self.suffix.is_empty() { + self.ns.map_unchecked(MownStr::from) + } else { + IriRef::new_unchecked(self.to_string().into()) + } + } } impl<'a> Term for NsTerm<'a> { @@ -93,3 +102,11 @@ mod test { assert!(t3a != t1b); } } + +impl<'a> std::ops::Mul> for &'a str { + type Output = crate::term::SimpleTerm<'a>; + + fn mul(self, rhs: NsTerm<'a>) -> Self::Output { + crate::term::SimpleTerm::LiteralDatatype(self.into(), rhs.to_iriref()) + } +} diff --git a/api/src/term.rs b/api/src/term.rs index 0d12be1d..b08f10fb 100644 --- a/api/src/term.rs +++ b/api/src/term.rs @@ -64,9 +64,13 @@ pub enum TermKind { /// However, while all other methods have a default implementtation (returning `None`), /// those corresponding to the supported kinds MUST be overridden accordingly, /// otherwise they will panic. -/// /// See below for an explaination of this design choice. /// +/// In order to test that all the methods are implemented consistently, +/// consider using the macro [`assert_consistent_term_impl`]. +/// The macro should be invoked on various instances of the type, +/// at least one for each [kind](Term::kind) that the type supports. +/// /// # Design rationale /// /// The methods defined by this trait are not independant: @@ -88,23 +92,19 @@ pub trait Term: std::fmt::Debug { /// A type of [`Term`] that can be borrowed from this type /// (i.e. that can be obtained from a simple reference to this type). /// It is used in particular for accessing constituents of quoted tripes ([`Term::triple`]) - /// or for sharing this term with a function that expects `T: Term` (rather than `&T`). - /// - /// In "standard" cases, this type is `&Self`. - /// - /// Exceptions, where this type is `Self` instead, are - /// * some [`Term`] implementations implementing [`Copy`]; - /// * in particular, the implementation of [`Term`] by *references* to "standard" [`Term`] implementations. + /// or for sharing this term with a function that expects `T: Term` (rather than `&T`) + /// using [`Term::borrow_term`]. /// - /// This design makes `T: Term` conceptually equivalent `T: Borrow` - /// (but the latter is not valid, since `Term` is a trait). - /// In this pattern, [`Term::borrow_term`] plays the rols of [`Borrow::borrow`](std::borrow). + /// In "standard" cases, this type is either `&Self` or `Self` + /// (for types implementing [`Copy`]). /// /// # Note to implementors - /// When in doubt, set this to `&Self`. - /// If that is not possible and your type implements [`Copy`], - /// consider setting this to `Self`. - /// Otherwise, your implementations is probably not a good fit for implementing [`Term`]. + /// * When in doubt, set `BorrowTerm<'x>` to `&'x Self`. + /// * If your type implements [`Copy`], + /// consider setting it to `Self`. + /// * If your type is a wrapper `W(T)` where `T: Term`, + /// consider setting it to `W(T::BorrowTerm<'x>)`. + /// * If none of the options above are possible, your type is probably not a good fit for implementing [`Term`]. type BorrowTerm<'x>: Term + Copy where Self: 'x; @@ -272,9 +272,30 @@ pub trait Term: std::fmt::Debug { .then(|| unimplemented!("Default implementation should have been overridden")) } - /// Get something implementing [`Term`] from a simple reference to `self`. + /// Get something implementing [`Term`] from a simple reference to `self`, + /// representing the same RDF term as `self`. /// - /// See [`Term::BorrowTerm`] for more detail. + /// # Wny do functions in Sophia expect `T: Term` and never `&T: Term`? + /// To understand the rationale of this design choice, + /// consider an imaginary type `Foo`. + /// A function `f(x: Foo)` requires users to waive the ownership of the `Foo` value they want to pass to the function. + /// This is not always suited to the users needs. + /// On the other hand, a function `g(x: &Foo)` not only allows, but *forces* its users to maintain ownership of the `Foo` value they want to pass to the function. + /// Again, there are situations where this is not suitable. + /// The standard solution to this problem is to use the [`Borrow`](std::borrow) trait: + /// a function `h(x: T) where T: Borrow` allows `x` to be passed either by *value* (transferring ownership) + /// or by reference (simply borrowing the caller's `Foo`). + /// + /// While this design pattern is usable with a single type (`Foo` in our example above), + /// it is not usable with a trait, such as `Term`: + /// the following trait bound is not valid in Rust: `T: Borrow`. + /// Yet, we would like any function expecting a terms to be able to either take its ownership or simply borrow it, + /// depending on the caller's needs and preferences. + /// + /// The `borrow_term` methods offer a solution to this problem, + /// and therefore the trait bound `T: Term` must be thought of as equivalent to `T: Borrow`: + /// the caller can chose to either waive ownership of its term (by passing it directly) + /// or keep it (by passing the result of `borrow_term()` instead). fn borrow_term(&self) -> Self::BorrowTerm<'_>; /// Iter over all the constituents of this term. @@ -376,7 +397,7 @@ pub trait Term: std::fmt::Debug { /// * Quoted triples are ordered in lexicographical order /// /// NB: literals are ordered by their *lexical* value, - /// so for example, `"10"^^xsd:integer` come *before* `"2"^^xsd:integer`. + /// so for example, `"10"^^xsd:integer` comes *before* `"2"^^xsd:integer`. fn cmp(&self, other: T) -> Ordering where T: Term, @@ -447,7 +468,7 @@ pub trait Term: std::fmt::Debug { /// Convert this term in another type. /// - /// This method is to [`FromTerm`] was [`Into::into`] is to [`From`]. + /// This method is to [`FromTerm`] what [`Into::into`] is to [`From`]. /// /// NB: if you want to make a *copy* of this term without consuming it, /// you can use `this_term.`[`borrow_term`](Term::borrow_term)`().into_term::()`. @@ -461,10 +482,10 @@ pub trait Term: std::fmt::Debug { /// Try to convert this term into another type. /// - /// This method is to [`FromTerm`] was [`TryInto::try_into`] is to [`TryFrom`]. + /// This method is to [`TryFromTerm`] what [`TryInto::try_into`] is to [`TryFrom`]. /// /// NB: if you want to make a *copy* of this term without consuming it, - /// you can use `this_term.`[`borrow_term`](Term::borrow_term)`().into_term::()`. + /// you can use `this_term.`[`borrow_term`](Term::borrow_term)`().try_into_term::()`. #[inline] fn try_into_term(self) -> Result where diff --git a/api/src/term/language_tag.rs b/api/src/term/language_tag.rs index 4828eed7..56555a4e 100644 --- a/api/src/term/language_tag.rs +++ b/api/src/term/language_tag.rs @@ -1,19 +1,6 @@ //! I define the [`LanguageTag`] wrapper type, //! which guarantees that the underlying `str` //! is a valid [BCP47](https://tools.ietf.org/search/bcp47) language tag. -//! -//! A [`LanguageTag`] can be combined to a `&str` with the `*` operator, -//! to produce an RDF [language tagged string](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string) -//! implementing the [`Term`](crate::term::Term) trait: -//! -//! ``` -//! # use sophia_api::term::{LanguageTag, Term}; -//! let fr = LanguageTag::new_unchecked("fr"); -//! let message = "Bonjour le monde" * fr; -//! assert!(message.is_literal()); -//! assert_eq!(message.lexical_form().unwrap(), "Bonjour le monde"); -//! assert_eq!(message.language_tag().unwrap(), fr); -//! ``` use lazy_static::lazy_static; use regex::Regex; @@ -42,6 +29,20 @@ lazy_static! { /// is a valid [BCP47](https://tools.ietf.org/search/bcp47) language tag. /// /// NB: it is actually more permissive than BCP47. +/// +/// A [`LanguageTag`] can be combined to a `&str` with the `*` operator, +/// to produce an RDF [language tagged string](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string) +/// implementing the [`Term`](crate::term::Term) trait: +/// +/// ``` +/// # use sophia_api::{ns::rdf, term::{LanguageTag, Term}}; +/// let fr = LanguageTag::new_unchecked("fr"); +/// let message = "Bonjour le monde" * fr; +/// assert!(message.is_literal()); +/// assert_eq!(message.lexical_form().unwrap(), "Bonjour le monde"); +/// assert_eq!(message.datatype().unwrap(), rdf::langString.iri().unwrap()); +/// assert_eq!(message.language_tag().unwrap(), fr); +/// ``` #[derive(Clone, Copy, Debug)] pub struct LanguageTag>(T); diff --git a/book/src/00_introduction.md b/book/src/00_introduction.md deleted file mode 100644 index 05570e0c..00000000 --- a/book/src/00_introduction.md +++ /dev/null @@ -1,6 +0,0 @@ -# Introduction - -The [sophia crate](https://crates.io/crates/sophia) aims to provide a comprehensive toolkit for working with RDF and Linked Data in Rust. - -RDF is a data model designed to exchange knowledge on the Web in an interoperable way. Each piece of knowledge in RDF (a statement) is represented by a triple, made of three terms. A set of triples forms an RDF graph. Finally, several graphs can be grouped in a collection called a dataset, where each graph is identified by a unique name. - diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 2160f7b0..e2eec291 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -2,6 +2,7 @@ [Executive summary](./README.md) -- [Introduction](./00_introduction.md) -- [Getting Started](./01_getting_started.md) -- [Changes since version 0.7](./90_changes_since_07.md) +- [Introduction](./ch00_introduction.md) +- [Getting Started](./ch01_getting_started.md) +- [RDF Terms](./ch02_rdf_terms.md) +- [Changes since version 0.7](./ch90_changes_since_07.md) diff --git a/book/src/ch00_introduction.md b/book/src/ch00_introduction.md new file mode 100644 index 00000000..01019a66 --- /dev/null +++ b/book/src/ch00_introduction.md @@ -0,0 +1,34 @@ +# Introduction + +The [sophia crate](https://crates.io/crates/sophia) aims at providing a comprehensive toolkit for working with RDF and Linked Data in Rust. + +[RDF] is a data model designed to exchange knowledge on the Web in an interoperable way. Each piece of knowledge in RDF (a [statement]) is represented by a [triple], made of three [terms]. A set of triples forms an RDF [graph]. Finally, several graphs can be grouped in a collection called a [dataset], where each graph is identified by a unique name. + +In Sophia, each of these core concepts is modeled by a trait, which can be implemented in multiple ways (see for example the [`Graph`] trait and [some of the types implementing it](https://docs.rs/sophia_api/latest/sophia_api/graph/trait.Graph.html#foreign-impls)). Sophia is therefore not meant to provide the "ultimate" implementation of RDF in Rust, but a generic framework to help various implementations to interoperate with each other (in the spirit of [Apache Commons RDF] for Java or [RDFJS] for Javascript/Typescript). + +## Generalized vs. Strict RDF model +The data model supported by this Sophia is in fact +a superset of the RDF data model as defined by the W3C. +When the distinction matters, +they will be called, respectively, +the *generalized* RDF model, and the *strict* RDF model. +The generalized RDF model extends RDF as follows: +* In addition to standard RDF terms (IRIs, blank nodes and literals), + Sophia supports + - RDF-star [quoted triples](https://www.w3.org/2021/12/rdf-star.html#dfn-quoted) + - Variables (a concept borrowed from [SPARQL] or [Notation3]) +* Sophia allows any kind of term in any position (subject, predicate, object, graph name). +* Sophia allow IRIs to be relative IRI references + (while in strict RDF, [IRIs must be absolute](https://www.w3.org/TR/rdf11-concepts/#h3_section-IRIs)). + +[RDF]: https://www.w3.org/TR/rdf-concepts/ +[statement]: https://www.w3.org/TR/rdf-concepts/#dfn-rdf-statement +[triple]: https://www.w3.org/TR/rdf-concepts/#dfn-rdf-triple +[terms]: https://www.w3.org/TR/rdf-concepts/#dfn-rdf-term +[graph]: https://www.w3.org/TR/rdf-concepts/#dfn-rdf-graph +[dataset]: https://www.w3.org/TR/rdf-concepts/#dfn-rdf-dataset + +[`Graph`]: https://docs.rs/sophia_api/latest/sophia_api/graph/trait.Graph.html +[Apache Commons RDF]: https://github.com/apache/commons-rdf/ +[RDF/JS]: https://rdf.js.org/ + diff --git a/book/src/01_getting_started.md b/book/src/ch01_getting_started.md similarity index 93% rename from book/src/01_getting_started.md rename to book/src/ch01_getting_started.md index bb21b8af..d780753d 100644 --- a/book/src/01_getting_started.md +++ b/book/src/ch01_getting_started.md @@ -1,6 +1,6 @@ # Getting Started -Following a short example how to build a graph, mutate it and serialize it back. +Below is a short example demonstrating how to build a graph, mutate it and serialize it back. Add the sophia crate to your dependencies in `Cargo.toml` @@ -12,7 +12,6 @@ sophia = "0.8.0-alpha.3" Add these lines of code and run the program. ```rust,noplayground -# extern crate sophia; use sophia::api::prelude::*; use sophia::api::ns::Namespace; use sophia::inmem::graph::LightGraph; diff --git a/book/src/ch02_rdf_terms.md b/book/src/ch02_rdf_terms.md new file mode 100644 index 00000000..84572793 --- /dev/null +++ b/book/src/ch02_rdf_terms.md @@ -0,0 +1,169 @@ +# RDF Terms + +The [`Term`] trait defines how you interad with [RDF terms] in Sophia. + +## Using terms + +The first thing you usually need to know about a term is its *kind* (IRI, Literal...). +The kind is described by the [`TermKind`] enum, +and available from the [`Term::kind`] method. + +```rust,noplayground +# use sophia::api::term::{FromTerm, SimpleTerm, Term, TermKind}; +# use TermKind::*; +# let some_term = SimpleTerm::from_term("foo"); +match some_term.kind() { + Iri => { /* ... */ } + Literal => { /* ... */ } + BlankNode => { /* ... */ } + _ => { /* ... */ } +} +``` + +Alternatively, when only one kind is of interest, you can use [`Term::is_iri`], [`Term::is_literal`], [`Term::is_blank_node`], etc. + +If you are interested in the "value" of the term, the trait provides the following methods. All of them return an `Option`, which will be `None` if the term does not have the corresponding kind. + +* If the term is an IRI, [`Term::iri`] returns that IRI. +* If the term is a blank node, [`Term::bnode_id`] returns its [blank node identifier]. +* If the term is a literal: + + + [`Term::lexical_form`] returns its [lexical form] (the "textual value" of the literal), + + [`Term::datatype`] returns its datatype IRI, + + [`Term::language_tag`] returns its [language tag], if any. + +* If the term is a [quoted triple]: + + + [`Term::triple`] returns its 3 components in an array of terms, + + [`Term::constituents`] iterates over all its [constituents], + + [`Term::atoms`] iterates over all its atomic (i.e. non quoted-triple) constituents. + + (those three methods also have a `to_X` version that destructs the original term instead of borrowing it) + +* If the term is a variable, [`Term::variable`] returns its name. + +Finally, the method [`Term::eq`] can be used to check whether two values implementing [`Term`] represent the same RDF term. Note that the `==` operator may give a different result than [`Term::eq`] on some types implementing the [`Term`] trait. + + +## Useful types implementing [`Term`] + +Below is a list of useful types implementing the [`Term`] trait: + +* [`Iri`]`` and [`IriRef`]``, where `T: Borrow`, representing IRIs +* [`BnodeId`]`, where `T: Borrow`, representing blank nodes +* `str`, representing literals of type `xsd:string`, +* `i32`, `isize` and `usize` representing literals of type `xsd:integer`, +* `f64` representing literals of type `xsd:double`, +* [`SimpleTerm`](see below). + +[`SimpleTerm`] is a straightforward implementation of [`Term`], that can represent any kind of term, and can either own its own underlying data or borrow it from something else. + +Any term can be converted to a [`SimpleTerm`] using the [`Term::as_simple`] method. This method borrows as much as possible from the initial term. Alternatively, to convert any term to a self-sufficient [`SimpleTerm`], you can use [`Term::into_term`] + +See also the list of [recipes](#recipes-for-constructing-terms) below. + + +## Borrowing terms with [`Term::borrow_term`] + +In Sophia, all functions accepting terms as parameters are expecting a type `T: Term` -- not `&T`, but the type `T` itself. So what happens when you want to call such a function with a term `t`, but still want to retain ownership of `t`? + +The solution is to pass [`t.borrow_term()`] to the function. This method returns *something* implementing [`Term`], representing the same RDF term as `t`, without waiving ownership. This is a very common pattern in Sophia. + +More precisely, the type returned by [`t.borrow_term()`] is the associated type [`Term::BorrowTerm`]. In most cases, this is a reference a reference or a copy of `t`. + + +## Recipes for constructing terms + +### Constructing IRIs + +```rust,noplayground +# fn main() -> Result<(), Box> { +# +# use sophia::{iri::IriRef, api::ns::Namespace}; +# let some_text = "http://example.org"; +// construct an IRI from a constant +let iri1 = IriRef::new_unchecked("http://example.org"); + +// construct an IRI from an untrusted string +let iri2 = IriRef::new(some_text)?; + +// construct multiple IRIs from a namespace +let ns = Namespace::new_unchecked("http://example.org/ns#"); +let iri3 = ns.get_unchecked("foo"); +let iri4 = ns.get(some_text)?; + +// standard namespaces +use sophia::api::ns::{rdf, xsd}; +let iri5 = rdf::Property ; +let iri6 = xsd::string ; +# +# Ok(()) } +``` + +### Constructing literals +```rust,noplayground +# fn main() -> Result<(), Box> { +# +# use sophia::api::{ns::xsd, term::{LanguageTag, SimpleTerm, Term}}; +// use native types for xsd::string, xsd::integer, xsd::double +let lit_string = "hello world"; +let lit_integer = 42; +let lit_double = 1.23; + +// construct a language-tagged string +let fr = LanguageTag::new_unchecked("fr"); +let lit_fr = "Bonjour le monde" * fr; + +// construct a literal with an arbitrary datatype +let lit_date = "2023-11-15" * xsd::date; +# +# Ok(()) } +``` + +### Constructing blank nodes +```rust,noplayground +# fn main() -> Result<(), Box> { +# +# use sophia::api::term::BnodeId; +let b = BnodeId::new("x"); +# +# Ok(()) } +``` + + +[`Term`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html +[RDF terms]: https://www.w3.org/TR/rdf-concepts/#dfn-rdf-term +[generalized RDF]: ch00_introduction.html#generalized-vs-strict-rdf-model +[`TermKind`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/enum.TermKind.html +[`Term::kind`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#tymethod.kind +[`Term::is_iri`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.is_iri +[`Term::is_blank_node`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.is_blank_node +[`Term::is_literal`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.is_literal +[`Term::iri`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.iri +[`Term::bnode_id`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.bnode_id +[`Term::bnode_id`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.bnode_id +[`Term::bnode_id`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.bnode_id +[`Term::lexical_form`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.lexical_form +[`Term::datatype`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.datatype +[`Term::language_tag`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.language_tag +[`Term::triple`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.triple +[`Term::constituents`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.constituents +[`Term::atoms`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.atoms +[`Term::variable`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.variable +[`Term::eq`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.eq +[`Term::as_simple`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.as_simple +[`Term::into_term`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#method.into_term +[`Term::borrow_term`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#tymethod.borrow_term +[`t.borrow_term()`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#tymethod.borrow_term +[`Term::BorrowTerm`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/trait.Term.html#associatedtype.BorrowTerm + +[blank node identifier]: https://www.w3.org/TR/rdf-concepts/#dfn-blank-node-identifier +[lexical form]: https://www.w3.org/TR/rdf-concepts/#dfn-lexical-form +[datatype]: https://www.w3.org/TR/rdf-concepts/#dfn-datatype-iri +[language tag]: https://www.w3.org/TR/rdf-concepts/#dfn-language-tag +[quoted triple]: https://www.w3.org/2021/12/rdf-star.html#dfn-quoted +[constituents]: https://www.w3.org/2021/12/rdf-star.html#dfn-constituent + +[`Iri`]: https://docs.rs/sophia_iri/0.8.0-alpha.3/sophia_iri/struct.Iri.html +[`IriRef`]: https://docs.rs/sophia_iri/0.8.0-alpha.3/sophia_iri/struct.IriRef.html +[`BnodeId`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/bnode_id/struct.BnodeId.html +[`SimpleTerm`]: https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/enum.SimpleTerm.html \ No newline at end of file diff --git a/book/src/90_changes_since_07.md b/book/src/ch90_changes_since_07.md similarity index 84% rename from book/src/90_changes_since_07.md rename to book/src/ch90_changes_since_07.md index 0f78b0fa..53160253 100644 --- a/book/src/90_changes_since_07.md +++ b/book/src/ch90_changes_since_07.md @@ -41,7 +41,7 @@ trait, with a significantly different API, that serves several purposes: (they are interpreted as `xsd:string` and `xsd:integer` literals, respectively). Any code that handles terms will need some significant rewriting. -Eventually, a chapter of this book will extensively document the new `Term` trait. +See the chapter on [RDF terms](ch02_rdf_terms.md) for more detail. ### The end of the "IRI zoo" @@ -49,15 +49,18 @@ Historically, a number of different types have been created in Sophia for repres which was [causing some confusion](https://github.com/pchampin/sophia_rs/discussions/112). Most of them have now disappeared, in favor of the types defined in [`sophia_iri`](https://docs.rs/sophia_iri/latest/sophia_iri/). -### The end of the [`sophia_term`](https://docs.rs/sophia_term/0.7.2/sophia_term/) crate +### Reducing the [`sophia_term`](https://docs.rs/sophia_term/latest/sophia_term/) crate -The [`sophia_term`](https://docs.rs/sophia_term/0.7.2/sophia_term/) -crate no longer exists in version 0.8.0. +The [`sophia_term`](https://docs.rs/sophia_term/latest/sophia_term/) crate, +from which most term implementations came in 0.7, has been significantly reduced. The most general types that it provided ([`BoxTerm`](https://docs.rs/sophia_term/0.7.2/sophia_term/type.BoxTerm.html), [`RefTerm`](https://docs.rs/sophia_term/0.7.2/sophia_term/type.RefTerm.html)) -are now subsumed by [`SimpleTerm`](https://github.com/pchampin/sophia_rs/blob/a925e6177cfdd7e90dafd4b917ae0790c40a0165/api/src/term/_simple.rs#L8), -a straightforward implementation of the `Term` trait, provided by `sophia_api`. -More specific types (such as [`RcTerm`](https://docs.rs/sophia_term/0.7.2/sophia_term/type.RcTerm.html) or [`ArcTerm`](https://docs.rs/sophia_term/0.7.2/sophia_term/type.ArcTerm.html)) -have been deemed non-essential, and are no longer provided at the moment. +are now subsumed by [`SimpleTerm`](https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/term/enum.SimpleTerm.html), +a straightforward implementation of the `Term` trait, provided by +[`sophia_api`](https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/index.html). +More specific types (such as +[`RcTerm`](https://docs.rs/sophia_term/0.8.0-alpha.3/sophia_term/type.RcTerm.html) or +[`ArcTerm`](https://docs.rs/sophia_term/0.8.0-alpha.3/sophia_term/type.ArcTerm.html)) +are still provided by `sophia_term`. ## Simplification of the `Graph` and `Dataset` traits @@ -66,7 +69,7 @@ such as [`triples_with_s`](https://docs.rs/sophia_api/0.7.2/sophia_api/graph/tra or [`triples_with_po`](https://docs.rs/sophia_api/0.7.2/sophia_api/graph/trait.Graph.html#method.triples_with_po) (and similarly for `Dataset`: `quads_with_s`, etc.). -All these methods have disappeared in favor of `triples_matching`, +All these methods have disappeared in favor of [`triples_matching`](https://docs.rs/sophia_api/0.8.0-alpha.3/sophia_api/graph/trait.Graph.html#method.triples_matching), so that instead of: ```rust,noplayground,ignore for t in g.triples_with_s(mys) { diff --git a/json-ld-api b/json-ld-api deleted file mode 160000 index a109677c..00000000 --- a/json-ld-api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a109677c31a8238041a1aaefd744f1a137b52111 diff --git a/sophia/src/lib.rs b/sophia/src/lib.rs index 42d75dc6..d4803afd 100644 --- a/sophia/src/lib.rs +++ b/sophia/src/lib.rs @@ -17,40 +17,8 @@ //! //! # Getting Started //! -//! Following a short example how to build a graph, mutate it and serialize it -//! back. +//! See the [Sophia book](https://pchampin.github.io/sophia_rs/ch01_getting_started.html) //! -//! ``` -//! use sophia::api::prelude::*; -//! use sophia::api::ns::Namespace; -//! use sophia::inmem::graph::LightGraph; -//! use sophia::turtle::parser::turtle; -//! use sophia::turtle::serializer::nt::NtSerializer; -//! -//! let example = r#" -//! @prefix : . -//! @prefix foaf: . -//! -//! :alice foaf:name "Alice"; -//! foaf:mbox . -//! -//! :bob foaf:name "Bob". -//! "#; -//! let mut graph: LightGraph = turtle::parse_str(example).collect_triples()?; -//! -//! let ex = Namespace::new("http://example.org/")?; -//! let foaf = Namespace::new("http://xmlns.com/foaf/0.1/")?; -//! graph.insert( -//! ex.get("bob")?, -//! foaf.get("knows")?, -//! ex.get("alice")?, -//! )?; -//! -//! let mut nt_stringifier = NtSerializer::new_stringifier(); -//! let example2 = nt_stringifier.serialize_graph(&graph)?.as_str(); -//! println!("The resulting graph:\n{}", example2); -//! # Ok::<(), Box>(()) -//! ``` //! //! [RDF]: https://www.w3.org/TR/rdf-primer/ //! [Linked Data]: http://linkeddata.org/ @@ -67,3 +35,18 @@ pub use sophia_term as term; pub use sophia_turtle as turtle; #[cfg(feature = "xml")] pub use sophia_xml as xml; + +/// Including tests from all code snippets in the book +/// from https://github.com/rust-lang/mdBook/issues/706#issuecomment-1085137304 +#[cfg(doctest)] +mod booktest { + macro_rules! booktest { + ($i:ident) => { + #[doc = include_str!(concat!("../../book/src/", stringify!($i), ".md"))] + mod $i {} + }; + } + booktest!(ch01_getting_started); + booktest!(ch02_rdf_terms); + booktest!(ch90_changes_since_07); +}