1use nu_protocol::{LabeledError, Span, Value, ast::CellPath};
2use semver::{BuildMetadata, Prerelease, Version};
3
4#[derive(Debug, Eq, PartialEq, Clone, Copy)]
5pub enum Action {
6 SemVerAction(SemVerAction),
7 Default,
8}
9
10#[derive(Debug, Eq, PartialEq, Clone, Copy)]
11pub enum SemVerAction {
12 Major,
13 Minor,
14 Patch,
15}
16
17#[derive(Default, Clone)]
18pub struct Inc {
19 pub error: Option<String>,
20 pub cell_path: Option<CellPath>,
21 pub action: Option<Action>,
22}
23
24impl Inc {
25 pub fn new() -> Self {
26 Default::default()
27 }
28
29 fn apply(&self, input: &str, head: Span) -> Value {
30 match &self.action {
31 Some(Action::SemVerAction(act_on)) => {
32 let mut ver = match semver::Version::parse(input) {
33 Ok(parsed_ver) => parsed_ver,
34 Err(_) => return Value::string(input, head),
35 };
36
37 match act_on {
38 SemVerAction::Major => Self::increment_major(&mut ver),
39 SemVerAction::Minor => Self::increment_minor(&mut ver),
40 SemVerAction::Patch => Self::increment_patch(&mut ver),
41 }
42
43 Value::string(ver.to_string(), head)
44 }
45 Some(Action::Default) | None => {
46 if let Ok(v) = input.parse::<u64>() {
47 Value::string((v + 1).to_string(), head)
48 } else {
49 Value::string(input, head)
50 }
51 }
52 }
53 }
54
55 pub fn increment_patch(v: &mut Version) {
56 v.patch += 1;
57 v.pre = Prerelease::EMPTY;
58 v.build = BuildMetadata::EMPTY;
59 }
60
61 pub fn increment_minor(v: &mut Version) {
62 v.minor += 1;
63 v.patch = 0;
64 v.pre = Prerelease::EMPTY;
65 v.build = BuildMetadata::EMPTY;
66 }
67
68 pub fn increment_major(v: &mut Version) {
69 v.major += 1;
70 v.minor = 0;
71 v.patch = 0;
72 v.pre = Prerelease::EMPTY;
73 v.build = BuildMetadata::EMPTY;
74 }
75
76 pub fn for_semver(&mut self, part: SemVerAction) {
77 if self.permit() {
78 self.action = Some(Action::SemVerAction(part));
79 } else {
80 self.log_error("can only apply one");
81 }
82 }
83
84 fn permit(&mut self) -> bool {
85 self.action.is_none()
86 }
87
88 fn log_error(&mut self, message: &str) {
89 self.error = Some(message.to_string());
90 }
91
92 pub fn inc(&self, head: Span, value: &Value) -> Result<Value, LabeledError> {
93 if let Some(cell_path) = &self.cell_path {
94 let cell_value = value.follow_cell_path(&cell_path.members)?;
95
96 let cell_value = self.inc_value(head, &cell_value)?;
97
98 let mut value = value.clone();
99 value.update_data_at_cell_path(&cell_path.members, cell_value)?;
100 Ok(value)
101 } else {
102 self.inc_value(head, value)
103 }
104 }
105
106 pub fn inc_value(&self, head: Span, value: &Value) -> Result<Value, LabeledError> {
107 match value {
108 Value::Int { val, .. } => Ok(Value::int(val + 1, head)),
109 Value::String { val, .. } => Ok(self.apply(val, head)),
110 x => {
111 let msg = x.coerce_string().map_err(|e| {
112 LabeledError::new("Unable to extract string").with_label(
113 format!("value cannot be converted to string {x:?} - {e}"),
114 head,
115 )
116 })?;
117
118 Err(LabeledError::new("Incorrect value").with_label(msg, head))
119 }
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 mod semver {
127 use nu_protocol::{Span, Value};
128
129 use crate::Inc;
130 use crate::inc::SemVerAction;
131
132 #[test]
133 fn major() {
134 let expected = Value::test_string("1.0.0");
135 let mut inc = Inc::new();
136 inc.for_semver(SemVerAction::Major);
137 assert_eq!(inc.apply("0.1.3", Span::test_data()), expected)
138 }
139
140 #[test]
141 fn minor() {
142 let expected = Value::test_string("0.2.0");
143 let mut inc = Inc::new();
144 inc.for_semver(SemVerAction::Minor);
145 assert_eq!(inc.apply("0.1.3", Span::test_data()), expected)
146 }
147
148 #[test]
149 fn patch() {
150 let expected = Value::test_string("0.1.4");
151 let mut inc = Inc::new();
152 inc.for_semver(SemVerAction::Patch);
153 assert_eq!(inc.apply("0.1.3", Span::test_data()), expected)
154 }
155 }
156}