svgtypes/
funciri.rs
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
116 #[test]
117 fn parse_iri_1() {
118 assert_eq!(IRI::from_str("#id").unwrap(), IRI("id"));
119 }
120
121 #[test]
122 fn parse_iri_2() {
123 assert_eq!(IRI::from_str(" #id ").unwrap(), IRI("id"));
124 }
125
126 #[test]
127 fn parse_iri_3() {
128 assert_eq!(Stream::from(" #id text").parse_iri().unwrap(), "id");
130 assert_eq!(IRI::from_str(" #id text").unwrap_err().to_string(),
131 "unexpected data at position 10");
132 }
133
134 #[test]
135 fn parse_iri_4() {
136 assert_eq!(IRI::from_str("#1").unwrap(), IRI("1"));
137 }
138
139 #[test]
140 fn parse_err_iri_1() {
141 assert_eq!(IRI::from_str("# id").unwrap_err().to_string(), "invalid value");
142 }
143
144 #[test]
145 fn parse_func_iri_1() {
146 assert_eq!(FuncIRI::from_str("url(#id)").unwrap(), FuncIRI("id"));
147 }
148
149 #[test]
150 fn parse_func_iri_2() {
151 assert_eq!(FuncIRI::from_str("url(#1)").unwrap(), FuncIRI("1"));
152 }
153
154 #[test]
155 fn parse_func_iri_3() {
156 assert_eq!(FuncIRI::from_str(" url( #id ) ").unwrap(), FuncIRI("id"));
157 }
158
159 #[test]
160 fn parse_func_iri_4() {
161 assert_eq!(Stream::from("url(#id) qwe").parse_func_iri().unwrap(), "id");
163 assert_eq!(FuncIRI::from_str("url(#id) qwe").unwrap_err().to_string(),
164 "unexpected data at position 10");
165 }
166
167 #[test]
168 fn parse_func_iri_5() {
169 assert_eq!(FuncIRI::from_str("url('#id')").unwrap(), FuncIRI("id"));
170 assert_eq!(FuncIRI::from_str("url(' #id ')").unwrap(), FuncIRI("id"));
171 }
172
173 #[test]
174 fn parse_func_iri_6() {
175 assert_eq!(FuncIRI::from_str("url(\"#id\")").unwrap(), FuncIRI("id"));
176 assert_eq!(FuncIRI::from_str("url(\" #id \")").unwrap(), FuncIRI("id"));
177 }
178
179 #[test]
180 fn parse_err_func_iri_1() {
181 assert_eq!(FuncIRI::from_str("url ( #1 )").unwrap_err().to_string(),
182 "expected 'url(' not 'url ' at position 1");
183 }
184
185 #[test]
186 fn parse_err_func_iri_2() {
187 assert_eq!(FuncIRI::from_str("url(#)").unwrap_err().to_string(), "invalid value");
188 }
189
190 #[test]
191 fn parse_err_func_iri_3() {
192 assert_eq!(FuncIRI::from_str("url(# id)").unwrap_err().to_string(),
193 "invalid value");
194 }
195
196 #[test]
197 fn parse_err_func_iri_4() {
198 assert_eq!(FuncIRI::from_str("url('#id)").unwrap_err().to_string(),
200 "unexpected end of stream");
201 assert_eq!(FuncIRI::from_str("url(#id')").unwrap_err().to_string(),
202 "invalid value");
203 }
204}