uqa_sql/catalog/node_tree/
read.rs1use super::{invalid, Field, Node};
10use crate::SQLError;
11
12pub fn parse(text: &str) -> Result<Field, SQLError> {
13 let mut input = Reader {
14 bytes: text.as_bytes(),
15 offset: 0,
16 };
17 let value = input.value(0)?;
18 if input.peek().is_some() {
19 return Err(invalid("trailing data in node tree"));
20 }
21 Ok(value)
22}
23
24struct Reader<'a> {
25 bytes: &'a [u8],
26 offset: usize,
27}
28
29impl Reader<'_> {
30 fn peek(&mut self) -> Option<u8> {
31 while self
32 .bytes
33 .get(self.offset)
34 .is_some_and(u8::is_ascii_whitespace)
35 {
36 self.offset += 1;
37 }
38 self.bytes.get(self.offset).copied()
39 }
40
41 fn value(&mut self, depth: usize) -> Result<Field, SQLError> {
42 if depth > 256 {
43 return Err(SQLError::Routine {
44 sqlstate: "54001".into(),
45 message: "node tree nesting is too deep".into(),
46 });
47 }
48 match self.peek() {
49 Some(b'{') => self.node(depth + 1).map(Field::Node),
50 Some(b'(') => self.list(depth + 1).map(Field::List),
51 Some(b')' | b'}' | b'[' | b']') | None => Err(invalid("unexpected end of node field")),
52 _ => {
53 let (token, escaped) = self.token()?;
54 if escaped {
55 return Ok(Field::String(token));
56 }
57 match token.as_str() {
58 "<>" => Ok(Field::Null),
59 "\"\"" => Ok(Field::String(String::new())),
60 _ if self.peek() == Some(b'[') => self.datum(&token),
61 _ => Ok(Field::Atom(token)),
62 }
63 }
64 }
65 }
66
67 fn node(&mut self, depth: usize) -> Result<Node, SQLError> {
68 self.offset += 1;
69 let (kind, escaped) = self.token()?;
70 if escaped
71 || kind.is_empty()
72 || !kind
73 .bytes()
74 .all(|byte| byte.is_ascii_uppercase() || byte.is_ascii_digit() || byte == b'_')
75 {
76 return Err(invalid("invalid node tag"));
77 }
78 let mut fields = Vec::new();
79 loop {
80 if self.peek() == Some(b'}') {
81 self.offset += 1;
82 break;
83 }
84 let (field, escaped) = self.token()?;
85 let name = field
86 .strip_prefix(':')
87 .filter(|name| !name.is_empty())
88 .ok_or_else(|| invalid("expected a named node field"))?;
89 if escaped || fields.iter().any(|(field, _)| field == name) {
90 return Err(invalid("invalid or duplicate node field"));
91 }
92 fields.push((name.to_owned(), self.value(depth)?));
93 }
94 Ok(Node { kind, fields })
95 }
96
97 fn list(&mut self, depth: usize) -> Result<Vec<Field>, SQLError> {
98 self.offset += 1;
99 let mut values = Vec::new();
100 loop {
101 if self.peek() == Some(b')') {
102 self.offset += 1;
103 return Ok(values);
104 }
105 values.push(self.value(depth)?);
106 }
107 }
108
109 fn datum(&mut self, length: &str) -> Result<Field, SQLError> {
110 let length = length
111 .parse()
112 .map_err(|_| invalid("invalid Datum length"))?;
113 self.offset += 1;
114 let mut bytes = Vec::new();
115 loop {
116 if self.peek() == Some(b']') {
117 self.offset += 1;
118 return Ok(Field::Datum { length, bytes });
119 }
120 let (token, escaped) = self.token()?;
121 let byte = token
122 .parse::<i16>()
123 .ok()
124 .filter(|byte| (-128..=255).contains(byte))
125 .ok_or_else(|| invalid("invalid Datum byte"))?;
126 if escaped {
127 return Err(invalid("escaped Datum byte"));
128 }
129 bytes.push(byte.to_le_bytes()[0]);
130 }
131 }
132
133 fn token(&mut self) -> Result<(String, bool), SQLError> {
134 self.peek();
135 let mut bytes = Vec::new();
136 let mut escaped = false;
137 while let Some(&byte) = self.bytes.get(self.offset) {
138 if byte.is_ascii_whitespace() || matches!(byte, b'{' | b'}' | b'(' | b')' | b'[' | b']')
139 {
140 break;
141 }
142 self.offset += 1;
143 if byte == b'\\' {
144 escaped = true;
145 let &next = self
146 .bytes
147 .get(self.offset)
148 .ok_or_else(|| invalid("unfinished node token escape"))?;
149 bytes.push(next);
150 self.offset += 1;
151 } else {
152 bytes.push(byte);
153 }
154 }
155 if bytes.is_empty() {
156 return Err(invalid("missing node token"));
157 }
158 String::from_utf8(bytes)
159 .map(|token| (token, escaped))
160 .map_err(|_| invalid("invalid UTF-8 node token"))
161 }
162}