uqa_sql/catalog/
node_tree.rs1use std::fmt;
10
11use crate::SQLError;
12
13mod read;
14pub use read::parse;
15pub mod deparse;
16pub mod expressions;
17mod values;
18pub use values::constant_with_source as constant;
19pub(crate) use values::numeric::{
20 decode_with_control as decode_numeric_datum_with_control, encode as encode_numeric_datum,
21};
22pub(crate) use values::temporal::{
23 decode as decode_temporal_datum, encode as encode_temporal_datum,
24};
25
26#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
27#[serde(deny_unknown_fields)]
28pub struct Node {
29 pub kind: String,
30 pub fields: Vec<(String, Field)>,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
34#[serde(deny_unknown_fields)]
35pub enum Field {
36 Null,
37 Atom(String),
38 String(String),
39 StringValue(String),
41 Node(Node),
42 List(Vec<Field>),
43 Datum {
45 length: usize,
46 bytes: Vec<u8>,
47 },
48}
49
50impl Node {
51 pub fn new(
52 kind: impl Into<String>,
53 fields: impl IntoIterator<Item = (&'static str, Field)>,
54 ) -> Self {
55 Self {
56 kind: kind.into(),
57 fields: fields
58 .into_iter()
59 .map(|(name, value)| (name.into(), value))
60 .collect(),
61 }
62 }
63
64 pub fn field(&self, name: &str) -> Result<&Field, SQLError> {
65 self.fields
66 .iter()
67 .find_map(|(field, value)| (field == name).then_some(value))
68 .ok_or_else(|| invalid(format!("missing {name} in {} node", self.kind)))
69 }
70
71 pub fn integer(&self, name: &str) -> Result<i64, SQLError> {
72 match self.field(name)? {
73 Field::Atom(value) => value
74 .parse()
75 .map_err(|_| invalid(format!("invalid integer field {name}"))),
76 _ => Err(invalid(format!("invalid integer field {name}"))),
77 }
78 }
79
80 pub fn boolean(&self, name: &str) -> Result<bool, SQLError> {
81 match self.field(name)? {
82 Field::Atom(value) if value == "true" => Ok(true),
83 Field::Atom(value) if value == "false" => Ok(false),
84 _ => Err(invalid(format!("invalid boolean field {name}"))),
85 }
86 }
87}
88
89impl From<i64> for Field {
90 fn from(value: i64) -> Self {
91 Self::Atom(value.to_string())
92 }
93}
94
95impl From<bool> for Field {
96 fn from(value: bool) -> Self {
97 Self::Atom(value.to_string())
98 }
99}
100
101impl From<Node> for Field {
102 fn from(value: Node) -> Self {
103 Self::Node(value)
104 }
105}
106
107impl fmt::Display for Node {
108 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
109 write!(output, "{{{}", self.kind)?;
110 for (name, value) in &self.fields {
111 write!(output, " :{name} {value}")?;
112 }
113 output.write_str("}")
114 }
115}
116
117impl fmt::Display for Field {
118 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Self::Null => output.write_str("<>"),
121 Self::Atom(value) => output.write_str(value),
122 Self::String(value) => write_token(output, value),
123 Self::StringValue(value) => {
124 output.write_str("\"")?;
125 if !value.is_empty() {
126 write_token(output, value)?;
127 }
128 output.write_str("\"")
129 }
130 Self::Node(node) => node.fmt(output),
131 Self::List(values) => {
132 output.write_str("(")?;
133 for (index, value) in values.iter().enumerate() {
134 if index != 0 {
135 output.write_str(" ")?;
136 }
137 value.fmt(output)?;
138 }
139 output.write_str(")")
140 }
141 Self::Datum { length, bytes } => {
142 write!(output, "{length} [")?;
143 for byte in bytes {
144 write!(output, " {}", std::ffi::c_char::from_ne_bytes([*byte]))?;
146 }
147 output.write_str(" ]")
148 }
149 }
150 }
151}
152
153fn write_token(output: &mut fmt::Formatter<'_>, value: &str) -> fmt::Result {
154 if value.is_empty() {
155 return output.write_str("\"\"");
156 }
157 let mut bytes = value.bytes();
158 let first = bytes.next().expect("nonempty token");
159 if matches!(first, b'<' | b'"')
160 || first.is_ascii_digit()
161 || (matches!(first, b'+' | b'-')
162 && bytes
163 .next()
164 .is_some_and(|byte| byte.is_ascii_digit() || byte == b'.'))
165 {
166 output.write_str("\\")?;
167 }
168 for character in value.chars() {
169 if matches!(character, ' ' | '\n' | '\t' | '(' | ')' | '{' | '}' | '\\') {
170 output.write_str("\\")?;
171 }
172 write!(output, "{character}")?;
173 }
174 Ok(())
175}
176
177pub(super) fn invalid(message: impl Into<String>) -> SQLError {
178 SQLError::Routine {
179 sqlstate: "XX000".into(),
180 message: message.into(),
181 }
182}
183
184#[cfg(test)]
185mod tests;