1use alloc::format;
2use core::{
3 cmp::Ordering,
4 fmt,
5 hash::{Hash, Hasher},
6 str::FromStr,
7};
8
9use anyhow::anyhow;
10
11use super::{
12 interner::{symbols, Symbol},
13 SourceSpan, Spanned,
14};
15use crate::{
16 define_attr_type,
17 formatter::{self, PrettyPrint},
18};
19
20#[derive(Copy, Clone, PartialEq, Eq, Hash, Spanned)]
22pub struct FunctionIdent {
23 pub module: Ident,
24 #[span]
25 pub function: Ident,
26}
27define_attr_type!(FunctionIdent);
28impl FunctionIdent {
29 pub fn display(&self) -> impl fmt::Display + '_ {
30 use crate::formatter::*;
31
32 flatten(
33 const_text(self.module.as_str())
34 + const_text("::")
35 + const_text(self.function.as_str()),
36 )
37 }
38}
39impl FromStr for FunctionIdent {
40 type Err = anyhow::Error;
41
42 fn from_str(s: &str) -> Result<Self, Self::Err> {
43 match s.rsplit_once("::") {
44 Some((ns, id)) => {
45 let module = Ident::with_empty_span(Symbol::intern(ns));
46 let function = Ident::with_empty_span(Symbol::intern(id));
47 Ok(Self { module, function })
48 }
49 None => Err(anyhow!(
50 "invalid function name, expected fully-qualified identifier, e.g. \
51 'std::math::u64::checked_add'"
52 )),
53 }
54 }
55}
56impl fmt::Debug for FunctionIdent {
57 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58 f.debug_struct("FunctionIdent")
59 .field("module", &self.module.name)
60 .field("function", &self.function.name)
61 .finish()
62 }
63}
64impl fmt::Display for FunctionIdent {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 self.pretty_print(f)
67 }
68}
69impl PrettyPrint for FunctionIdent {
70 fn render(&self) -> formatter::Document {
71 use crate::formatter::*;
72
73 flatten(const_text("(") + display(self.module) + const_text(" ") + display(self.function))
74 }
75}
76impl PartialOrd for FunctionIdent {
77 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
78 Some(self.cmp(other))
79 }
80}
81impl Ord for FunctionIdent {
82 fn cmp(&self, other: &Self) -> Ordering {
83 self.module.cmp(&other.module).then(self.function.cmp(&other.function))
84 }
85}
86
87#[derive(Copy, Clone, Eq, Spanned)]
91pub struct Ident {
92 pub name: Symbol,
93 #[span]
94 pub span: SourceSpan,
95}
96define_attr_type!(Ident);
97impl Default for Ident {
98 fn default() -> Self {
99 Self {
100 name: symbols::Empty,
101 span: SourceSpan::UNKNOWN,
102 }
103 }
104}
105impl FromStr for Ident {
106 type Err = core::convert::Infallible;
107
108 fn from_str(name: &str) -> Result<Self, Self::Err> {
109 Ok(Self::from(name))
110 }
111}
112impl<'a> From<&'a str> for Ident {
113 fn from(name: &'a str) -> Self {
114 Self::with_empty_span(Symbol::intern(name))
115 }
116}
117impl From<Symbol> for Ident {
118 #[inline]
119 fn from(sym: Symbol) -> Self {
120 Self::with_empty_span(sym)
121 }
122}
123impl From<Ident> for Symbol {
124 #[inline]
125 fn from(id: Ident) -> Self {
126 id.as_symbol()
127 }
128}
129impl Ident {
130 #[inline]
131 pub const fn new(name: Symbol, span: SourceSpan) -> Ident {
132 Ident { name, span }
133 }
134
135 #[inline]
136 pub const fn with_empty_span(name: Symbol) -> Ident {
137 Ident::new(name, SourceSpan::UNKNOWN)
138 }
139
140 #[inline]
141 pub fn as_str(self) -> &'static str {
142 self.name.as_str()
143 }
144
145 #[inline(always)]
146 pub fn as_symbol(self) -> Symbol {
147 self.name
148 }
149
150 pub fn requires_quoting(&self) -> bool {
153 self.as_str().contains(|c| match c {
154 c if c.is_ascii_control() => true,
155 ' ' | '\'' | '"' | ',' | ';' | '[' | ']' => true,
156 c if c.is_ascii_graphic() => false,
157 _ => true,
158 })
159 }
160}
161impl AsRef<str> for Ident {
162 #[inline(always)]
163 fn as_ref(&self) -> &str {
164 self.as_str()
165 }
166}
167impl alloc::borrow::Borrow<Symbol> for Ident {
168 #[inline]
169 fn borrow(&self) -> &Symbol {
170 &self.name
171 }
172}
173impl alloc::borrow::Borrow<str> for Ident {
174 #[inline]
175 fn borrow(&self) -> &str {
176 self.as_str()
177 }
178}
179impl Ord for Ident {
180 #[inline]
181 fn cmp(&self, other: &Self) -> Ordering {
182 self.as_str().cmp(other.as_str())
183 }
184}
185impl PartialOrd for Ident {
186 #[inline]
187 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
188 Some(self.cmp(other))
189 }
190}
191impl PartialEq for Ident {
192 #[inline]
193 fn eq(&self, rhs: &Self) -> bool {
194 self.name == rhs.name
195 }
196}
197impl PartialEq<Symbol> for Ident {
198 #[inline]
199 fn eq(&self, rhs: &Symbol) -> bool {
200 self.name.eq(rhs)
201 }
202}
203impl PartialEq<str> for Ident {
204 fn eq(&self, rhs: &str) -> bool {
205 self.name.as_str() == rhs
206 }
207}
208impl Hash for Ident {
209 fn hash<H: Hasher>(&self, state: &mut H) {
210 self.name.hash(state);
211 }
212}
213impl fmt::Debug for Ident {
214 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215 self.pretty_print(f)
216 }
217}
218impl fmt::Display for Ident {
219 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220 self.pretty_print(f)
221 }
222}
223impl PrettyPrint for Ident {
224 fn render(&self) -> formatter::Document {
225 use crate::formatter::*;
226
227 if self.requires_quoting() {
228 text(format!("\"{}\"", self.as_str().escape_default()))
229 } else {
230 text(format!("#{}", self.as_str()))
231 }
232 }
233}