diskplan_schema/
expression.rs1use std::{fmt::Display, vec};
2
3#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
5pub struct Expression<'t>(Vec<Token<'t>>);
6
7impl<'t> Expression<'t> {
8 pub fn tokens(&self) -> &[Token<'t>] {
10 &self.0[..]
11 }
12}
13
14impl<'t> From<Vec<Token<'t>>> for Expression<'t> {
15 fn from(tokens: Vec<Token<'t>>) -> Self {
16 Expression(tokens)
17 }
18}
19
20impl<'t> From<&[Token<'t>]> for Expression<'t> {
21 fn from(tokens: &[Token<'t>]) -> Self {
22 Expression(tokens.into())
23 }
24}
25
26impl Display for Expression<'_> {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 for token in self.0.iter() {
29 write!(f, "{token}")?;
30 }
31 Ok(())
32 }
33}
34
35impl PartialEq<&str> for Expression<'_> {
36 fn eq(&self, other: &&str) -> bool {
37 match &self.0[..] {
40 [Token::Text(text)] => *text == *other,
41 _ => false,
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
48pub enum Token<'t> {
49 Text(&'t str),
51 Variable(Identifier<'t>),
53 Special(Special),
55}
56
57impl Display for Token<'_> {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 match self {
60 Token::Text(s) => f.write_str(s),
61 Token::Variable(v) => write!(f, "${{{v}}}"),
62 Token::Special(sp) => write!(f, "${{{sp}}}"),
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
69pub enum Special {
70 PathRelative,
72 PathAbsolute,
74 PathNameOnly,
76 ParentRelative,
78 ParentAbsolute,
80 ParentNameOnly,
82 RootPath,
84}
85
86impl Special {
87 pub const SAME_PATH_RELATIVE: &'static str = "PATH";
89 pub const SAME_PATH_ABSOLUTE: &'static str = "FULL_PATH";
91 pub const SAME_PATH_NAME: &'static str = "NAME";
93 pub const PARENT_PATH_RELATIVE: &'static str = "PARENT_PATH";
95 pub const PARENT_PATH_ABSOLUTE: &'static str = "PARENT_FULL_PATH";
97 pub const PARENT_PATH_NAME: &'static str = "PARENT_NAME";
99 pub const ROOT_PATH: &'static str = "ROOT_PATH";
101}
102
103impl Display for Special {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.write_str(match self {
106 Special::PathRelative => Special::SAME_PATH_RELATIVE,
107 Special::PathAbsolute => Special::SAME_PATH_ABSOLUTE,
108 Special::PathNameOnly => Special::SAME_PATH_NAME,
109 Special::ParentRelative => Special::PARENT_PATH_RELATIVE,
110 Special::ParentAbsolute => Special::PARENT_PATH_ABSOLUTE,
111 Special::ParentNameOnly => Special::PARENT_PATH_NAME,
112 Special::RootPath => Special::ROOT_PATH,
113 })
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
119pub struct Identifier<'t>(&'t str);
120
121impl<'t> Identifier<'t> {
122 pub fn new(s: &'t str) -> Self {
124 Identifier(s)
125 }
126
127 pub fn value(&self) -> &'t str {
129 self.0
130 }
131}
132
133impl Display for Identifier<'_> {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 write!(f, "{}", self.0)
136 }
137}
138
139impl<'a> From<&'a str> for Identifier<'a> {
140 fn from(s: &'a str) -> Self {
141 Identifier::new(s)
142 }
143}
144
145impl<'a> From<Identifier<'a>> for Expression<'a> {
146 fn from(identifier: Identifier<'a>) -> Self {
147 Expression(vec![Token::Variable(identifier)])
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 fn test_expression() -> Expression<'static> {
156 Expression(vec![
157 Token::Text("normal text/"),
158 Token::Variable(Identifier("a_variable")),
159 Token::Text("/"),
160 Token::Special(Special::ParentRelative),
161 Token::Text("_AAA"),
162 ])
163 }
164
165 #[test]
166 fn format_identifier() {
167 assert_eq!(&format!("{}", Identifier("something")), "something");
168 }
169
170 #[test]
171 fn format_variable() {
172 let something = Identifier("something");
173 assert_eq!(&format!("{}", Token::Variable(something)), "${something}");
174 }
175
176 #[test]
177 fn format_expression_all_types() {
178 let expr = test_expression();
179 assert_eq!(
180 &format!("{expr}"),
181 "normal text/${a_variable}/${PARENT_PATH}_AAA"
182 );
183 }
184
185 #[test]
186 fn formatted_expression_is_valid_schema_expression() {
187 let expr = test_expression();
188 let schema_text = format!("symlink/ -> {expr}");
189 let schema_node = crate::parse_schema(&schema_text).unwrap();
190 let directory_schema = schema_node.schema.as_directory().unwrap();
191 let (_, symlink_node) = directory_schema.entries().first().unwrap();
192 let symlink_expression = symlink_node.symlink.as_ref().unwrap();
193
194 assert_eq!(*symlink_expression, expr);
195 }
196}