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 Variable(String),
19 UnclosedVariable(String),
25 Literal(String),
31 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}