use crate::{
ClassPartition, Dataset, Feature, GraphCollection, GraphDescription, NamedGraphDescription, PropertyPartition,
SD_BASIC_FEDERATED_QUERY_STR, SD_DEREFERENCES_URIS_STR, SD_EMPTY_GRAPHS_STR, SD_REQUIRES_DATASET_STR,
SD_SPARQL10_QUERY_STR, SD_SPARQL11_QUERY_STR, SD_SPARQL11_UPDATE_STR, SD_UNION_DEFAULT_GRAPH_STR,
ServiceDescription, ServiceDescriptionError, SparqlResultFormat, SupportedLanguage, dct_title, sd_available_graphs,
sd_default_dataset, sd_default_graph, sd_endpoint, sd_feature, sd_graph, sd_name, sd_named_graph, sd_result_format,
sd_service, sd_supported_language, void_class, void_class_partition, void_classes, void_property,
void_property_partition, void_triples,
};
use rudof_iri::IriS;
use rudof_rdf::rdf_core::{
FocusRDF, RDFError,
parser::{
RDFParse,
rdf_node_parser::{
ParserExt, RDFNodeParse,
constructors::{
FocusParser, IriOrBlankNodeParser, IrisPropertyParser, SetFocusParser, SingleIntegerPropertyParser,
SingleIriOrBlankNodePropertyParser, SingleIriPropertyParser, SingleStringPropertyParser, SuccessParser,
},
},
},
term::{IriOrBlankNode, literal::NumericLiteral},
};
use std::{collections::HashSet, fmt::Debug};
use tracing::trace;
pub struct ServiceDescriptionParser<RDF>
where
RDF: FocusRDF + Debug,
{
rdf_parser: RDFParse<RDF>,
}
impl<RDF> ServiceDescriptionParser<RDF>
where
RDF: FocusRDF + Debug + 'static,
RDF::Term: From<IriOrBlankNode> + TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
pub fn new(rdf: RDF) -> ServiceDescriptionParser<RDF> {
ServiceDescriptionParser {
rdf_parser: RDFParse::new(rdf),
}
}
pub fn parse(&mut self) -> Result<ServiceDescription, ServiceDescriptionError> {
let service_node = self.rdf_parser.find_single_instance(sd_service().clone())?;
let term: RDF::Term = service_node.into();
self.rdf_parser.set_focus(&term);
let service = Self::service_description().parse_focused(self.rdf_parser.rdf_mut())?;
Ok(service.with_prefixmap(self.rdf_parser.prefixmap()))
}
pub fn service_description() -> impl RDFNodeParse<RDF, Output = ServiceDescription>
where
RDF: FocusRDF + 'static,
RDF::Term: From<IriOrBlankNode> + TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
IriOrBlankNodeParser::new().then(|focus| {
let focus_term: RDF::Term = focus.clone().into();
endpoint().then(move |maybe_iri| {
let focus_term = focus_term.clone();
supported_language().then(move |supported_language| {
result_format().then({
let focus_term = focus_term.clone();
let iri = maybe_iri.clone();
move |result_format| {
feature().then({
let focus_term = focus_term.clone();
let sl = supported_language.clone();
let iri = iri.clone();
move |feature| {
default_dataset(focus_term.clone()).optional().then({
let focus_term = focus_term.clone();
let iri = iri.clone();
let sl = sl.clone();
let result_format = result_format.clone();
move |default_ds| {
let focus_term = focus_term.clone();
let iri = iri.clone();
let sl = sl.clone();
let result_format = result_format.clone();
let feature = feature.clone();
available_graphs(focus_term.clone()).then({
move |ags| {
let focus_term = focus_term.clone();
let iri = iri.clone();
let sl = sl.clone();
let result_format = result_format.clone();
let feature = feature.clone();
let ags = ags.clone();
let default_ds = default_ds.clone();
title(focus_term).then(move |title| {
let mut sd = ServiceDescription::new()
.with_endpoint(iri.clone())
.with_available_graphs(ags.clone())
.with_default_dataset(default_ds.clone());
sd.add_title(title.as_deref());
sd.add_supported_languages(sl.clone());
sd.add_features(feature.clone());
sd.add_result_formats(result_format.clone());
SuccessParser::new(sd)
})
}
})
}
})
}
})
}
})
})
})
})
}
}
pub fn title<RDF>(focus: RDF::Term) -> impl RDFNodeParse<RDF, Output = Option<String>>
where
RDF: FocusRDF + 'static,
RDF::Term: Clone,
{
SetFocusParser::new(focus).with(SingleStringPropertyParser::new(dct_title().clone()).optional())
}
pub fn endpoint<RDF>() -> impl RDFNodeParse<RDF, Output = Option<IriS>>
where
RDF: FocusRDF + 'static,
{
SingleIriPropertyParser::new(sd_endpoint().clone()).optional()
}
pub fn feature<RDF>() -> impl RDFNodeParse<RDF, Output = HashSet<Feature>>
where
RDF: FocusRDF,
{
IrisPropertyParser::new(sd_feature().clone()).flat_map(|ref iris| {
let features = get_features(iris)?;
Ok(features)
})
}
pub fn result_format<RDF>() -> impl RDFNodeParse<RDF, Output = HashSet<SparqlResultFormat>>
where
RDF: FocusRDF,
{
IrisPropertyParser::new(sd_result_format().clone()).flat_map(|ref iris| {
let result_format = get_result_formats(iris)?;
Ok(result_format)
})
}
pub fn supported_language<RDF>() -> impl RDFNodeParse<RDF, Output = HashSet<SupportedLanguage>>
where
RDF: FocusRDF,
{
IrisPropertyParser::new(sd_supported_language().clone()).flat_map(|ref iris| {
let langs = get_supported_languages(iris)?;
Ok(langs)
})
}
fn get_supported_languages(iris: &HashSet<IriS>) -> Result<HashSet<SupportedLanguage>, RDFError> {
let mut res = HashSet::new();
for i in iris {
let supported_language = supported_language_iri(i)?;
res.insert(supported_language);
}
Ok(res)
}
fn get_features(iris: &HashSet<IriS>) -> Result<HashSet<Feature>, RDFError> {
let mut res = HashSet::new();
for i in iris {
let feature = feature_iri(i)?;
res.insert(feature);
}
Ok(res)
}
fn get_result_formats(iris: &HashSet<IriS>) -> Result<HashSet<SparqlResultFormat>, RDFError> {
let mut res = HashSet::new();
for i in iris {
let res_format = result_format_iri(i)?;
res.insert(res_format);
}
Ok(res)
}
fn supported_language_iri(iri: &IriS) -> Result<SupportedLanguage, RDFError> {
match iri.as_str() {
SD_SPARQL10_QUERY_STR => Ok(SupportedLanguage::SPARQL10Query),
SD_SPARQL11_QUERY_STR => Ok(SupportedLanguage::SPARQL11Query),
SD_SPARQL11_UPDATE_STR => Ok(SupportedLanguage::SPARQL11Update),
_ => Err(RDFError::DefaultError {
msg: format!("Unexpected value for supported language: {iri}"),
}),
}
}
fn result_format_iri(iri: &IriS) -> Result<SparqlResultFormat, RDFError> {
let rf = match iri.as_str() {
"http://www.w3.org/ns/formats/SPARQL_Results_XML" => SparqlResultFormat::Xml,
"http://www.w3.org/ns/formats/JSON-LD" => SparqlResultFormat::JsonLD,
"http://www.w3.org/ns/formats/N-Triples" => SparqlResultFormat::NTriples,
"http://www.w3.org/ns/formats/SPARQL_Results_CSV" => SparqlResultFormat::Csv,
"http://www.w3.org/ns/formats/SPARQL_Results_JSON" => SparqlResultFormat::Json,
"http://www.w3.org/ns/formats/Turtle" => SparqlResultFormat::Turtle,
"http://www.w3.org/ns/formats/SPARQL_Results_TSV" => SparqlResultFormat::Tsv,
"http://www.w3.org/ns/formats/RDF_XML" => SparqlResultFormat::RdfXml,
_ => SparqlResultFormat::Other(iri.clone()),
};
Ok(rf)
}
fn feature_iri(iri: &IriS) -> Result<Feature, RDFError> {
match iri.as_str() {
SD_BASIC_FEDERATED_QUERY_STR => Ok(Feature::BasicFederatedQuery),
SD_UNION_DEFAULT_GRAPH_STR => Ok(Feature::UnionDefaultGraph),
SD_EMPTY_GRAPHS_STR => Ok(Feature::EmptyGraphs),
SD_REQUIRES_DATASET_STR => Ok(Feature::RequiresDataset),
SD_DEREFERENCES_URIS_STR => Ok(Feature::DereferencesURIs),
_ => Ok(Feature::Other(iri.clone())),
}
}
pub fn available_graphs<RDF>(node: RDF::Term) -> impl RDFNodeParse<RDF, Output = Vec<GraphCollection>>
where
RDF: FocusRDF + 'static,
RDF::Term: From<IriOrBlankNode> + TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
SetFocusParser::new(node).with(available_graph().map_property(sd_available_graphs().clone()))
}
pub fn available_graph<RDF>() -> impl RDFNodeParse<RDF, Output = GraphCollection>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + From<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
FocusParser::new().then(|focus: RDF::Term| {
let focus_clone = focus.clone();
named_graph()
.map_property(sd_named_graph().clone())
.map(move |named_graphs| {
let iri_or_blank: IriOrBlankNode = focus_clone
.clone()
.try_into()
.expect("Failed to convert Term to IriOrBlankNode");
GraphCollection::new(&iri_or_blank).with_collection(named_graphs.into_iter())
})
})
}
pub fn default_dataset<RDF>(node: RDF::Term) -> impl RDFNodeParse<RDF, Output = Dataset>
where
RDF: FocusRDF + 'static,
RDF::Term: From<IriOrBlankNode> + TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
SetFocusParser::new(node)
.with(SingleIriOrBlankNodePropertyParser::new(sd_default_dataset().clone()).then(|node_ds| dataset(node_ds)))
}
pub fn dataset<RDF>(node_ds: IriOrBlankNode) -> impl RDFNodeParse<RDF, Output = Dataset>
where
RDF: FocusRDF + 'static,
RDF::Term: From<IriOrBlankNode> + TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
let node_term: RDF::Term = node_ds.clone().into();
SetFocusParser::new(node_term.clone()).with(
FocusParser::new()
.and(default_graph(node_term.clone()).optional())
.and(named_graphs(node_term))
.then(move |((_, dg), named_gs)| {
SuccessParser::new(
Dataset::new(&node_ds)
.with_default_graph(dg)
.with_named_graphs(named_gs),
)
}),
)
}
pub fn default_graph<RDF>(focus: RDF::Term) -> impl RDFNodeParse<RDF, Output = GraphDescription>
where
RDF: FocusRDF + 'static,
RDF::Term: From<IriOrBlankNode> + TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
trace!("parsing default_graph with focus");
SetFocusParser::new(focus).with(
SingleIriOrBlankNodePropertyParser::new(sd_default_graph().clone())
.then(|node| graph_description(node.clone())),
)
}
pub fn graph_description<RDF>(node: IriOrBlankNode) -> impl RDFNodeParse<RDF, Output = GraphDescription>
where
RDF: FocusRDF + 'static,
RDF::Term: From<IriOrBlankNode> + TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
trace!("parsing graph_description: focus={node}");
let node_term: RDF::Term = node.clone().into();
SetFocusParser::new(node_term.clone()).with(
FocusParser::new()
.and(parse_void_triples(node_term.clone()))
.and(parse_void_classes(node_term.clone()))
.and(parse_void_class_partition(node_term.clone()))
.and(parse_void_property_partition(node_term.clone()))
.map(
move |((((_, triples), classes), class_partition), property_partition)| {
let d = GraphDescription::new(&node)
.with_triples(triples)
.with_classes(classes)
.with_class_partition(class_partition)
.with_property_partition(property_partition);
trace!("parsed graph_description: {d}");
d
},
),
)
}
pub fn named_graphs<RDF>(focus: RDF::Term) -> impl RDFNodeParse<RDF, Output = Vec<NamedGraphDescription>>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + From<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
SetFocusParser::new(focus).with(named_graph().map_property(sd_named_graph().clone()))
}
pub fn named_graph<RDF>() -> impl RDFNodeParse<RDF, Output = NamedGraphDescription>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + From<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
FocusParser::new().then(|focus| named_graph_description(focus))
}
fn named_graph_description<RDF>(focus: RDF::Term) -> impl RDFNodeParse<RDF, Output = NamedGraphDescription>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + From<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
trace!("parsing named_graph_description with focus");
let focus_clone = focus.clone();
SetFocusParser::new(focus).with(
FocusParser::new()
.and(name())
.and(graph().map_property(sd_graph().clone()))
.map(move |((_, name), graphs)| {
let focus_iri: IriOrBlankNode = focus_clone
.clone()
.try_into()
.expect("Failed to convert Term to IriOrBlankNode");
trace!(
"named_graph_description: focus={focus_iri}, name={name}, graphs={}",
graphs.len()
);
NamedGraphDescription::new(Some(focus_iri), name).with_graphs(graphs)
}),
)
}
fn name<RDF>() -> impl RDFNodeParse<RDF, Output = IriS>
where
RDF: FocusRDF + 'static,
{
SingleIriPropertyParser::new(sd_name().clone())
}
fn graph<RDF>() -> impl RDFNodeParse<RDF, Output = GraphDescription>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + From<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
FocusParser::new().then(|focus: RDF::Term| {
trace!("Parsing graph at focus, parsing it...");
let iri_or_blank: IriOrBlankNode = focus.try_into().expect("Failed to convert Term to IriOrBlankNode");
graph_description(iri_or_blank)
})
}
pub fn parse_void_triples<RDF>(node: RDF::Term) -> impl RDFNodeParse<RDF, Output = Option<NumericLiteral>>
where
RDF: FocusRDF + 'static,
RDF::Term: Clone,
{
SetFocusParser::new(node).with(
SingleIntegerPropertyParser::new(void_triples().clone())
.map(|n: isize| NumericLiteral::Integer(n as i128))
.optional(),
)
}
pub fn parse_void_classes<RDF>(node: RDF::Term) -> impl RDFNodeParse<RDF, Output = Option<NumericLiteral>>
where
RDF: FocusRDF + 'static,
RDF::Term: Clone,
{
SetFocusParser::new(node).with(
SingleIntegerPropertyParser::new(void_classes().clone())
.map(|n: isize| NumericLiteral::Integer(n as i128))
.optional(),
)
}
pub fn parse_void_class_partition<RDF>(node: RDF::Term) -> impl RDFNodeParse<RDF, Output = Vec<ClassPartition>>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
SetFocusParser::new(node).with(class_partition().map_property(void_class_partition().clone()))
}
pub fn parse_void_property_partition<RDF>(node: RDF::Term) -> impl RDFNodeParse<RDF, Output = Vec<PropertyPartition>>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
SetFocusParser::new(node).with(property_partition().map_property(void_property_partition().clone()))
}
pub fn class_partition<RDF>() -> impl RDFNodeParse<RDF, Output = ClassPartition>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
trace!("parsing class_partition");
FocusParser::new().then(move |focus: RDF::Term| {
let focus_clone = focus.clone();
SuccessParser::new(focus)
.and(SingleIriPropertyParser::new(void_class().clone()))
.and(property_partition().map_property(void_property().clone()))
.map(move |((_, class), property_partition)| {
let focus_iri: IriOrBlankNode = focus_clone
.clone()
.try_into()
.expect("Failed to convert Term to IriOrBlankNode");
ClassPartition::new(&class)
.with_id(&focus_iri)
.with_property_partition(property_partition)
})
})
}
pub fn property_partition<RDF>() -> impl RDFNodeParse<RDF, Output = crate::PropertyPartition>
where
RDF: FocusRDF + 'static,
RDF::Term: TryInto<IriOrBlankNode> + Clone,
<RDF::Term as TryInto<IriOrBlankNode>>::Error: Debug,
{
FocusParser::new()
.and(SingleIriPropertyParser::new(void_property().clone()).map(|p| p.clone()))
.and(
SingleIntegerPropertyParser::new(void_triples().clone())
.optional()
.map(|opt: Option<isize>| opt.map(|n| NumericLiteral::Integer(n as i128))),
)
.map(
|((focus, property), triples): ((RDF::Term, IriS), Option<NumericLiteral>)| {
let focus_iri: IriOrBlankNode = focus.try_into().expect("Failed to convert Term to IriOrBlankNode");
PropertyPartition::new(&property)
.with_id(&focus_iri)
.with_triples(triples)
},
)
}