From 9d4fe700d65d258d69f0bb6fbdfba5c5b3087d60 Mon Sep 17 00:00:00 2001 From: Tpt Date: Wed, 29 Jul 2020 19:07:37 +0200 Subject: [PATCH] Cleans up code related to file syntaxes --- js/src/store.rs | 6 +- lib/src/io/mod.rs | 6 ++ lib/src/{ => io}/syntax.rs | 136 +++++++++++++++++++++++++++++++------ lib/src/lib.rs | 12 ++-- lib/src/sparql/model.rs | 67 +++++++++++++++--- python/src/memory_store.rs | 6 +- python/src/sled_store.rs | 6 +- server/src/main.rs | 17 +++-- wikibase/src/main.rs | 12 +++- 9 files changed, 218 insertions(+), 50 deletions(-) create mode 100644 lib/src/io/mod.rs rename lib/src/{ => io}/syntax.rs (52%) diff --git a/js/src/store.rs b/js/src/store.rs index d04ac1b2..3df65bec 100644 --- a/js/src/store.rs +++ b/js/src/store.rs @@ -4,7 +4,7 @@ use crate::utils::to_err; use js_sys::{Array, Map}; use oxigraph::model::GraphName; use oxigraph::sparql::{QueryOptions, QueryResult}; -use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, MemoryStore}; +use oxigraph::{DatasetSyntax, GraphSyntax, MemoryStore}; use std::convert::TryInto; use std::io::Cursor; use wasm_bindgen::prelude::*; @@ -157,7 +157,7 @@ impl JsMemoryStore { None }; - if let Some(graph_syntax) = GraphSyntax::from_mime_type(mime_type) { + if let Some(graph_syntax) = GraphSyntax::from_media_type(mime_type) { self.store .load_graph( Cursor::new(data), @@ -166,7 +166,7 @@ impl JsMemoryStore { base_iri.as_deref(), ) .map_err(to_err) - } else if let Some(dataset_syntax) = DatasetSyntax::from_mime_type(mime_type) { + } else if let Some(dataset_syntax) = DatasetSyntax::from_media_type(mime_type) { if to_graph_name.is_some() { return Err(format_err!( "The target graph name parameter is not available for dataset formats" diff --git a/lib/src/io/mod.rs b/lib/src/io/mod.rs new file mode 100644 index 00000000..648c7d4d --- /dev/null +++ b/lib/src/io/mod.rs @@ -0,0 +1,6 @@ +mod syntax; + +pub use self::syntax::DatasetSyntax; +#[allow(deprecated)] +pub use self::syntax::FileSyntax; +pub use self::syntax::GraphSyntax; diff --git a/lib/src/syntax.rs b/lib/src/io/syntax.rs similarity index 52% rename from lib/src/syntax.rs rename to lib/src/io/syntax.rs index 5d1aa5e7..2c402a4b 100644 --- a/lib/src/syntax.rs +++ b/lib/src/io/syntax.rs @@ -1,8 +1,9 @@ /// A file serialization format. /// /// Is implemented by `GraphSyntax` for graph files and `DatasetSyntax` for dataset files. +#[deprecated(note = "Use directly the methods on the implementing types")] pub trait FileSyntax: Sized { - /// Its canonical IRI according to [Unique URIs for file formats registry](https://www.w3.org/ns/formats/). + /// Its canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/). fn iri(self) -> &'static str; /// Its [IANA media type](https://tools.ietf.org/html/rfc2046). @@ -12,12 +13,6 @@ pub trait FileSyntax: Sized { fn file_extension(self) -> &'static str; /// Looks for a known syntax from a media type. - /// - /// Example: - /// ``` - /// use oxigraph::{GraphSyntax, FileSyntax}; - /// assert_eq!(GraphSyntax::from_mime_type("text/turtle; charset=utf-8"), Some(GraphSyntax::Turtle)) - /// ``` fn from_mime_type(media_type: &str) -> Option; } @@ -32,8 +27,15 @@ pub enum GraphSyntax { RdfXml, } -impl FileSyntax for GraphSyntax { - fn iri(self) -> &'static str { +impl GraphSyntax { + /// The syntax canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/). + /// + /// ``` + /// use oxigraph::io::GraphSyntax; + /// + /// assert_eq!(GraphSyntax::NTriples.iri(), "http://www.w3.org/ns/formats/N-Triples") + /// ``` + pub fn iri(self) -> &'static str { match self { GraphSyntax::NTriples => "http://www.w3.org/ns/formats/N-Triples", GraphSyntax::Turtle => "http://www.w3.org/ns/formats/Turtle", @@ -41,7 +43,14 @@ impl FileSyntax for GraphSyntax { } } - fn media_type(self) -> &'static str { + /// The syntax [IANA media type](https://tools.ietf.org/html/rfc2046). + /// + /// ``` + /// use oxigraph::io::GraphSyntax; + /// + /// assert_eq!(GraphSyntax::NTriples.media_type(), "application/n-triples") + /// ``` + pub fn media_type(self) -> &'static str { match self { GraphSyntax::NTriples => "application/n-triples", GraphSyntax::Turtle => "text/turtle", @@ -49,17 +58,34 @@ impl FileSyntax for GraphSyntax { } } - fn file_extension(self) -> &'static str { + /// The syntax [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension. + /// + /// ``` + /// use oxigraph::io::GraphSyntax; + /// + /// assert_eq!(GraphSyntax::NTriples.file_extension(), "nt") + /// ``` + pub fn file_extension(self) -> &'static str { match self { GraphSyntax::NTriples => "nt", GraphSyntax::Turtle => "ttl", GraphSyntax::RdfXml => "rdf", } } - - fn from_mime_type(media_type: &str) -> Option { + /// Looks for a known syntax from a media type. + /// + /// It supports some media type aliases. + /// For example "application/xml" is going to return `GraphSyntax::RdfXml` even if it is not its canonical media type. + /// + /// Example: + /// ``` + /// use oxigraph::io::GraphSyntax; + /// + /// assert_eq!(GraphSyntax::from_media_type("text/turtle; charset=utf-8"), Some(GraphSyntax::Turtle)) + /// ``` + pub fn from_media_type(media_type: &str) -> Option { if let Some(base_type) = media_type.split(';').next() { - match base_type { + match base_type.trim() { "application/n-triples" | "text/plain" => Some(GraphSyntax::NTriples), "text/turtle" | "application/turtle" | "application/x-turtle" => { Some(GraphSyntax::Turtle) @@ -73,6 +99,25 @@ impl FileSyntax for GraphSyntax { } } +#[allow(deprecated)] +impl FileSyntax for GraphSyntax { + fn iri(self) -> &'static str { + self.iri() + } + + fn media_type(self) -> &'static str { + self.media_type() + } + + fn file_extension(self) -> &'static str { + self.file_extension() + } + + fn from_mime_type(media_type: &str) -> Option { + Self::from_media_type(media_type) + } +} + /// [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) serialization formats. #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] pub enum DatasetSyntax { @@ -82,31 +127,61 @@ pub enum DatasetSyntax { TriG, } -impl FileSyntax for DatasetSyntax { - fn iri(self) -> &'static str { +impl DatasetSyntax { + /// The syntax canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/). + /// + /// ``` + /// use oxigraph::io::DatasetSyntax; + /// + /// assert_eq!(DatasetSyntax::NQuads.iri(), "http://www.w3.org/ns/formats/N-Quads") + /// ``` + pub fn iri(self) -> &'static str { match self { DatasetSyntax::NQuads => "http://www.w3.org/ns/formats/N-Quads", DatasetSyntax::TriG => "http://www.w3.org/ns/formats/TriG", } } - fn media_type(self) -> &'static str { + /// The syntax [IANA media type](https://tools.ietf.org/html/rfc2046). + /// + /// ``` + /// use oxigraph::io::DatasetSyntax; + /// + /// assert_eq!(DatasetSyntax::NQuads.media_type(), "application/n-quads") + /// ``` + pub fn media_type(self) -> &'static str { match self { DatasetSyntax::NQuads => "application/n-quads", DatasetSyntax::TriG => "application/trig", } } - fn file_extension(self) -> &'static str { + /// The syntax [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension. + /// + /// ``` + /// use oxigraph::io::DatasetSyntax; + /// + /// assert_eq!(DatasetSyntax::NQuads.file_extension(), "nq") + /// ``` + pub fn file_extension(self) -> &'static str { match self { DatasetSyntax::NQuads => "nq", DatasetSyntax::TriG => "trig", } } - - fn from_mime_type(media_type: &str) -> Option { + /// Looks for a known syntax from a media type. + /// + /// It supports some media type aliases. + /// + /// Example: + /// ``` + /// use oxigraph::io::DatasetSyntax; + /// + /// assert_eq!(DatasetSyntax::from_media_type("application/n-quads; charset=utf-8"), Some(DatasetSyntax::NQuads)) + /// ``` + pub fn from_media_type(media_type: &str) -> Option { if let Some(base_type) = media_type.split(';').next() { - match base_type { + match base_type.trim() { "application/n-quads" | "text/x-nquads" | "text/nquads" => { Some(DatasetSyntax::NQuads) } @@ -118,3 +193,22 @@ impl FileSyntax for DatasetSyntax { } } } + +#[allow(deprecated)] +impl FileSyntax for DatasetSyntax { + fn iri(self) -> &'static str { + self.iri() + } + + fn media_type(self) -> &'static str { + self.media_type() + } + + fn file_extension(self) -> &'static str { + self.file_extension() + } + + fn from_mime_type(media_type: &str) -> Option { + Self::from_media_type(media_type) + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 7c04213e..8d159466 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -104,18 +104,22 @@ #![doc(test(attr(deny(warnings))))] mod error; +pub mod io; pub mod model; pub mod sparql; pub mod store; -mod syntax; pub use error::Error; pub type Result = ::std::result::Result; +#[deprecated(note = "Use oxigraph::io::DatasetSyntax instead")] +pub use crate::io::DatasetSyntax; +#[deprecated(note = "Use oxigraph::io::FileSyntax instead")] +#[allow(deprecated)] +pub use crate::io::FileSyntax; +#[deprecated(note = "Use oxigraph::io::GraphSyntax instead")] +pub use crate::io::GraphSyntax; pub use crate::store::memory::MemoryStore; #[cfg(feature = "rocksdb")] pub use crate::store::rocksdb::RocksDbStore; #[cfg(feature = "sled")] pub use crate::store::sled::SledStore; -pub use crate::syntax::DatasetSyntax; -pub use crate::syntax::FileSyntax; -pub use crate::syntax::GraphSyntax; diff --git a/lib/src/sparql/model.rs b/lib/src/sparql/model.rs index 2738538f..b7e3d7fe 100644 --- a/lib/src/sparql/model.rs +++ b/lib/src/sparql/model.rs @@ -1,8 +1,9 @@ +#[allow(deprecated)] +use crate::io::{FileSyntax, GraphSyntax}; use crate::model::*; use crate::sparql::json_results::write_json_results; use crate::sparql::xml_results::{read_xml_results, write_xml_results}; -use crate::Error; -use crate::{FileSyntax, GraphSyntax, Result}; +use crate::{Error, Result}; use rand::random; use rio_api::formatter::TriplesFormatter; use rio_turtle::{NTriplesFormatter, TurtleFormatter}; @@ -120,29 +121,60 @@ pub enum QueryResultSyntax { Json, } -impl FileSyntax for QueryResultSyntax { - fn iri(self) -> &'static str { +impl QueryResultSyntax { + /// The syntax canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/). + /// + /// ``` + /// use oxigraph::sparql::QueryResultSyntax; + /// + /// assert_eq!(QueryResultSyntax::Json.iri(), "http://www.w3.org/ns/formats/SPARQL_Results_JSON") + /// ``` + pub fn iri(self) -> &'static str { match self { QueryResultSyntax::Xml => "http://www.w3.org/ns/formats/SPARQL_Results_XML", QueryResultSyntax::Json => "http://www.w3.org/ns/formats/SPARQL_Results_JSON", } } - - fn media_type(self) -> &'static str { + /// The syntax [IANA media type](https://tools.ietf.org/html/rfc2046). + /// + /// ``` + /// use oxigraph::sparql::QueryResultSyntax; + /// + /// assert_eq!(QueryResultSyntax::Json.media_type(), "application/sparql-results+json") + /// ``` + pub fn media_type(self) -> &'static str { match self { QueryResultSyntax::Xml => "application/sparql-results+xml", QueryResultSyntax::Json => "application/sparql-results+json", } } - fn file_extension(self) -> &'static str { + /// The syntax [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension. + /// + /// ``` + /// use oxigraph::sparql::QueryResultSyntax; + /// + /// assert_eq!(QueryResultSyntax::Json.file_extension(), "srj") + /// ``` + pub fn file_extension(self) -> &'static str { match self { QueryResultSyntax::Xml => "srx", QueryResultSyntax::Json => "srj", } } - fn from_mime_type(media_type: &str) -> Option { + /// Looks for a known syntax from a media type. + /// + /// It supports some media type aliases. + /// For example "application/xml" is going to return `QueryResultSyntax::Xml` even if it is not its canonical media type. + /// + /// Example: + /// ``` + /// use oxigraph::sparql::QueryResultSyntax; + /// + /// assert_eq!(QueryResultSyntax::from_media_type("application/sparql-results+json; charset=utf-8"), Some(QueryResultSyntax::Json)) + /// ``` + pub fn from_media_type(media_type: &str) -> Option { if let Some(base_type) = media_type.split(';').next() { match base_type { "application/sparql-results+xml" | "application/xml" | "text/xml" => { @@ -159,6 +191,25 @@ impl FileSyntax for QueryResultSyntax { } } +#[allow(deprecated)] +impl FileSyntax for QueryResultSyntax { + fn iri(self) -> &'static str { + self.iri() + } + + fn media_type(self) -> &'static str { + self.media_type() + } + + fn file_extension(self) -> &'static str { + self.file_extension() + } + + fn from_mime_type(media_type: &str) -> Option { + Self::from_media_type(media_type) + } +} + /// An iterator over query result solutions /// /// ``` diff --git a/python/src/memory_store.rs b/python/src/memory_store.rs index f6b99f87..9f097e50 100644 --- a/python/src/memory_store.rs +++ b/python/src/memory_store.rs @@ -2,7 +2,7 @@ use crate::model::*; use crate::store_utils::*; use oxigraph::model::*; use oxigraph::sparql::QueryOptions; -use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, MemoryStore}; +use oxigraph::{DatasetSyntax, GraphSyntax, MemoryStore}; use pyo3::basic::CompareOp; use pyo3::exceptions::{NotImplementedError, ValueError}; use pyo3::prelude::*; @@ -81,7 +81,7 @@ impl PyMemoryStore { Some(base_iri) }; - if let Some(graph_syntax) = GraphSyntax::from_mime_type(mime_type) { + if let Some(graph_syntax) = GraphSyntax::from_media_type(mime_type) { self.inner .load_graph( Cursor::new(data), @@ -90,7 +90,7 @@ impl PyMemoryStore { base_iri, ) .map_err(|e| ParseError::py_err(e.to_string())) - } else if let Some(dataset_syntax) = DatasetSyntax::from_mime_type(mime_type) { + } else if let Some(dataset_syntax) = DatasetSyntax::from_media_type(mime_type) { if to_graph_name.is_some() { return Err(ValueError::py_err( "The target graph name parameter is not available for dataset formats", diff --git a/python/src/sled_store.rs b/python/src/sled_store.rs index 90795f97..b3b2c2ee 100644 --- a/python/src/sled_store.rs +++ b/python/src/sled_store.rs @@ -2,7 +2,7 @@ use crate::model::*; use crate::store_utils::*; use oxigraph::model::*; use oxigraph::sparql::QueryOptions; -use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, SledStore}; +use oxigraph::{DatasetSyntax, GraphSyntax, SledStore}; use pyo3::exceptions::{IOError, ValueError}; use pyo3::prelude::*; use pyo3::types::PyTuple; @@ -87,7 +87,7 @@ impl PySledStore { Some(base_iri) }; - if let Some(graph_syntax) = GraphSyntax::from_mime_type(mime_type) { + if let Some(graph_syntax) = GraphSyntax::from_media_type(mime_type) { self.inner .load_graph( Cursor::new(data), @@ -96,7 +96,7 @@ impl PySledStore { base_iri, ) .map_err(|e| ParseError::py_err(e.to_string())) - } else if let Some(dataset_syntax) = DatasetSyntax::from_mime_type(mime_type) { + } else if let Some(dataset_syntax) = DatasetSyntax::from_media_type(mime_type) { if to_graph_name.is_some() { return Err(ValueError::py_err( "The target graph name parameter is not available for dataset formats", diff --git a/server/src/main.rs b/server/src/main.rs index 05c1ff12..c1227989 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -18,7 +18,7 @@ use async_std::task::{block_on, spawn, spawn_blocking}; use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode}; use oxigraph::model::GraphName; use oxigraph::sparql::{Query, QueryOptions, QueryResult, QueryResultSyntax}; -use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, RocksDbStore}; +use oxigraph::{DatasetSyntax, GraphSyntax, RocksDbStore}; use std::str::FromStr; use url::form_urlencoded; @@ -60,7 +60,7 @@ async fn handle_request(request: Request, store: RocksDbStore) -> Result { if let Some(content_type) = request.content_type() { - match if let Some(format) = GraphSyntax::from_mime_type(content_type.essence()) { + match if let Some(format) = GraphSyntax::from_media_type(content_type.essence()) { spawn_blocking(move || { store.load_graph( SyncAsyncBufReader::from(request), @@ -69,7 +69,8 @@ async fn handle_request(request: Request, store: RocksDbStore) -> Result(request: Request, supported: &[&str]) -> Result { +fn content_negotiation( + request: Request, + supported: &[&str], + parse: impl Fn(&str) -> Option, +) -> Result { let header = request .header(headers::ACCEPT) .map(|h| h.last().as_str().trim()) @@ -272,7 +279,7 @@ fn content_negotiation(request: Request, supported: &[&str]) -> R } } - F::from_mime_type(result.essence()) + parse(result.essence()) .ok_or_else(|| Error::from_str(StatusCode::InternalServerError, "Unknown mime type")) } diff --git a/wikibase/src/main.rs b/wikibase/src/main.rs index 31f30c05..0aa2ada8 100644 --- a/wikibase/src/main.rs +++ b/wikibase/src/main.rs @@ -17,7 +17,7 @@ use async_std::prelude::*; use async_std::task::{spawn, spawn_blocking}; use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode}; use oxigraph::sparql::{Query, QueryOptions, QueryResult, QueryResultSyntax}; -use oxigraph::{FileSyntax, GraphSyntax, RocksDbStore}; +use oxigraph::{GraphSyntax, RocksDbStore}; use std::str::FromStr; use std::time::Duration; use url::form_urlencoded; @@ -188,6 +188,7 @@ async fn evaluate_sparql_query( GraphSyntax::Turtle.media_type(), GraphSyntax::RdfXml.media_type(), ], + GraphSyntax::from_media_type, )?; let mut body = Vec::default(); results.write_graph(&mut body, format)?; @@ -201,6 +202,7 @@ async fn evaluate_sparql_query( QueryResultSyntax::Xml.media_type(), QueryResultSyntax::Json.media_type(), ], + QueryResultSyntax::from_media_type, )?; let mut body = Vec::default(); results.write(&mut body, format)?; @@ -246,7 +248,11 @@ async fn http_server< Ok(()) } -fn content_negotiation(request: Request, supported: &[&str]) -> Result { +fn content_negotiation( + request: Request, + supported: &[&str], + parse: impl Fn(&str) -> Option, +) -> Result { let header = request .header(headers::ACCEPT) .map(|h| h.last().as_str().trim()) @@ -282,6 +288,6 @@ fn content_negotiation(request: Request, supported: &[&str]) -> R } } - F::from_mime_type(result.essence()) + parse(result.essence()) .ok_or_else(|| Error::from_str(StatusCode::InternalServerError, "Unknown mime type")) }