rib/type_checker/
path.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16use std::fmt::Display;
17
18#[derive(Clone, Debug, Default)]
19pub struct Path(Vec<PathElem>);
20
21impl Path {
22    pub fn is_empty(&self) -> bool {
23        self.0.is_empty()
24    }
25
26    pub fn current(&self) -> Option<&PathElem> {
27        self.0.first()
28    }
29
30    pub fn progress(&mut self) {
31        if !self.0.is_empty() {
32            self.0.remove(0);
33        }
34    }
35
36    pub fn from_elem(elem: PathElem) -> Self {
37        Path(vec![elem])
38    }
39
40    pub fn from_elems(elems: Vec<&str>) -> Self {
41        Path(
42            elems
43                .iter()
44                .map(|x| PathElem::Field(x.to_string()))
45                .collect(),
46        )
47    }
48
49    pub fn push_front(&mut self, elem: PathElem) {
50        self.0.insert(0, elem);
51    }
52}
53
54pub enum PathType {
55    RecordPath(Path),
56    IndexPath(Path),
57}
58
59impl PathType {
60    pub fn from_path(path: &Path) -> Option<PathType> {
61        if path.0.first().map(|elem| elem.is_field()).unwrap_or(false) {
62            Some(PathType::RecordPath(path.clone()))
63        } else if path.0.first().map(|elem| elem.is_index()).unwrap_or(false) {
64            Some(PathType::IndexPath(path.clone()))
65        } else {
66            None
67        }
68    }
69}
70
71impl Display for Path {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        let mut is_first = true;
74
75        for elem in &self.0 {
76            match elem {
77                PathElem::Field(name) => {
78                    if is_first {
79                        write!(f, "{}", name)?;
80                        is_first = false;
81                    } else {
82                        write!(f, ".{}", name)?;
83                    }
84                }
85                PathElem::Index(index) => {
86                    if is_first {
87                        write!(f, "index: {}", index)?;
88                        is_first = false;
89                    } else {
90                        write!(f, "[{}]", index)?;
91                    }
92                }
93            }
94        }
95        Ok(())
96    }
97}
98
99#[derive(Clone, Debug, PartialEq)]
100pub enum PathElem {
101    Field(String),
102    Index(usize),
103}
104
105impl PathElem {
106    pub fn is_field(&self) -> bool {
107        matches!(self, PathElem::Field(_))
108    }
109
110    pub fn is_index(&self) -> bool {
111        matches!(self, PathElem::Index(_))
112    }
113}