Skip to main content

morphir_core/traversal/
cursor.rs

1use std::fmt;
2
3/// A typed semantic location within a Morphir IR tree.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum CursorSegment {
6    Distribution,
7    Package(String),
8    Dependency(String),
9    Module(String),
10    Type(String),
11    Value(String),
12    Constructor(String),
13    Field(String),
14    Argument(usize),
15    PatternCase(usize),
16    LetBinding(String),
17    Branch(&'static str),
18}
19
20impl fmt::Display for CursorSegment {
21    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            Self::Distribution => formatter.write_str("distribution"),
24            Self::Package(name) => write!(formatter, "package:{name}"),
25            Self::Dependency(name) => write!(formatter, "dependency:{name}"),
26            Self::Module(name) => write!(formatter, "module:{name}"),
27            Self::Type(name) => write!(formatter, "type:{name}"),
28            Self::Value(name) => write!(formatter, "value:{name}"),
29            Self::Constructor(name) => write!(formatter, "constructor:{name}"),
30            Self::Field(name) => write!(formatter, "field:{name}"),
31            Self::Argument(index) => write!(formatter, "argument:{index}"),
32            Self::PatternCase(index) => write!(formatter, "pattern-case:{index}"),
33            Self::LetBinding(name) => write!(formatter, "let-binding:{name}"),
34            Self::Branch(name) => write!(formatter, "branch:{name}"),
35        }
36    }
37}
38
39/// Cursor used by visitors and migration diagnostics.
40#[derive(Debug, Clone, Default, PartialEq, Eq)]
41pub struct IrCursor {
42    segments: Vec<CursorSegment>,
43}
44
45impl IrCursor {
46    /// Create a cursor at the root of an IR artifact.
47    pub fn root() -> Self {
48        Self::default()
49    }
50
51    pub fn from_segments(segments: impl IntoIterator<Item = CursorSegment>) -> Self {
52        Self {
53            segments: segments.into_iter().collect(),
54        }
55    }
56
57    pub fn is_root(&self) -> bool {
58        self.segments.is_empty()
59    }
60
61    pub fn segments(&self) -> &[CursorSegment] {
62        &self.segments
63    }
64
65    /// Return a cursor extended with one child segment.
66    pub fn child(mut self, segment: CursorSegment) -> Self {
67        self.segments.push(segment);
68        self
69    }
70
71    /// Run an operation under a child segment and restore the parent cursor.
72    pub fn with_segment<R>(
73        &mut self,
74        segment: CursorSegment,
75        operation: impl FnOnce(&mut Self) -> R,
76    ) -> R {
77        self.segments.push(segment);
78        let result = operation(self);
79        self.segments.pop();
80        result
81    }
82}
83
84impl fmt::Display for IrCursor {
85    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86        let path = self
87            .segments
88            .iter()
89            .map(ToString::to_string)
90            .collect::<Vec<_>>()
91            .join("/");
92        formatter.write_str(&path)
93    }
94}