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}