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 if self.peek() == Some(b'"') {
106 let (token, _) = self.token()?;
107 let value = token
108 .strip_prefix('"')
109 .and_then(|token| token.strip_suffix('"'))
110 .ok_or_else(|| invalid("unfinished String value in node list"))?;
111 values.push(Field::StringValue(value.into()));
112 } else {
113 values.push(self.value(depth)?);
114 }
115 }
116 }
117
118 fn datum(&mut self, length: &str) -> Result<Field, SQLError> {
119 let length = length
120 .parse()
121 .map_err(|_| invalid("invalid Datum length"))?;
122 self.offset += 1;
123 let mut bytes = Vec::new();
124 loop {
125 if self.peek() == Some(b']') {
126 self.offset += 1;
127 return Ok(Field::Datum { length, bytes });
128 }
129 let (token, escaped) = self.token()?;
130 let byte = token
131 .parse::<i16>()
132 .ok()
133 .filter(|byte| (-128..=255).contains(byte))
134 .ok_or_else(|| invalid("invalid Datum byte"))?;
135 if escaped {
136 return Err(invalid("escaped Datum byte"));
137 }
138 bytes.push(byte.to_le_bytes()[0]);
139 }
140 }
141
142 fn token(&mut self) -> Result<(String, bool), SQLError> {
143 self.peek();
144 let mut bytes = Vec::new();
145 let mut escaped = false;
146 while let Some(&byte) = self.bytes.get(self.offset) {
147 if byte.is_ascii_whitespace() || matches!(byte, b'{' | b'}' | b'(' | b')' | b'[' | b']')
148 {
149 break;
150 }
151 self.offset += 1;
152 if byte == b'\\' {
153 escaped = true;
154 let &next = self
155 .bytes
156 .get(self.offset)
157 .ok_or_else(|| invalid("unfinished node token escape"))?;
158 bytes.push(next);
159 self.offset += 1;
160 } else {
161 bytes.push(byte);
162 }
163 }
164 if bytes.is_empty() {
165 return Err(invalid("missing node token"));
166 }
167 String::from_utf8(bytes)
168 .map(|token| (token, escaped))
169 .map_err(|_| invalid("invalid UTF-8 node token"))
170 }
171}