1use crate::LLSDValue;
25use anyhow::{anyhow, Error};
26use std::collections::HashMap;
27use core::iter::{Peekable};
28use core::str::{Chars, Bytes};
29use uuid::{Uuid};
30use chrono::DateTime;
31use base64::Engine;
32
33pub const LLSDNOTATIONPREFIX: &str = "<? llsd/notation ?>\n";
38pub const LLSDNOTATIONSENTINEL: &str = LLSDNOTATIONPREFIX;
40
41pub fn from_bytes(b: &[u8]) -> Result<LLSDValue, Error> {
43 LLSDStreamBytes::parse(b)
44}
45
46pub fn from_str(s: &str) -> Result<LLSDValue, Error> {
48 LLSDStreamChars::parse(s)
49}
50
51trait LLSDStream<C, S> {
54 fn next(&mut self) -> Option<C>;
56
57 fn next_ok(&mut self) -> Result<C, Error> {
59 if let Some(ch) = self.next() {
60 Ok(ch)
61 } else {
62 Err(anyhow!("Unexpected end of input parsing Notation"))
63 }
64 }
65
66 fn peek(&mut self) -> Option<&C>;
68
69 fn peek_ok(&mut self) -> Result<&C, Error> {
71 if let Some(ch) = self.peek() {
72 Ok(ch)
73 } else {
74 Err(anyhow!("Unexpected end of input parsing Notation"))
75 }
76 }
77
78 fn into_char(ch: &C) -> char;
80
81 fn consume_whitespace(&mut self) -> Result<(), Error> {
84 while let Some(ch) = self.peek() {
85 match Self::into_char(ch) {
86 ' ' | '\n' => { let _ = self.next(); }, '\\' => {
88 let _ = self.next(); let ch = Self::into_char(&self.next_ok()?); if ch != 'n' { return Err(anyhow!("Unexpected escape sequence \"\\{}\" where white space expected.", ch));
92 }
93 }
94 _ => break
95 }
96 }
97 Ok(())
98 }
99
100 fn consume_char(&mut self, expected_ch: char) -> Result<(), Error> {
102 self.consume_whitespace()?;
103 let ch = Self::into_char(&self.next_ok()?);
104 if ch == expected_ch {
105 Ok(())
106 } else {
107 Err(anyhow!("Expected '{}', found '{}'.", expected_ch, ch))
108 }
109 }
110
111 fn parse_integer(&mut self) -> Result<LLSDValue, Error> {
113 let mut s = String::with_capacity(20); while let Some(ch) = self.peek() {
116 match Self::into_char(ch) {
117 '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'+'|'-' => s.push(Self::into_char(&self.next().unwrap())),
118 _ => break
119 }
120 }
121 Ok(LLSDValue::Integer(s.parse::<i32>()?))
123 }
124 fn parse_real(&mut self) -> Result<LLSDValue, Error> {
127 let mut s = String::with_capacity(20); while let Some(ch) = self.peek() {
131 match Self::into_char(ch) {
132 '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'+'|'-'|'.' => s.push(Self::into_char(&self.next().unwrap())),
133 _ => break
134 }
135 }
136 Ok(LLSDValue::Real(s.parse::<f64>()?))
138 }
139
140 fn parse_boolean(&mut self, first_char: char) -> Result<LLSDValue, Error> {
142 let mut s = String::with_capacity(4);
144 s.push(first_char); loop {
146 if let Some(ch) = self.peek() {
147 if Self::into_char(ch).is_alphabetic() {
148 s.push(Self::into_char(&self.next().unwrap()));
149 continue
150 }
151 }
152 break;
153 }
154 match s.as_str() {
156 "f" | "F" | "false" | "FALSE" => Ok(LLSDValue::Boolean(false)),
157 "t" | "T" | "true" | "TRUE" => Ok(LLSDValue::Boolean(true)),
158 _ => Err(anyhow!("Parsing Boolean, got {}", s))
159 }
160 }
161 fn parse_quoted_string(&mut self, delim: char) -> Result<String, Error> {
165 self.consume_whitespace()?;
166 let mut s = String::with_capacity(128); loop {
168 let ch = Self::into_char(&self.next_ok()?); if ch == delim { break } if ch == '\\' { let ch = Self::into_char(&self.next_ok()?); match ch {
173 '\\' | '\'' | '\"' => s.push(ch), 'n' => s.push('\n'), _ => { return Err(anyhow!("Unexpected escape sequence \"\\{}\" within quoted string.", ch)); }
176 }
177 } else {
178 s.push(ch)
179 }
180 }
181 String::shrink_to_fit(&mut s); Ok(s)
183 }
184 fn parse_date(&mut self) -> Result<LLSDValue, Error> {
186 if let Some(delim) = self.next() {
187 if Self::into_char(&delim) == '"' || Self::into_char(&delim) == '\'' {
188 let s = self.parse_quoted_string(Self::into_char(&delim))?;
189 let naive_date = DateTime::parse_from_rfc3339(&s)?; Ok(LLSDValue::Date(naive_date.timestamp())) } else {
192 Err(anyhow!("URI did not begin with '\"'"))
193 }
194 } else {
195 Err(anyhow!("URI at end of file."))
196 }
197 }
198
199 fn parse_uri(&mut self) -> Result<LLSDValue, Error> {
201 if let Some(delim) = self.next() {
202 if Self::into_char(&delim) == '"' || Self::into_char(&delim) == '\'' {
203 let s = self.parse_quoted_string(Self::into_char(&delim))?;
204 Ok(LLSDValue::URI(urlencoding::decode(&s)?.to_string()))
205 } else {
206 Err(anyhow!("URI did not begin with '\"'"))
207 }
208 } else {
209 Err(anyhow!("URI at end of file."))
210 }
211 }
212 fn parse_uuid(&mut self) -> Result<LLSDValue, Error> {
214 const UUID_LEN: usize = "c69b29b1-8944-58ae-a7c5-2ca7b23e22fb".len(); let mut s = String::with_capacity(UUID_LEN);
216 for _ in 0..UUID_LEN {
217 s.push(Self::into_char(&(self.next().ok_or(anyhow!("EOF parsing UUID"))?)));
218 }
219 Ok(LLSDValue::UUID(Uuid::parse_str(&s)?))
220 }
221
222 fn parse_map(&mut self) -> Result<LLSDValue, Error> {
224 let mut kvmap = HashMap::new(); loop {
226 self.consume_whitespace()?;
227 let key = {
228 let ch = Self::into_char(&self.next_ok()?);
229 match ch {
230 '}' => { break } '\'' | '"' => self.parse_quoted_string(ch)?,
232 _ => { return Err(anyhow!("Map key began with {} instead of quote.", ch)); }
233 }
234 };
235 self.consume_char(':')?;
236 let value = self.parse_value()?; kvmap.insert(key, value);
238 self.consume_whitespace()?;
240 if Self::into_char(self.peek_ok()?) == ',' {
241 let _ = self.next(); }
243 }
244 Ok(LLSDValue::Map(kvmap))
245 }
246
247 fn parse_array(&mut self) -> Result<LLSDValue, Error> {
251 let mut array_items = Vec::new();
252 loop {
254 self.consume_whitespace()?;
256 let ch = Self::into_char(self.peek_ok()?);
257 if ch == ']' {
258 let _ = self.next(); break; }
260 array_items.push(self.parse_value()?); self.consume_whitespace()?;
263 if Self::into_char(self.peek_ok()?) == ',' {
264 let _ = self.next(); }
266 }
267 Ok(LLSDValue::Array(array_items)) }
269
270 fn parse_binary(&mut self) -> Result<LLSDValue, Error>; fn parse_sized_string(&mut self) -> Result<LLSDValue, Error>; fn parse_value(&mut self) -> Result<LLSDValue, Error> {
278 self.consume_whitespace()?; let ch = Self::into_char(&self.next_ok()?);
280 match ch {
281 '!' => { Ok(LLSDValue::Undefined) } '0' => { Ok(LLSDValue::Boolean(false)) } '1' => { Ok(LLSDValue::Boolean(true)) } 'f' | 'F' => { self.parse_boolean(ch) } 't' | 'T' => { self.parse_boolean(ch) } '{' => { self.parse_map() } '[' => { self.parse_array() } 'i' => { self.parse_integer() } 'r' => { self.parse_real() } 'd' => { self.parse_date() } 'u' => { self.parse_uuid() } 'l' => { self.parse_uri() } 'b' => { self.parse_binary() } 's' => { self.parse_sized_string() } '"' => { Ok(LLSDValue::String(self.parse_quoted_string(ch)?)) } '\'' => { Ok(LLSDValue::String(self.parse_quoted_string(ch)?)) } _ => { Err(anyhow!("Unexpected character: {:?}", ch)) } }
300 }
301}
302
303struct LLSDStreamChars<'a> {
305 cursor: Peekable<Chars<'a>>,
307}
308
309impl LLSDStream<char, Peekable<Chars<'_>>> for LLSDStreamChars<'_> {
310 fn next(&mut self) -> Option<char> {
312 self.cursor.next()
313 }
314 fn peek(&mut self) -> Option<&char> {
316 self.cursor.peek()
317 }
318 fn into_char(ch: &char) -> char {
320 *ch
321 }
322
323 fn parse_binary(&mut self) -> Result<LLSDValue, Error> {
325 Err(anyhow!("Byte-counted binary data inside UTF-8 won't work."))
326 }
327
328 fn parse_sized_string(&mut self) -> Result<LLSDValue, Error> {
330 Err(anyhow!("Byte-counted string data inside UTF-8 won't work."))
331 }
332}
333
334impl LLSDStreamChars<'_> {
335 pub fn parse(notation_str: &str) -> Result<LLSDValue, Error> {
338 let mut stream = LLSDStreamChars { cursor: notation_str.chars().peekable() };
339 match stream.parse_value() {
340 Ok(v) => Ok(v),
341 Err(e) => {
342 let s = beginning_to_iterator(notation_str, &stream.cursor);
344 Err(anyhow!("LLSD notation string parse error: {:?}. Parse got this far: {}", e, s))
345 }
346 }
347 }
348}
349
350struct LLSDStreamBytes<'a> {
352 cursor: Peekable<std::slice::Iter<'a, u8>>,
354}
355
356impl LLSDStream<u8, Peekable<Bytes<'_>>> for LLSDStreamBytes<'_> {
357 fn next(&mut self) -> Option<u8> {
359 self.cursor.next().copied()
360 }
361 fn peek(&mut self) -> Option<&u8> {
363 self.cursor.peek().copied()
364 }
365 fn into_char(ch: &u8) -> char {
367 (*ch).into()
368 }
369
370 fn parse_binary(&mut self) -> Result<LLSDValue, Error> {
380 if let Some(ch) = self.peek() {
381 match Self::into_char(ch) {
382 '(' => {
383 let cnt = self.parse_number_in_parentheses()?;
384 self.consume_char('"')?;
385 let s = self.next_chunk(cnt)?;
386 self.consume_char('"')?; Ok(LLSDValue::Binary(s)) }
389 '1' => {
390 self.consume_char('1')?;
391 self.consume_char('6')?; self.consume_char('"')?; let mut s = self.parse_quoted_string('"')?;
394 s.retain(|c| !c.is_whitespace());
395 Ok(LLSDValue::Binary(hex::decode(s)?))
396 }
397 '6' => {
398 self.consume_char('6')?;
399 self.consume_char('4')?;
400 self.consume_char('"')?; let mut s = self.parse_quoted_string('"')?;
402 s.retain(|c| !c.is_whitespace());
403 println!("Base 64 decode input: \"{}\"", s); let bytes = base64::engine::general_purpose::STANDARD.decode(s)?;
405 Ok(LLSDValue::Binary(bytes))
406 }
407 _ => Err(anyhow!("Binary value started with {} instead of (, 1, or 6", ch))
408 }
409 } else {
410 Err(anyhow!("Binary value started with EOF"))
411 }
412 }
413
414 fn parse_sized_string(&mut self) -> Result<LLSDValue, Error> {
417 let cnt = self.parse_number_in_parentheses()?;
418 self.consume_char('"')?;
421 let s = self.next_chunk(cnt)?;
422 self.consume_char('"')?;
423 Ok(LLSDValue::String(String::from_utf8(s)?))
424 }
425}
426
427impl LLSDStreamBytes<'_> {
428 pub fn parse(notation_bytes: &[u8]) -> Result<LLSDValue, Error> {
431 let mut stream = LLSDStreamBytes { cursor: notation_bytes.iter().peekable() };
432 stream.parse_value()
433 }
434
435 fn parse_number_in_parentheses(&mut self) -> Result<usize, Error> {
437 self.consume_char('(')?;
438 let val = self.parse_integer()?;
439 self.consume_char(')')?;
440 if let LLSDValue::Integer(v) = val {
441 Ok(v as usize)
442 } else {
443 panic!("Integer parse did not return an integer.");
444 }
445 }
446
447 fn next_chunk(&mut self, cnt: usize) -> Result<Vec<u8>, Error> {
449 let mut s = Vec::with_capacity(cnt);
450 for _ in 0..cnt {
452 s.push(self.next_ok()?);
453 }
454 Ok(s)
455 }
456
457}
458
459fn beginning_to_iterator<'a>(orig: &'a str, pos: &Peekable<Chars>) -> &'a str {
462 let suffix: String = pos.clone().collect();
463 println!("Suffix: {}", suffix);
464 if let Some(s) = orig.strip_suffix(&suffix) {
465 s
466 } else {
467 orig
468 }
469}
470
471#[test]
472fn notationparse1() {
474 let s1 = "\"ABC☺DEF\"".to_string(); let mut stream1 = LLSDStreamChars { cursor: s1.chars().peekable() };
476 stream1.consume_char('"').unwrap(); let v1 = stream1.parse_quoted_string('"').unwrap();
478 assert_eq!(v1, "ABC☺DEF");
479}
480
481#[test]
482fn notationparse2() {
483 const TESTNOTATION2: &str = r#"
485[
486 {'destination':l"http://secondlife.com"},
487 {'version':i1},
488 {
489 'agent_id':u3c115e51-04f4-523c-9fa6-98aff1034730,
490 'session_id':u2c585cec-038c-40b0-b42e-a25ebab4d132,
491 'circuit_code':i1075,
492 'first_name':'Phoenix',
493 'last_name':'Linden',
494 'position':[r70.9247,r254.378,r38.7304],
495 'look_at':[r-0.043753,r-0.999042,r0],
496 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003],
497 'attachment_data':
498 [
499 {
500 'attachment_point':i2,
501 'item_id':ud6852c11-a74e-309a-0462-50533f1ef9b3,
502 'asset_id':uc69b29b1-8944-58ae-a7c5-2ca7b23e22fb
503 },
504 {
505 'attachment_point':i10,
506 'item_id':uff852c22-a74e-309a-0462-50533f1ef900,
507 'asset_id':u5868dd20-c25a-47bd-8b4c-dedc99ef9479
508 }
509 ]
510 }
511]
512"#;
513 let parsed_s = LLSDStreamChars::parse(TESTNOTATION2);
514 println!("Parse of string form {}: \n{:#?}", TESTNOTATION2, parsed_s);
515 let parsed_b = LLSDStreamBytes::parse(TESTNOTATION2.as_bytes());
516 println!("Parse of byte form: {:#?}", parsed_b);
517 assert_eq!(parsed_s.unwrap(), parsed_b.unwrap());
518}
519
520#[test]
521fn notationparse3() {
522 const TESTNOTATION3: &str = r#"
524[
525 {
526 'creation-date':d"2007-03-15T18:30:18Z",
527 'creator-id':u3c115e51-04f4-523c-9fa6-98aff1034730
528 },
529 s(10)"0123456789",
530 "Where's the beef?",
531 'Over here.',
532 b(158)"default
533{
534 state_entry()
535 {
536 llSay(0, "Hello, Avatar!");
537 }
538
539 touch_start(integer total_number)
540 {
541 llSay(0, "Touched.");
542 }
543}",
544 b64"AABAAAAAAAAAAAIAAAA//wAAP/8AAADgAAAA5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
545AABkAAAAZAAAAAAAAAAAAAAAZAAAAAAAAAABAAAAAAAAAAAAAAAAAAAABQAAAAEAAAAQAAAAAAAA
546AAUAAAAFAAAAABAAAAAAAAAAPgAAAAQAAAAFAGNbXgAAAABgSGVsbG8sIEF2YXRhciEAZgAAAABc
547XgAAAAhwEQjRABeVAAAABQBjW14AAAAAYFRvdWNoZWQuAGYAAAAAXF4AAAAIcBEI0QAXAZUAAEAA
548AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
549]
550"#;
551 let parsed_b = from_bytes(TESTNOTATION3.as_bytes()).unwrap();
552 println!("Parse of byte form: {:#?}", parsed_b);
553 let parsed_b = from_str(TESTNOTATION3);
554 assert!(parsed_b.is_err()); println!("Parse of string form: {:#?}", parsed_b);
556}
557
558#[test]
559fn notationparse4() {
560 const TESTNOTATION4: &str = r#"
562 {'gltf_json':['{\"asset\":{\"version\":\"2.0\"},\"images\":[{\"uri\":\"5748decc-f629-461c-9a36-a35a221fe21f\"},
563 {\"uri\":\"5748decc-f629-461c-9a36-a35a221fe21f\"}],\"materials\":[{\"occlusionTexture\":{\"index\":1},\"pbrMetallicRoughness\":{\"metallicRoughnessTexture\":{\"index\":0},\"roughnessFactor\":0.20000000298023224}}],\"textures\":[{\"source\":0},
564 {\"source\":1}]}\\n'],'local_id':i8893800,'object_id':u6ac43d70-80eb-e526-ec91-110b4116293e,'region_handle_x':i342016,'region_handle_y':i343552,'sides':[i0]}"
565"#;
566 let parsed_b = from_bytes(TESTNOTATION4.as_bytes());
567 println!("Parse of byte form: {:#?}", parsed_b);
568 let local_id = *parsed_b.unwrap().as_map().unwrap().get("local_id").unwrap().as_integer().unwrap();
569 assert_eq!(local_id, 8893800); let parsed_b = from_str(TESTNOTATION4);
571 println!("Parse of str form: {:#?}", parsed_b);
572 let local_id = *parsed_b.unwrap().as_map().unwrap().get("local_id").unwrap().as_integer().unwrap();
573 assert_eq!(local_id, 8893800); }
575
576#[test]
577fn notationparse5() {
578 const TESTNOTATION5: &str = r#"
580 {'gltf_json':['{\"asset\":{\"version\":\"2.0\"},\"im\ages\":[{\"uri\":\"5748decc-f629-461c-9a36-a35a221fe21f\"},
581 {\"uri\":\"5748decc-f629-461c-9a36-a35a221fe21f\"}],\"materials\":[{\"occlusionTexture\":{\"index\":1},\"pbrMetallicRoughness\":{\"metallicRoughnessTexture\":{\"index\":0},\"roughnessFactor\":0.20000000298023224}}],\"textures\":[{\"source\":0},
582 {\"source\":1}]}\\n'],'local_id':i8893800,'object_id':u6ac43d70-80eb-e526-ec91-110b4116293e,'region_handle_x':i342016,'region_handle_y':i343552,'sides':[i0]}"
583"#;
584 println!("Test notationparse5");
585 let parsed_b = from_str(TESTNOTATION5);
586 println!("Parse of byte form: {:#?}", parsed_b);
587 assert!(parsed_b.is_err());
588}