Parse one cron field (e.g., "*/5", "1-3", "1,2,3", "*").
(field: &str, min: u8, max: u8)
| 150 | |
| 151 | /// Parse one cron field (e.g., "*/5", "1-3", "1,2,3", "*"). |
| 152 | fn parse_field(field: &str, min: u8, max: u8) -> crate::Result<BTreeSet<u8>> { |
| 153 | let mut result = BTreeSet::new(); |
| 154 | |
| 155 | for part in field.split(',') { |
| 156 | let part = part.trim(); |
| 157 | if part == "*" { |
| 158 | for v in min..=max { |
| 159 | result.insert(v); |
| 160 | } |
| 161 | } else if let Some(step_str) = part.strip_prefix("*/") { |
| 162 | let step: u8 = step_str.parse().map_err(|_| crate::Error::BadRequest { |
| 163 | detail: format!("invalid step: '{step_str}'"), |
| 164 | })?; |
| 165 | if step == 0 { |
| 166 | return Err(crate::Error::BadRequest { |
| 167 | detail: "step cannot be zero".to_string(), |
| 168 | }); |
| 169 | } |
| 170 | let mut v = min; |
| 171 | while v <= max { |
| 172 | result.insert(v); |
| 173 | v = v.saturating_add(step); |
| 174 | } |
| 175 | } else if part.contains('/') { |
| 176 | // range/step: "1-10/2" |
| 177 | let (range_part, step_str) = |
| 178 | part.split_once('/') |
| 179 | .ok_or_else(|| crate::Error::BadRequest { |
| 180 | detail: format!("invalid field: '{part}'"), |
| 181 | })?; |
| 182 | let step: u8 = step_str.parse().map_err(|_| crate::Error::BadRequest { |
| 183 | detail: format!("invalid step: '{step_str}'"), |
| 184 | })?; |
| 185 | if step == 0 { |
| 186 | return Err(crate::Error::BadRequest { |
| 187 | detail: "step cannot be zero".to_string(), |
| 188 | }); |
| 189 | } |
| 190 | let (lo, hi) = parse_range(range_part, min, max)?; |
| 191 | let mut v = lo; |
| 192 | while v <= hi { |
| 193 | result.insert(v); |
| 194 | v = v.saturating_add(step); |
| 195 | } |
| 196 | } else if part.contains('-') { |
| 197 | let (lo, hi) = parse_range(part, min, max)?; |
| 198 | for v in lo..=hi { |
| 199 | result.insert(v); |
| 200 | } |
| 201 | } else { |
| 202 | let v: u8 = part.parse().map_err(|_| crate::Error::BadRequest { |
| 203 | detail: format!("invalid value: '{part}'"), |
| 204 | })?; |
| 205 | if v < min || v > max { |
| 206 | return Err(crate::Error::BadRequest { |
| 207 | detail: format!("value {v} out of range {min}-{max}"), |
| 208 | }); |
| 209 | } |