prqlc_parser/parser/pr/
ident.rs1use std::fmt::Write;
2
3use schemars::JsonSchema;
4use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
5
6#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, JsonSchema)]
9pub struct Ident {
10 pub path: Vec<String>,
11 pub name: String,
12}
13
14impl Ident {
15 pub fn from_name<S: ToString>(name: S) -> Self {
16 Ident {
17 path: Vec::new(),
18 name: name.to_string(),
19 }
20 }
21
22 pub fn from_path<S: ToString>(mut path: Vec<S>) -> Self {
26 let name = path.pop().unwrap().to_string();
27 Ident {
28 path: path.into_iter().map(|x| x.to_string()).collect(),
29 name,
30 }
31 }
32
33 pub fn len(&self) -> usize {
34 self.path.len() + 1
35 }
36
37 pub fn is_empty(&self) -> bool {
38 false
39 }
40
41 pub fn pop(self) -> Option<Self> {
44 let mut path = self.path;
45 path.pop().map(|name| Ident { path, name })
46 }
47
48 pub fn pop_front(mut self) -> (String, Option<Ident>) {
49 if self.path.is_empty() {
50 (self.name, None)
51 } else {
52 let first = self.path.remove(0);
53 (first, Some(self))
54 }
55 }
56
57 pub fn prepend(self, mut parts: Vec<String>) -> Ident {
58 parts.extend(self);
59 Ident::from_path(parts)
60 }
61
62 pub fn push(&mut self, name: String) {
63 self.path.push(std::mem::take(&mut self.name));
64 self.name = name;
65 }
66
67 pub fn with_name<S: ToString>(mut self, name: S) -> Self {
68 self.name = name.to_string();
69 self
70 }
71
72 pub fn iter(&self) -> impl Iterator<Item = &String> {
73 self.path.iter().chain(std::iter::once(&self.name))
74 }
75
76 pub fn starts_with(&self, prefix: &Ident) -> bool {
77 if prefix.len() > self.len() {
78 return false;
79 }
80 prefix
81 .iter()
82 .zip(self.iter())
83 .all(|(prefix_component, self_component)| prefix_component == self_component)
84 }
85
86 pub fn starts_with_path<S: AsRef<str>>(&self, prefix: &[S]) -> bool {
87 if prefix.len() > self.len() {
88 return false;
89 }
90 prefix
91 .iter()
92 .zip(self.iter())
93 .all(|(prefix_component, self_component)| prefix_component.as_ref() == self_component)
94 }
95
96 pub fn starts_with_part(&self, prefix: &str) -> bool {
97 self.starts_with_path(&[prefix])
98 }
99}
100
101impl std::fmt::Debug for Ident {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_list()
104 .entries(&self.path)
105 .entry(&self.name)
106 .finish()
107 }
108}
109
110impl std::fmt::Display for Ident {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 display_ident(f, self)
113 }
114}
115
116impl IntoIterator for Ident {
117 type Item = String;
118 type IntoIter = std::iter::Chain<
119 std::vec::IntoIter<std::string::String>,
120 std::option::IntoIter<std::string::String>,
121 >;
122
123 fn into_iter(self) -> Self::IntoIter {
124 self.path.into_iter().chain(Some(self.name))
125 }
126}
127
128impl std::ops::Add<Ident> for Ident {
129 type Output = Ident;
130
131 fn add(self, rhs: Ident) -> Self::Output {
132 Ident {
133 path: self.into_iter().chain(rhs.path).collect(),
134 name: rhs.name,
135 }
136 }
137}
138
139impl Serialize for Ident {
140 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141 where
142 S: Serializer,
143 {
144 let mut seq = serializer.serialize_seq(Some(self.len()))?;
145 for part in &self.path {
146 seq.serialize_element(part)?;
147 }
148 seq.serialize_element(&self.name)?;
149 seq.end()
150 }
151}
152
153impl<'de> Deserialize<'de> for Ident {
154 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
155 where
156 D: Deserializer<'de>,
157 {
158 <Vec<String> as Deserialize>::deserialize(deserializer).map(Ident::from_path)
159 }
160}
161
162pub fn display_ident(f: &mut std::fmt::Formatter, ident: &Ident) -> Result<(), std::fmt::Error> {
163 let path = &ident.path[..];
164
165 for part in path {
166 display_ident_part(f, part)?;
167 f.write_char('.')?;
168 }
169 display_ident_part(f, &ident.name)?;
170 Ok(())
171}
172
173pub fn display_ident_part(f: &mut std::fmt::Formatter, s: &str) -> Result<(), std::fmt::Error> {
174 fn forbidden_start(c: char) -> bool {
175 !(c.is_ascii_alphabetic() || matches!(c, '_' | '$'))
176 }
177 fn forbidden_subsequent(c: char) -> bool {
178 !(c.is_ascii_alphabetic() || c.is_ascii_digit() || c == '_')
179 }
180 let needs_escape = s.is_empty()
181 || s.starts_with(forbidden_start)
182 || (s.len() > 1 && s.chars().skip(1).any(forbidden_subsequent));
183
184 if needs_escape {
185 write!(f, "`{s}`")
186 } else {
187 write!(f, "{s}")
188 }
189}