Skip to main content

rudof_rdf/rdf_core/
rdf_data_config.rs

1use crate::rdf_core::{RDFError, visualizer::RDFVisualizationConfig};
2use std::{collections::HashMap, io, path::Path, str::FromStr};
3
4use prefixmap::PrefixMap;
5
6use iri_s::{IriS, error::IriSError};
7use serde::{Deserialize, Serialize};
8use std::io::Read;
9
10/// Configuration for RDF data readers and visualization settings.
11///
12/// This struct defines how RDF data should be processed, including base IRI resolution,
13/// SPARQL endpoints for querying external data, and visualization preferences.
14#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
15pub struct RdfDataConfig {
16    /// Default base IRI to resolve relative IRIs. If `None`, relative IRIs will be treated as errors.
17    pub base: Option<IriS>,
18
19    /// SPARQL endpoints for querying RDF data. Each endpoint is identified by a unique name.
20    pub endpoints: Option<HashMap<String, EndpointDescription>>,
21
22    /// If true, automatically set the base IRI to the local file or URI of the document being processed.
23    pub automatic_base: Option<bool>,
24
25    /// Configuration for RDF visualization appearance and styling.
26    pub rdf_visualization: Option<RDFVisualizationConfig>,
27}
28
29impl RdfDataConfig {
30    /// Creates a new `RdfDataConfig` with default settings.
31    ///
32    /// The default configuration has no base IRI, no endpoints, automatic base detection enabled,
33    /// and no custom visualization settings.
34    pub fn new() -> RdfDataConfig {
35        RdfDataConfig {
36            base: None,
37            endpoints: None,
38            automatic_base: Some(true),
39            rdf_visualization: None,
40        }
41    }
42
43    /// Adds a Wikidata SPARQL endpoint to the configuration.
44    ///
45    /// This method configures the Wikidata query service endpoint with appropriate prefixes
46    /// for convenient querying of Wikidata's knowledge graph.
47    ///
48    /// # Returns
49    /// The modified `RdfDataConfig` with the Wikidata endpoint added.
50    pub fn with_wikidata(mut self) -> Self {
51        let wikidata_name = "wikidata";
52        let wikidata_iri = "https://query.wikidata.org/sparql";
53        let wikidata = EndpointDescription::new_unchecked(wikidata_iri).with_prefixmap(PrefixMap::wikidata());
54
55        match self.endpoints {
56            None => {
57                self.endpoints = Some(HashMap::from([(wikidata_name.to_string(), wikidata)]));
58            },
59            Some(ref mut map) => {
60                map.insert(wikidata_name.to_string(), wikidata);
61            },
62        };
63        self
64    }
65
66    /// Loads an `RdfDataConfig` from a TOML file at the specified path.
67    ///
68    /// # Arguments
69    /// * `path` - Path to the TOML configuration file.
70    ///
71    /// # Returns
72    /// A `Result` containing the parsed configuration or an error if reading/parsing fails.
73    ///
74    /// # Errors
75    /// Returns `RDFError` if the file cannot be read or the TOML is invalid.
76    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<RdfDataConfig, RDFError> {
77        let path_name = path.as_ref().display().to_string();
78        let f = std::fs::File::open(path).map_err(|e| RDFError::ReadingConfigError {
79            path_name: path_name.clone(),
80            error: e,
81        })?;
82        let s = read_string(f).map_err(|e| RDFError::ReadingConfigError {
83            path_name: path_name.clone(),
84            error: e,
85        })?;
86        let config: RdfDataConfig = toml::from_str(s.as_str()).map_err(|e| RDFError::TomlError {
87            path_name: path_name.to_string(),
88            error: e,
89        })?;
90        Ok(config)
91    }
92
93    /// Gets the RDF visualization configuration, using defaults if none is set.
94    ///
95    /// # Returns
96    /// The `RDFVisualizationConfig` to use for visualization, either from this config
97    /// or the default configuration if none is specified.
98    pub fn rdf_visualization_config(&self) -> RDFVisualizationConfig {
99        self.rdf_visualization.clone().unwrap_or_default()
100    }
101}
102
103impl Default for RdfDataConfig {
104    /// Returns the default RDF data configuration with Wikidata endpoint pre-configured.
105    ///
106    /// The default configuration includes the Wikidata SPARQL endpoint and automatic
107    /// base IRI detection enabled.
108    fn default() -> Self {
109        Self::new().with_wikidata()
110    }
111}
112
113/// Description of a SPARQL endpoint for querying RDF data.
114///
115/// This struct contains the necessary information to connect to and query a SPARQL endpoint,
116/// including URLs for queries and updates, and optional prefix mappings.
117#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118pub struct EndpointDescription {
119    /// The URL of the SPARQL query endpoint.
120    query_url: IriS,
121    /// Optional URL for SPARQL update operations.
122    update_url: Option<IriS>,
123    /// Optional prefix map for abbreviating IRIs in queries.
124    prefixmap: Option<PrefixMap>,
125}
126
127impl EndpointDescription {
128    /// Creates a new `EndpointDescription` from a URL string without validation.
129    ///
130    /// # Arguments
131    /// * `str` - The URL string for the SPARQL query endpoint.
132    pub fn new_unchecked(str: &str) -> Self {
133        EndpointDescription {
134            query_url: IriS::new_unchecked(str),
135            update_url: None,
136            prefixmap: None,
137        }
138    }
139
140    /// Returns the query URL for this endpoint.
141    ///
142    /// # Returns
143    /// A reference to the `IriS` representing the SPARQL query endpoint URL.
144    pub fn query_url(&self) -> &IriS {
145        &self.query_url
146    }
147
148    /// Returns the prefix map for this endpoint, or a default empty map if none is set.
149    ///
150    /// # Returns
151    /// The `PrefixMap` containing IRI prefixes for query abbreviation.
152    pub fn prefixmap(&self) -> PrefixMap {
153        self.prefixmap.clone().unwrap_or_default()
154    }
155
156    /// Sets the prefix map for this endpoint.
157    ///
158    /// # Arguments
159    /// * `prefixmap` - The `PrefixMap` to associate with this endpoint.
160    ///
161    /// # Returns
162    /// The modified `EndpointDescription` with the new prefix map.
163    pub fn with_prefixmap(mut self, prefixmap: PrefixMap) -> Self {
164        self.prefixmap = Some(prefixmap);
165        self
166    }
167
168    /// Adds or replaces the prefix map for this endpoint.
169    ///
170    /// # Arguments
171    /// * `prefixmap` - The `PrefixMap` to set for this endpoint.
172    pub fn add_prefixmap(&mut self, prefixmap: PrefixMap) {
173        self.prefixmap = Some(prefixmap);
174    }
175}
176
177impl FromStr for EndpointDescription {
178    type Err = IriSError;
179
180    /// Parses an `EndpointDescription` from a URL string.
181    ///
182    /// This validates that the provided string is a valid IRI before creating the endpoint description.
183    ///
184    /// # Arguments
185    /// * `query_url` - The URL string to parse as the SPARQL query endpoint.
186    ///
187    /// # Returns
188    /// A `Result` containing the parsed `EndpointDescription` or an `IriSError` if parsing fails.
189    fn from_str(query_url: &str) -> Result<Self, Self::Err> {
190        let iri = IriS::from_str(query_url)?;
191        Ok(EndpointDescription {
192            query_url: iri,
193            update_url: None,
194            prefixmap: None,
195        })
196    }
197}
198
199/// Reads the entire contents of a reader into a string.
200///
201/// # Arguments
202/// * `reader` - The reader to read from.
203///
204/// # Returns
205/// A `Result` containing the string content or an I/O error.
206fn read_string<R: Read>(mut reader: R) -> io::Result<String> {
207    let mut buf = String::new();
208    reader.read_to_string(&mut buf)?;
209    Ok(buf)
210}