1use crate::{Error, Stream};
5
6#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10pub struct IRI<'a>(pub &'a str);
11
12impl<'a> IRI<'a> {
13 #[allow(clippy::should_implement_trait)]
23 pub fn from_str(text: &'a str) -> Result<Self, Error> {
24 let mut s = Stream::from(text);
25 let link = s.parse_iri()?;
26 s.skip_spaces();
27 if !s.at_end() {
28 return Err(Error::UnexpectedData(s.calc_char_pos()));
29 }
30
31 Ok(Self(link))
32 }
33}
34
35#[derive(Clone, Copy, PartialEq, Eq, Debug)]
39pub struct FuncIRI<'a>(pub &'a str);
40
41impl<'a> FuncIRI<'a> {
42 #[allow(clippy::should_implement_trait)]
52 pub fn from_str(text: &'a str) -> Result<Self, Error> {
53 let mut s = Stream::from(text);
54 let link = s.parse_func_iri()?;
55 s.skip_spaces();
56 if !s.at_end() {
57 return Err(Error::UnexpectedData(s.calc_char_pos()));
58 }
59
60 Ok(Self(link))
61 }
62}
63
64impl<'a> Stream<'a> {
65 pub fn parse_iri(&mut self) -> Result<&'a str, Error> {
66 self.skip_spaces();
67 self.consume_byte(b'#')?;
68 let link = self.consume_bytes(|_, c| c != b' ');
69 if link.is_empty() {
70 return Err(Error::InvalidValue);
71 }
72 Ok(link)
73 }
74
75 pub fn parse_func_iri(&mut self) -> Result<&'a str, Error> {
76 self.skip_spaces();
77 self.consume_string(b"url(")?;
78 self.skip_spaces();
79
80 let quote = match self.curr_byte() {
81 Ok(b'\'') | Ok(b'"') => self.curr_byte().ok(),
82 _ => None,
83 };
84 if quote.is_some() {
85 self.advance(1);
86 self.skip_spaces();
87 }
88 self.consume_byte(b'#')?;
89 let link = if let Some(quote) = quote {
90 self.consume_bytes(|_, c| c != quote).trim_end()
91 } else {
92 self.consume_bytes(|_, c| c != b' ' && c != b')')
93 };
94 if link.is_empty() {
95 return Err(Error::InvalidValue);
96 }
97 if link.contains('\'') || link.contains('"') {
99 return Err(Error::InvalidValue);
100 }
101 self.skip_spaces();
102 if let Some(quote) = quote {
103 self.consume_byte(quote)?;
104 self.skip_spaces();
105 }
106 self.consume_byte(b')')?;
107 Ok(link)
108 }
109}
110
111#[rustfmt::skip]
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use alloc::string::ToString;
116
117 #[test]
118 fn parse_iri_1() {
119 assert_eq!(IRI::from_str("#id").unwrap(), IRI("id"));
120 }
121
122 #[test]
123 fn parse_iri_2() {
124 assert_eq!(IRI::from_str(" #id ").unwrap(), IRI("id"));
125 }
126
127 #[test]
128 fn parse_iri_3() {
129 assert_eq!(Stream::from(" #id text").parse_iri().unwrap(), "id");
131 assert_eq!(IRI::from_str(" #id text").unwrap_err().to_string(),
132 "unexpected data at position 10");
133 }
134
135 #[test]
136 fn parse_iri_4() {
137 assert_eq!(IRI::from_str("#1").unwrap(), IRI("1"));
138 }
139
140 #[test]
141 fn parse_err_iri_1() {
142 assert_eq!(IRI::from_str("# id").unwrap_err().to_string(), "invalid value");
143 }
144
145 #[test]
146 fn parse_func_iri_1() {
147 assert_eq!(FuncIRI::from_str("url(#id)").unwrap(), FuncIRI("id"));
148 }
149
150 #[test]
151 fn parse_func_iri_2() {
152 assert_eq!(FuncIRI::from_str("url(#1)").unwrap(), FuncIRI("1"));
153 }
154
155 #[test]
156 fn parse_func_iri_3() {
157 assert_eq!(FuncIRI::from_str(" url( #id ) ").unwrap(), FuncIRI("id"));
158 }
159
160 #[test]
161 fn parse_func_iri_4() {
162 assert_eq!(Stream::from("url(#id) qwe").parse_func_iri().unwrap(), "id");
164 assert_eq!(FuncIRI::from_str("url(#id) qwe").unwrap_err().to_string(),
165 "unexpected data at position 10");
166 }
167
168 #[test]
169 fn parse_func_iri_5() {
170 assert_eq!(FuncIRI::from_str("url('#id')").unwrap(), FuncIRI("id"));
171 assert_eq!(FuncIRI::from_str("url(' #id ')").unwrap(), FuncIRI("id"));
172 }
173
174 #[test]
175 fn parse_func_iri_6() {
176 assert_eq!(FuncIRI::from_str("url(\"#id\")").unwrap(), FuncIRI("id"));
177 assert_eq!(FuncIRI::from_str("url(\" #id \")").unwrap(), FuncIRI("id"));
178 }
179
180 #[test]
181 fn parse_err_func_iri_1() {
182 assert_eq!(FuncIRI::from_str("url ( #1 )").unwrap_err().to_string(),
183 "expected 'url(' not 'url ' at position 1");
184 }
185
186 #[test]
187 fn parse_err_func_iri_2() {
188 assert_eq!(FuncIRI::from_str("url(#)").unwrap_err().to_string(), "invalid value");
189 }
190
191 #[test]
192 fn parse_err_func_iri_3() {
193 assert_eq!(FuncIRI::from_str("url(# id)").unwrap_err().to_string(),
194 "invalid value");
195 }
196
197 #[test]
198 fn parse_err_func_iri_4() {
199 assert_eq!(FuncIRI::from_str("url('#id)").unwrap_err().to_string(),
201 "unexpected end of stream");
202 assert_eq!(FuncIRI::from_str("url(#id')").unwrap_err().to_string(),
203 "invalid value");
204 }
205}