nu_command/conversions/into/
semver_range.rs1use crate::semver::range::SemverRangeValue;
2use nu_engine::command_prelude::*;
3use nu_protocol::shell_error::generic::GenericError;
4
5#[derive(Clone)]
6pub struct IntoSemverRange;
7
8impl Command for IntoSemverRange {
9 fn name(&self) -> &str {
10 "into semver-range"
11 }
12
13 fn signature(&self) -> Signature {
14 Signature::build("into semver-range")
15 .input_output_types(vec![
16 (Type::String, Type::Custom("semver-range".into())),
17 (
18 Type::Custom("semver-range".into()),
19 Type::Custom("semver-range".into()),
20 ),
21 ])
22 .category(Category::Conversions)
23 }
24
25 fn description(&self) -> &str {
26 "Convert a string to a semver range."
27 }
28
29 fn search_terms(&self) -> Vec<&str> {
30 vec!["version", "requirement", "semantic"]
31 }
32
33 fn run(
34 &self,
35 engine_state: &EngineState,
36 _stack: &mut Stack,
37 call: &Call,
38 input: PipelineData,
39 ) -> Result<PipelineData, ShellError> {
40 let head = call.head;
41
42 input.map(
43 move |value| into_semver_range(&value, head),
44 engine_state.signals(),
45 )
46 }
47
48 fn examples(&self) -> Vec<Example<'static>> {
49 vec![
50 Example {
51 description: "Convert a string to a semver range",
52 example: "'>=1.0.0' | into semver-range",
53 result: None,
54 },
55 Example {
56 description: "Convert a complex range",
57 example: "'^1.2.3' | into semver-range",
58 result: None,
59 },
60 ]
61 }
62}
63
64fn into_semver_range(input: &Value, head: Span) -> Value {
65 match input {
66 Value::Custom { val, .. } if val.type_name() == "semver-range" => input.clone(),
67 Value::String { val, .. } => match semver::VersionReq::parse(val) {
68 Ok(requirement) => Value::custom(Box::new(SemverRangeValue::new(requirement)), head),
69 Err(_) => Value::error(
70 ShellError::Generic(
71 GenericError::new(
72 format!("Cannot convert \"{val}\" to a semver range"),
73 "the given string is not a valid semver requirement",
74 head,
75 )
76 .with_help("expected format: >=1.0.0, ^1.2.3, ~1.2, etc."),
77 ),
78 head,
79 ),
80 },
81 _ => Value::error(
82 ShellError::Generic(GenericError::new(
83 format!("Cannot convert {} to semver range", input.get_type()),
84 "expected a string or semver-range value",
85 head,
86 )),
87 head,
88 ),
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 fn get_custom_value(value: &Value) -> &SemverRangeValue {
97 match value {
98 Value::Custom { val, .. } => val.as_any().downcast_ref::<SemverRangeValue>().unwrap(),
99 _ => panic!("Expected Custom value"),
100 }
101 }
102
103 #[test]
104 fn test_into_semver_range_from_string() {
105 let value = Value::string(">=1.0.0", Span::test_data());
106 let result = into_semver_range(&value, Span::test_data());
107
108 assert!(matches!(result, Value::Custom { .. }));
109 let range_val = get_custom_value(&result);
110 assert_eq!(range_val.requirement.to_string(), ">=1.0.0");
111 }
112
113 #[test]
114 fn test_into_semver_range_caret() {
115 let value = Value::string("^1.2.3", Span::test_data());
116 let result = into_semver_range(&value, Span::test_data());
117
118 let range_val = get_custom_value(&result);
119 assert_eq!(range_val.requirement.to_string(), "^1.2.3");
120 }
121
122 #[test]
123 fn test_into_semver_range_tilde() {
124 let value = Value::string("~1.2", Span::test_data());
125 let result = into_semver_range(&value, Span::test_data());
126
127 let range_val = get_custom_value(&result);
128 assert_eq!(range_val.requirement.to_string(), "~1.2");
129 }
130
131 #[test]
132 fn test_into_semver_range_complex() {
133 let value = Value::string(">=1.0.0, <2.0.0", Span::test_data());
134 let result = into_semver_range(&value, Span::test_data());
135
136 let range_val = get_custom_value(&result);
137 assert_eq!(range_val.requirement.to_string(), ">=1.0.0, <2.0.0");
138 }
139
140 #[test]
141 fn test_into_semver_range_wildcard() {
142 let value = Value::string("*", Span::test_data());
143 let result = into_semver_range(&value, Span::test_data());
144
145 let range_val = get_custom_value(&result);
146 assert_eq!(range_val.requirement.to_string(), "*");
147 }
148
149 #[test]
150 fn test_into_semver_range_from_semver_range() {
151 let original = SemverRangeValue::new(semver::VersionReq::parse(">=1.0.0").unwrap());
152 let value = Value::custom(Box::new(original), Span::test_data());
153 let result = into_semver_range(&value, Span::test_data());
154
155 let range_val = get_custom_value(&result);
157 assert_eq!(range_val.requirement.to_string(), ">=1.0.0");
158 }
159
160 #[test]
161 fn test_into_semver_range_invalid() {
162 let value = Value::string("not-a-range", Span::test_data());
163 let result = into_semver_range(&value, Span::test_data());
164
165 assert!(matches!(result, Value::Error { .. }));
166 }
167
168 #[test]
169 fn test_into_semver_range_unsupported_type() {
170 let value = Value::int(42, Span::test_data());
171 let result = into_semver_range(&value, Span::test_data());
172
173 assert!(matches!(result, Value::Error { .. }));
174 }
175
176 #[test]
177 fn test_into_semver_range_wrong_custom_value() {
178 use crate::semver::value::SemverValue;
180 let semver = SemverValue::new(semver::Version::parse("1.2.3").unwrap());
181 let value = Value::custom(Box::new(semver), Span::test_data());
182 let result = into_semver_range(&value, Span::test_data());
183
184 assert!(matches!(result, Value::Error { .. }));
185 }
186}
187
188#[cfg(test)]
189mod test {
190 use super::*;
191
192 #[test]
193 fn test_examples() -> nu_test_support::Result {
194 nu_test_support::test().examples(IntoSemverRange)
195 }
196}