1use crate::{Expression, Identifier, Location, Node, NodeID, simple_node_impl};
18
19use leo_span::{Span, Symbol};
20
21use itertools::Itertools;
22use serde::{Deserialize, Serialize};
23use std::{fmt, hash::Hash};
24
25#[derive(Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
27pub struct Path {
28 user_program: Option<Identifier>,
30
31 qualifier: Vec<Identifier>,
34
35 identifier: Identifier,
37
38 target: PathTarget,
40
41 pub span: Span,
43
44 pub id: NodeID,
46}
47
48#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
49pub enum PathTarget {
50 Unresolved,
51 Local(Symbol),
52 Global(Location),
53}
54
55simple_node_impl!(Path);
56
57impl Path {
58 pub fn new(
66 user_program: Option<Identifier>,
67 qualifier: Vec<Identifier>,
68 identifier: Identifier,
69 span: Span,
70 id: NodeID,
71 ) -> Self {
72 Self { user_program, qualifier, identifier, target: PathTarget::Unresolved, span, id }
73 }
74
75 pub fn identifier(&self) -> &Identifier {
77 &self.identifier
78 }
79
80 pub fn qualifier(&self) -> &[Identifier] {
82 &self.qualifier
83 }
84
85 pub fn segments_iter(&self) -> impl Iterator<Item = Symbol> + '_ {
87 self.qualifier.iter().map(|id| id.name).chain(std::iter::once(self.identifier.name))
88 }
89
90 pub fn segments(&self) -> Vec<Symbol> {
92 self.segments_iter().collect()
93 }
94
95 pub fn user_program(&self) -> Option<&Identifier> {
97 self.user_program.as_ref()
98 }
99
100 pub fn with_user_program(mut self, user_program: Identifier) -> Self {
102 self.user_program = Some(user_program);
103 self
104 }
105
106 pub fn span(&self) -> Span {
107 self.span
108 }
109
110 pub fn id(&self) -> NodeID {
111 self.id
112 }
113
114 pub fn is_resolved(&self) -> bool {
115 !matches!(self.target, PathTarget::Unresolved)
116 }
117
118 pub fn is_local(&self) -> bool {
119 matches!(self.target, PathTarget::Local(_))
120 }
121
122 pub fn is_global(&self) -> bool {
123 matches!(self.target, PathTarget::Global(_))
124 }
125
126 pub fn program(&self) -> Option<Symbol> {
133 if let Some(id) = &self.user_program {
134 return Some(id.name);
135 }
136
137 match &self.target {
138 PathTarget::Global(location) => Some(location.program),
139 _ => None,
140 }
141 }
142
143 pub fn try_local_symbol(&self) -> Option<Symbol> {
145 match self.target {
146 PathTarget::Local(sym) => Some(sym),
147 _ => None,
148 }
149 }
150
151 pub fn try_global_location(&self) -> Option<&Location> {
153 match &self.target {
154 PathTarget::Global(loc) => Some(loc),
155 _ => None,
156 }
157 }
158
159 pub fn expect_local_symbol(&self) -> Symbol {
161 match self.target {
162 PathTarget::Local(sym) => sym,
163 _ => panic!("Expected a local path, found {:?}", self.target),
164 }
165 }
166
167 pub fn expect_global_location(&self) -> &Location {
169 match &self.target {
170 PathTarget::Global(loc) => loc,
171 _ => panic!("Expected a global path, found {:?}", self.target),
172 }
173 }
174
175 pub fn to_local(self) -> Self {
177 Self { target: PathTarget::Local(self.identifier.name), ..self }
178 }
179
180 pub fn to_global(self, location: Location) -> Self {
182 Self { target: PathTarget::Global(location), ..self }
183 }
184
185 pub fn with_updated_last_symbol(self, new_symbol: Symbol) -> Self {
194 let Path { mut identifier, target, user_program, qualifier, span, id } = self;
195
196 identifier.name = new_symbol;
198
199 let target = match target {
200 PathTarget::Unresolved => PathTarget::Unresolved,
201
202 PathTarget::Local(_) => PathTarget::Local(new_symbol),
203
204 PathTarget::Global(location) => {
205 let Location { program, mut path } = location;
206
207 assert!(!path.is_empty(), "global location must have at least one path segment");
208
209 *path.last_mut().unwrap() = new_symbol;
210
211 PathTarget::Global(Location { program, path })
212 }
213 };
214
215 Self { user_program, qualifier, identifier, target, span, id }
216 }
217
218 pub fn resolve_as_global_in_module<I>(self, program: Symbol, current_module: I) -> Self
228 where
229 I: IntoIterator<Item = Symbol>,
230 {
231 let Path { user_program, qualifier, identifier, span, id, .. } = self;
232
233 let mut path: Vec<Symbol> = Vec::new();
234
235 path.extend(current_module);
237
238 path.extend(qualifier.iter().map(|id| id.name));
240
241 path.push(identifier.name);
243
244 let target = PathTarget::Global(Location { program: user_program.map(|id| id.name).unwrap_or(program), path });
245
246 Self { user_program, qualifier, identifier, target, span, id }
247 }
248}
249
250impl fmt::Display for Path {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 let program: Option<Symbol> = self
254 .user_program
255 .as_ref()
256 .map(|id| id.name) .or_else(|| self.try_global_location().map(|global| global.program));
258
259 if let Some(program) = program {
261 write!(f, "{}.aleo/", program)?;
262 }
263
264 if !self.qualifier.is_empty() {
266 write!(f, "{}::", self.qualifier.iter().map(|id| &id.name).format("::"))?;
267 }
268
269 write!(f, "{}", self.identifier.name)
271 }
272}
273
274impl fmt::Debug for Path {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 write!(f, "{}", self)?;
278
279 match &self.target {
281 PathTarget::Local(sym) => write!(f, " [local: {sym}]"),
282 PathTarget::Global(loc) => {
283 write!(f, " [global: {loc}]")
284 }
285 PathTarget::Unresolved => write!(f, " [unresolved]"),
286 }
287 }
288}
289
290impl From<Path> for Expression {
291 fn from(value: Path) -> Self {
292 Expression::Path(value)
293 }
294}