sparesults/
format.rs

1use std::fmt;
2
3/// [SPARQL query](https://www.w3.org/TR/sparql11-query/) results serialization formats.
4#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
5#[non_exhaustive]
6pub enum QueryResultsFormat {
7    /// [SPARQL Query Results XML Format](https://www.w3.org/TR/rdf-sparql-XMLres/)
8    Xml,
9    /// [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/)
10    Json,
11    /// [SPARQL Query Results CSV Format](https://www.w3.org/TR/sparql11-results-csv-tsv/)
12    Csv,
13    /// [SPARQL Query Results TSV Format](https://www.w3.org/TR/sparql11-results-csv-tsv/)
14    Tsv,
15}
16
17impl QueryResultsFormat {
18    /// The format canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/).
19    ///
20    /// ```
21    /// use sparesults::QueryResultsFormat;
22    ///
23    /// assert_eq!(
24    ///     QueryResultsFormat::Json.iri(),
25    ///     "http://www.w3.org/ns/formats/SPARQL_Results_JSON"
26    /// )
27    /// ```
28    #[inline]
29    pub fn iri(self) -> &'static str {
30        match self {
31            Self::Xml => "http://www.w3.org/ns/formats/SPARQL_Results_XML",
32            Self::Json => "http://www.w3.org/ns/formats/SPARQL_Results_JSON",
33            Self::Csv => "http://www.w3.org/ns/formats/SPARQL_Results_CSV",
34            Self::Tsv => "http://www.w3.org/ns/formats/SPARQL_Results_TSV",
35        }
36    }
37
38    /// The format [IANA media type](https://tools.ietf.org/html/rfc2046).
39    ///
40    /// ```
41    /// use sparesults::QueryResultsFormat;
42    ///
43    /// assert_eq!(
44    ///     QueryResultsFormat::Json.media_type(),
45    ///     "application/sparql-results+json"
46    /// )
47    /// ```
48    #[inline]
49    pub fn media_type(self) -> &'static str {
50        match self {
51            Self::Xml => "application/sparql-results+xml",
52            Self::Json => "application/sparql-results+json",
53            Self::Csv => "text/csv; charset=utf-8",
54            Self::Tsv => "text/tab-separated-values; charset=utf-8",
55        }
56    }
57
58    /// The format [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension.
59    ///
60    /// ```
61    /// use sparesults::QueryResultsFormat;
62    ///
63    /// assert_eq!(QueryResultsFormat::Json.file_extension(), "srj")
64    /// ```
65    #[inline]
66    pub fn file_extension(self) -> &'static str {
67        match self {
68            Self::Xml => "srx",
69            Self::Json => "srj",
70            Self::Csv => "csv",
71            Self::Tsv => "tsv",
72        }
73    }
74
75    /// The format name.
76    ///
77    /// ```
78    /// use sparesults::QueryResultsFormat;
79    ///
80    /// assert_eq!(QueryResultsFormat::Json.name(), "SPARQL Results in JSON")
81    /// ```
82    #[inline]
83    pub const fn name(self) -> &'static str {
84        match self {
85            Self::Xml => "SPARQL Results in XML",
86            Self::Json => "SPARQL Results in JSON",
87            Self::Csv => "SPARQL Results in CSV",
88            Self::Tsv => "SPARQL Results in TSV",
89        }
90    }
91
92    /// Looks for a known format from a media type.
93    ///
94    /// It supports some media type aliases.
95    /// For example, "application/xml" is going to return `Xml` even if it is not its canonical media type.
96    ///
97    /// Example:
98    /// ```
99    /// use sparesults::QueryResultsFormat;
100    ///
101    /// assert_eq!(
102    ///     QueryResultsFormat::from_media_type("application/sparql-results+json; charset=utf-8"),
103    ///     Some(QueryResultsFormat::Json)
104    /// )
105    /// ```
106    #[inline]
107    pub fn from_media_type(media_type: &str) -> Option<Self> {
108        const MEDIA_SUBTYPES: [(&str, QueryResultsFormat); 8] = [
109            ("csv", QueryResultsFormat::Csv),
110            ("json", QueryResultsFormat::Json),
111            ("plain", QueryResultsFormat::Csv),
112            ("sparql-results+json", QueryResultsFormat::Json),
113            ("sparql-results+xml", QueryResultsFormat::Xml),
114            ("tab-separated-values", QueryResultsFormat::Tsv),
115            ("tsv", QueryResultsFormat::Tsv),
116            ("xml", QueryResultsFormat::Xml),
117        ];
118
119        let (r#type, subtype) = media_type
120            .split_once(';')
121            .unwrap_or((media_type, ""))
122            .0
123            .trim()
124            .split_once('/')?;
125        let r#type = r#type.trim();
126        if !r#type.eq_ignore_ascii_case("application") && !r#type.eq_ignore_ascii_case("text") {
127            return None;
128        }
129        let subtype = subtype.trim();
130        let subtype = subtype.strip_prefix("x-").unwrap_or(subtype);
131        for (candidate_subtype, candidate_id) in MEDIA_SUBTYPES {
132            if candidate_subtype.eq_ignore_ascii_case(subtype) {
133                return Some(candidate_id);
134            }
135        }
136        None
137    }
138
139    /// Looks for a known format from an extension.
140    ///
141    /// It supports some aliases.
142    ///
143    /// Example:
144    /// ```
145    /// use sparesults::QueryResultsFormat;
146    ///
147    /// assert_eq!(
148    ///     QueryResultsFormat::from_extension("json"),
149    ///     Some(QueryResultsFormat::Json)
150    /// )
151    /// ```
152    #[inline]
153    pub fn from_extension(extension: &str) -> Option<Self> {
154        const EXTENSIONS: [(&str, QueryResultsFormat); 7] = [
155            ("csv", QueryResultsFormat::Csv),
156            ("json", QueryResultsFormat::Json),
157            ("srj", QueryResultsFormat::Json),
158            ("srx", QueryResultsFormat::Xml),
159            ("tsv", QueryResultsFormat::Tsv),
160            ("txt", QueryResultsFormat::Csv),
161            ("xml", QueryResultsFormat::Xml),
162        ];
163        for (candidate_extension, candidate_id) in EXTENSIONS {
164            if candidate_extension.eq_ignore_ascii_case(extension) {
165                return Some(candidate_id);
166            }
167        }
168        None
169    }
170}
171
172impl fmt::Display for QueryResultsFormat {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.write_str(self.name())
175    }
176}