maven_rs/types/
mod.rs

1use std::{fmt::Display, str::FromStr};
2
3use prop::ParseState;
4
5use crate::{
6    editor::{InvalidValueError, PomValue},
7    utils::{parse::ParseErrorExt, serde_utils::serde_via_string_types},
8};
9
10pub(crate) mod prop;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum Property {
14    /// A variable
15    /// ```xml
16    /// <version>${project.version}</version>
17    /// ```
18    Variable(String),
19    /// An unclosed variable
20    ///
21    /// ```xml
22    /// <version>${project.version</version>
23    /// ```
24    UnclosedVariable(String),
25    /// A literal string
26    ///
27    /// ```xml
28    /// <version>1.0.0</version>
29    /// ```
30    Literal(String),
31    /// An expression
32    /// ```xml
33    /// <version>${project.version}-${maven.buildNumber}</version>
34    /// ```
35    /// This would be parsed as
36    /// ```no_compile
37    /// let expression = vec![
38    ///     Property::Variable("project.version".to_string()),
39    ///     Property::Literal("-".to_string()),
40    ///     Property::Variable("maven.buildNumber".to_string())
41    /// ];
42    /// ```
43    Expression(Vec<Property>),
44}
45impl Default for Property {
46    fn default() -> Self {
47        Property::Literal(Default::default())
48    }
49}
50impl Property {
51    pub fn is_variable(&self) -> bool {
52        matches!(self, Property::Variable(_))
53    }
54    pub fn is_maven_variable(&self) -> bool {
55        let Property::Variable(name) = self else {
56            return false;
57        };
58        name.starts_with("maven.")
59    }
60    pub fn is_project_variable(&self) -> bool {
61        let Property::Variable(name) = self else {
62            return false;
63        };
64        name.starts_with("project.")
65    }
66}
67
68impl TryFrom<String> for Property {
69    type Error = ParseErrorExt<String, winnow::error::ContextError>;
70
71    fn try_from(value: String) -> Result<Self, Self::Error> {
72        match ParseState::default().parse(&value) {
73            Ok(o) => Ok(o),
74            Err(e) => {
75                let offset = e.offset;
76                let inner = e.inner;
77                Err(ParseErrorExt::new(value, offset, inner))
78            }
79        }
80    }
81}
82impl<'s> TryFrom<&'s str> for Property {
83    type Error = ParseErrorExt<&'s str, winnow::error::ContextError>;
84
85    fn try_from(value: &'s str) -> Result<Self, Self::Error> {
86        ParseState::default()
87            .parse(value)
88            .map_err(|e| e.map(|s| s.input))
89    }
90}
91impl FromStr for Property {
92    type Err = ParseErrorExt<(), winnow::error::ContextError>;
93
94    fn from_str(s: &str) -> Result<Self, Self::Err> {
95        ParseState::default().parse(s).map_err(|e| e.map(|_| ()))
96    }
97}
98impl Display for Property {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        match self {
101            Property::Variable(name) => write!(f, "${{{}}}", name),
102            Property::UnclosedVariable(value) => write!(f, "${{{}", value),
103            Property::Literal(value) => write!(f, "{}", value),
104            Property::Expression(vec) => {
105                for part in vec {
106                    part.fmt(f)?;
107                }
108                Ok(())
109            }
110        }
111    }
112}
113impl PomValue for Property {
114    fn from_str_for_editor(value: &str) -> Result<Self, crate::editor::InvalidValueError> {
115        Self::from_str(value).map_err(|err| InvalidValueError::InvalidFormattedValue {
116            error: err.to_string(),
117        })
118    }
119
120    fn to_string_for_editor(&self) -> String {
121        self.to_string()
122    }
123}
124
125serde_via_string_types!(Property);
126
127#[cfg(test)]
128mod tests {
129    use crate::types::Property;
130    #[test]
131    fn test_regular_string_parse() {
132        let value = Property::try_from("test").unwrap();
133        assert_eq!(value, Property::Literal("test".to_string()));
134    }
135    #[test]
136    fn test_variable_parse() {
137        let value = "${test}".parse::<Property>().unwrap();
138        assert_eq!(value, Property::Variable("test".to_string()));
139    }
140
141    #[test]
142    fn test_maven_variable() {
143        let value = "${maven.version}".parse::<Property>().unwrap();
144        assert!(value.is_maven_variable());
145    }
146    #[test]
147    fn test_commons_logging() {
148        let value = "${commons-logging.version}".parse::<Property>().unwrap();
149        assert!(!value.is_maven_variable());
150    }
151    #[test]
152    fn test_unclosed_var() {
153        let result = "${var".parse::<Property>();
154        assert!(result.is_err())
155    }
156}