### describe_with Source: https://docs.rs/cronspeak/latest/cronspeak/all.html Generates a human-readable description of a cron expression using specific formatting options. ```APIDOC ## describe_with(expression: &str, options: Options) -> Result ### Description Parses a cron expression string and returns a human-readable description based on the provided configuration options. ### Parameters - **expression** (&str) - Required - The cron expression string to be parsed. - **options** (Options) - Required - Configuration options for the description output. ``` -------------------------------- ### describe_with(expr: &str, options: &Options) -> Result Source: https://docs.rs/cronspeak/latest/cronspeak/fn.describe_with.html Describes a cron expression using the provided Options. Returns a Result containing the description string or a CronError if the expression is invalid. ```APIDOC ## describe_with ### Description Describes a cron expression using the given `Options`. ### Signature `pub fn describe_with(expr: &str, options: &Options) -> Result` ### Parameters - **expr** (&str) - The cron expression string to describe. - **options** (&Options) - The configuration options used for the description. ### Returns - **Result** - Returns a string description of the cron expression on success, or a `CronError` if the expression is invalid. ``` -------------------------------- ### Implement Kind Bounds and Display Max Source: https://docs.rs/cronspeak/latest/src/cronspeak/segment.rs.html Methods on Kind provide the valid numeric bounds for each field and the maximum displayable value for step calculations. ```rust impl Kind { /// Inclusive `(min, max)` accepted for the field. Day-of-week allows 7 /// (an alias for Sunday) on input. const fn bounds(self) -> (u32, u32) { match self { Kind::Minute => (0, 59), Kind::Hour => (0, 23), Kind::Dom => (1, 31), Kind::Month => (1, 12), Kind::Dow => (0, 7), } } /// The largest *displayable* value, used as the implicit upper bound of the /// `a/n` step form. Day-of-week tops out at Saturday (6), since 7 == Sunday. const fn display_max(self) -> u32 { match self { Kind::Dow => 6, _ => self.bounds().1, } } } ``` -------------------------------- ### Naming and Formatting Utilities in Rust Source: https://docs.rs/cronspeak/latest/src/cronspeak/names.rs.html Internal helper functions for converting months, days, and clock times into formatted strings, along with list joining and capitalization utilities. ```rust //! Naming helpers: clock times, month/day names, and list joining. use alloc::format; use alloc::string::String; use crate::TimeFormat; const MONTHS: [&str; 12] = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ]; const DAYS: [&str; 7] = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", ]; /// Month name for `1..=12`; falls back to the number if out of range. pub(crate) fn month_name(m: u32) -> String { MONTHS .get((m as usize).wrapping_sub(1)) .map_or_else(|| format!("month {m}"), |s| String::from(*s)) } /// Day-of-week name for `0..=7` (0 and 7 both = Sunday). pub(crate) fn day_name(w: u32) -> String { DAYS.get((w % 7) as usize) .map_or_else(|| format!("day {w}"), |s| String::from(*s)) } /// Render a clock time in the requested [`TimeFormat`]. pub(crate) fn clock(hour: u32, minute: u32, fmt: TimeFormat) -> String { match fmt { TimeFormat::H24 => format!("{hour:02}:{minute:02}"), TimeFormat::H12 => { let period = if hour < 12 { "AM" } else { "PM" }; let h12 = match hour % 12 { 0 => 12, h => h, }; format!("{h12:02}:{minute:02} {period}") } } } /// Join items in English: `"a"`, `"a and b"`, `"a, b and c"`. pub(crate) fn human_list(items: &[String]) -> String { match items { [] => String::new(), [one] => one.clone(), [a, b] => format!("{a} and {b}"), [rest @ .., last] => format!("{} and {last}", rest.join(", ")), } } /// Capitalize the first character of `s`. pub(crate) fn capitalize(s: &str) -> String { let mut chars = s.chars(); match chars.next() { None => String::new(), Some(first) => first.to_uppercase().collect::() + chars.as_str(), } } ``` -------------------------------- ### Describe a Schedule Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html The primary entry point for converting a parsed cron schedule into a human-readable string. ```rust pub(crate) fn describe(schedule: &Schedule, options: Options) -> String { let fmt = options.format(); let mut clauses: Vec = Vec::new(); clauses.push(time_clause(&schedule.minute, &schedule.hour, fmt)); let has_day_of_month = !is_all(&schedule.dom); let has_weekday = !is_all(&schedule.dow); // When BOTH day fields are restricted, standard cron runs on EITHER match // (OR semantics), so the two clauses are joined with "or". if has_day_of_month && has_weekday { clauses.push(format!( "{} or {}", dom_text(&schedule.dom), dow_text(&schedule.dow, true) )); } else if has_day_of_month { clauses.push(dom_text(&schedule.dom)); } if let Some(c) = month_clause(&schedule.month) { clauses.push(c); } if has_weekday && !has_day_of_month { clauses.push(dow_text(&schedule.dow, false)); } clauses.join(", ") } ``` -------------------------------- ### cronspeak::describe_with Source: https://docs.rs/cronspeak/latest/src/cronspeak/lib.rs.html Describes a cron expression using custom Options. ```APIDOC ## fn describe_with(expr: &str, options: &Options) -> Result ### Description Converts a cron expression string into a human-readable English string, allowing for custom configuration via the Options struct. ### Parameters - **expr** (&str) - Required - The cron expression string to describe. - **options** (&Options) - Required - Configuration options, such as TimeFormat (H12 or H24). ### Returns - **Result** - Returns the human-readable description on success, or a CronError if the expression is invalid. ``` -------------------------------- ### describe Source: https://docs.rs/cronspeak/latest/cronspeak/all.html Generates a human-readable description of a cron expression. ```APIDOC ## describe(expression: &str) -> Result ### Description Parses a cron expression string and returns a human-readable description of the schedule. ### Parameters - **expression** (&str) - Required - The cron expression string to be parsed. ``` -------------------------------- ### Describe hour window Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Generates a descriptive string for an hour segment, adjusting the end minute based on whether the minute field is fixed. ```rust fn hour_phrase(hour: &[Segment], fmt: TimeFormat, minute_fixed: bool) -> String { let end_min = if minute_fixed { 0 } else { 59 }; match hour { [Segment::Step(n)] => format!("every {n} hours"), [Segment::Value(h)] => { format!( "between {} and {}", clock(*h, 0, fmt), clock(*h, end_min, fmt) ) } [Segment::Range(a, b)] => { format!( "between {} and {}", clock(*a, 0, fmt), clock(*b, end_min, fmt) ) } [Segment::RangeStep(a, b, n)] => format!( "every {n} hours between {} and {}", clock(*a, 0, fmt), clock(*b, 0, fmt) ), _ => match all_values(hour) { Some(vals) => { let times: Vec = vals.iter().map(|&h| clock(h, 0, fmt)).collect(); format!("at {}", human_list(×)) } None => String::from("during the listed hours"), }, } } ``` -------------------------------- ### describe_with function signature Source: https://docs.rs/cronspeak/latest/cronspeak/fn.describe_with.html The function signature for describe_with, which accepts a cron expression string and an Options struct. ```rust pub fn describe_with(expr: &str, options: &Options) -> Result ``` -------------------------------- ### Describe day of month Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Formats the day-of-month cron segment into a human-readable string. ```rust fn dom_text(dom: &[Segment]) -> String { match dom { [Segment::Value(d)] => format!("on day {d} of the month"), [Segment::Range(a, b)] => format!("on days {a} through {b} of the month"), [Segment::Step(n)] => format!("every {n} days of the month"), [Segment::RangeStep(a, b, n)] => { format!( "{} from {a} through {b} of the month", every_unit(*n, "day") ) } _ => match all_values(dom) { Some(vals) => format!("on days {} of the month", number_list(&vals)), None => String::from("on the listed days of the month"), }, } } ``` -------------------------------- ### Minute Phrase Generation Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Constructs the minute-specific portion of the human-readable string. ```rust fn minute_phrase(minute: &[Segment]) -> String { if is_all(minute) { return String::from("every minute"); } if let Some(n) = step(minute) { return format!("every {n} minutes"); } match minute { [Segment::Value(m)] => format!("at minute {m}"), [Segment::Range(a, b)] => format!("every minute from {a} through {b}"), [Segment::RangeStep(a, b, n)] => { format!("{} from {a} through {b}", every_unit(*n, "minute")) } _ => match all_values(minute) { Some(vals) => format!("at minutes {}", number_list(&vals)), ``` -------------------------------- ### Describe a cron expression Source: https://docs.rs/cronspeak/latest/cronspeak/fn.describe.html Converts a standard cron expression into a human-readable string using default options. ```rust assert_eq!(cronspeak::describe("0 0 1 * *").unwrap(), "At 12:00 AM, on day 1 of the month"); ``` -------------------------------- ### Grammar Helpers Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Functions for pluralizing units and formatting time-based phrases. ```rust /// Pluralize a unit noun by count: `(1, "minute") -> "minute"`, else `"minutes"`. fn plural(n: u32, unit: &str) -> String { if n == 1 { String::from(unit) } else { format!("{unit}s") } } /// `"every minute"` for `n == 1`, otherwise `"every n minutes"`. fn every_unit(n: u32, unit: &str) -> String { if n == 1 { format!("every {unit}") } else { format!("every {n} {unit}s") } } ``` -------------------------------- ### cronspeak::describe Source: https://docs.rs/cronspeak/latest/src/cronspeak/lib.rs.html Describes a cron expression in human-readable English using default options. ```APIDOC ## fn describe(expr: &str) -> Result ### Description Converts a cron expression string into a human-readable English string using default formatting options. ### Parameters - **expr** (&str) - Required - The cron expression string to describe. ### Returns - **Result** - Returns the human-readable description on success, or a CronError if the expression is invalid. ``` -------------------------------- ### Describe month clause Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Returns an optional string describing the month segment, returning None if the segment represents all months. ```rust fn month_clause(month: &[Segment]) -> Option { if is_all(month) { return None; } Some(match month { [Segment::Value(m)] => format!("only in {}", month_name(*m)), [Segment::Range(a, b)] => format!("{} through {}", month_name(*a), month_name(*b)), [Segment::Step(n)] => format!("every {n} months"), [Segment::RangeStep(a, b, n)] => format!( "{} from {} through {}", every_unit(*n, "month"), month_name(*a), month_name(*b) ), _ => match all_values(month) { Some(vals) => { let names: Vec = vals.iter().map(|&m| month_name(m)).collect(); format!("only in {}", human_list(&names)) } None => String::from("in the listed months"), }, }) } ``` -------------------------------- ### Field Shape Helpers Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Utility functions to inspect the structure of cron segments. ```rust fn is_all(field: &[Segment]) -> bool { matches!(field, [Segment::All]) } fn single(field: &[Segment]) -> Option { match field { [Segment::Value(v)] => Some(*v), _ => None, } } fn step(field: &[Segment]) -> Option { match field { [Segment::Step(n)] => Some(*n), _ => None, } } /// If every segment is a plain `Value`, collect them. fn all_values(field: &[Segment]) -> Option> { field .iter() .map(|s| match s { Segment::Value(v) => Some(*v), _ => None, }) .collect() } ``` -------------------------------- ### Format number list Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Converts a slice of integers into a human-readable list string. ```rust fn number_list(vals: &[u32]) -> String { let items: Vec = vals.iter().map(|v| format!("{v}")).collect(); human_list(&items) } ``` -------------------------------- ### Mapping Cron Segments to Numeric Values Source: https://docs.rs/cronspeak/latest/src/cronspeak/segment.rs.html Matches month or day-of-week strings against a lookup table to return the associated integer. ```rust ("apr", "april", 4), ("may", "may", 5), ("jun", "june", 6), ("jul", "july", 7), ("aug", "august", 8), ("sep", "september", 9), ("oct", "october", 10), ("nov", "november", 11), ("dec", "december", 12), ], Kind::Dow => &[ ("sun", "sunday", 0), ("mon", "monday", 1), ("tue", "tuesday", 2), ("wed", "wednesday", 3), ("thu", "thursday", 4), ("fri", "friday", 5), ("sat", "saturday", 6), ], _ => return None, }; table .iter() .find(|(abbr, full, _)| lower == *abbr || lower == *full) .map(|(_, _, n)| *n) } ``` -------------------------------- ### Describe day of week Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Formats the day-of-week segment, with an optional mode to adjust phrasing when combined with day-of-month restrictions. ```rust fn dow_text(dow: &[Segment], or_mode: bool) -> String { let only = if or_mode { "on" } else { "only on" }; match dow { [Segment::Value(w)] => format!("{only} {}", day_name(*w)), [Segment::Range(a, b)] => { let body = format!("{} through {}", day_name(*a), day_name(*b)); if or_mode { format!("on {body}") } else { body } } [Segment::Step(n)] => format!("every {n} days of the week"), [Segment::RangeStep(a, b, n)] => format!( "{} from {} through {}", every_unit(*n, "day"), day_name(*a), day_name(*b) ), _ => match all_values(dow) { Some(vals) => { let names: Vec = vals.iter().map(|&w| day_name(w)).collect(); format!("{only} {}", human_list(&names)) } None => String::from("on the listed days of the week"), }, } } ``` -------------------------------- ### Time-of-Day Clause Generation Source: https://docs.rs/cronspeak/latest/src/cronspeak/describe.rs.html Logic for generating the time component of the human-readable string. ```rust fn time_clause(minute: &[Segment], hour: &[Segment], fmt: TimeFormat) -> String { if let (Some(m), Some(h)) = (single(minute), single(hour)) { return format!("At {}", clock(h, m, fmt)); } // A fixed minute across an explicit list of hours: "At 12:00 AM and 12:00 PM". if let (Some(m), Some(hours)) = (single(minute), all_values(hour)) { let times: Vec = hours.iter().map(|&h| clock(h, m, fmt)).collect(); return format!("At {}", human_list(×)); } if is_all(minute) && is_all(hour) { return String::from("Every minute"); } let minute_fixed = single(minute).is_some(); if is_all(hour) { if let Some(n) = step(minute) { return format!("Every {n} minutes"); } if let Some(m) = single(minute) { return format!("At {m} {} past the hour", plural(m, "minute")); } if let Some(vals) = all_values(minute) { return format!("At minutes {} past the hour", number_list(&vals)); } return capitalize(&minute_phrase(minute)); } if is_all(minute) { return format!("Every minute, {}", hour_phrase(hour, fmt, false)); } format!( "{}, {}", capitalize(&minute_phrase(minute)), hour_phrase(hour, fmt, minute_fixed) ) } ``` -------------------------------- ### Parse Cron Fields Source: https://docs.rs/cronspeak/latest/src/cronspeak/segment.rs.html Functions to parse a raw string into a collection of segments or individual parts. ```rust pub(crate) fn parse_field(raw: &str, kind: Kind) -> Result, CronError> { if raw.is_empty() { return Err(CronError::InvalidField(String::from(raw))); } raw.split(',').map(|part| parse_part(part, kind)).collect() } fn parse_part(part: &str, kind: Kind) -> Result { let invalid = || CronError::InvalidField(String::from(part)); if let Some((base, step_str)) = part.split_once('/') { let step: u32 = step_str .parse() .map_err(|_| CronError::InvalidStep(String::from(part)))?; if step == 0 { return Err(CronError::InvalidStep(String::from(part))); } if base == "*" { return Ok(if step == 1 { Segment::All } else { Segment::Step(step) }); } if let Some((a, b)) = base.split_once('-') { let a = resolve(a, kind).map_err(|_| invalid())?; let b = resolve(b, kind).map_err(|_| invalid())?; if a > b { return Err(invalid()); } return Ok(Segment::RangeStep(a, b, step)); } let a = resolve(base, kind).map_err(|_| invalid())?; return Ok(Segment::RangeStep(a, kind.display_max(), step)); } if part == "*" { return Ok(Segment::All); } if let Some((a, b)) = part.split_once('-') { let a = resolve(a, kind).map_err(|_| invalid())?; let b = resolve(b, kind).map_err(|_| invalid())?; if a > b { return Err(invalid()); } return Ok(Segment::Range(a, b)); } Ok(Segment::Value(resolve(part, kind).map_err(|_| invalid())?)) } ``` -------------------------------- ### Resolve Tokens and Map Names Source: https://docs.rs/cronspeak/latest/src/cronspeak/segment.rs.html Utilities to resolve numeric or named tokens into validated values. ```rust fn resolve(tok: &str, kind: Kind) -> Result { if tok.is_empty() { return Err(CronError::InvalidField(String::from(tok))); } let raw = match tok.parse::() { Ok(n) => n, Err(_) => { name_to_num(tok, kind).ok_or_else(|| CronError::InvalidField(String::from(tok)))? } }; let (min, max) = kind.bounds(); if raw < min || raw > max { return Err(CronError::InvalidField(String::from(tok))); } Ok(raw) } fn name_to_num(tok: &str, kind: Kind) -> Option { let lower = tok.to_ascii_lowercase(); let table: &[(&str, &str, u32)] = match kind { Kind::Month => &[ ("jan", "january", 1), ("feb", "february", 2), ("mar", "march", 3), ] ``` -------------------------------- ### Cron Parsing and Schedule Definition Source: https://docs.rs/cronspeak/latest/src/cronspeak/parse.rs.html Defines the CronError enum for validation failures and the Schedule struct for storing parsed fields. The parse function handles input trimming, macro expansion, and field validation. ```rust 1//! Parse a cron expression into a validated [`Schedule`]. 2 3use alloc::string::String; 4use alloc::vec::Vec; 5use core::fmt; 6 7use crate::segment::{parse_field, Kind, Segment}; 8 9/// An error produced while parsing a cron expression. 10#[derive(Debug, Clone, PartialEq, Eq)] 11#[non_exhaustive] 12pub enum CronError { 13 /// The input was empty or only whitespace. 14 Empty, 15 /// The expression did not have exactly five fields. 16 FieldCount { 17 /// Number of fields expected (always 5). 18 expected: usize, 19 /// Number of fields actually found. 20 got: usize, 21 }, 22 /// A step (`*/n`) was zero or not a number. 23 InvalidStep(String), 24 /// A field value, name, or range was invalid or out of range. 25 InvalidField(String), 26} 27 28impl fmt::Display for CronError { 29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 match self { 31 CronError::Empty => f.write_str("empty cron expression"), 32 CronError::FieldCount { expected, got } => { 33 write!(f, "expected {expected} fields, found {got}") 34 } 35 CronError::InvalidStep(s) => write!(f, "invalid step: {s:?}"), 36 CronError::InvalidField(s) => write!(f, "invalid field: {s:?}"), 37 } 38 } 39} 40 41impl core::error::Error for CronError {} 42 43/// A fully parsed, validated cron schedule (five fields). 44pub(crate) struct Schedule { 45 pub minute: Vec, 46 pub hour: Vec, 47 pub dom: Vec, 48 pub month: Vec, 49 pub dow: Vec, 50} 51 52pub(crate) fn parse(expr: &str) -> Result { 53 let trimmed = expr.trim(); 54 if trimmed.is_empty() { 55 return Err(CronError::Empty); 56 } 57 58 let expanded = expand_macro(trimmed).unwrap_or(trimmed); 59 let fields: Vec<&str> = expanded.split_whitespace().collect(); 60 if fields.len() != 5 { 61 return Err(CronError::FieldCount { 62 expected: 5, 63 got: fields.len(), 64 }); 65 } 66 67 Ok(Schedule { 68 minute: parse_field(fields[0], Kind::Minute)?, 69 hour: parse_field(fields[1], Kind::Hour)?, 70 dom: parse_field(fields[2], Kind::Dom)?, 71 month: parse_field(fields[3], Kind::Month)?, 72 dow: parse_field(fields[4], Kind::Dow)?, 73 }) 74} 75 76/// Expand a `@`-macro to its equivalent 5-field expression. 77fn expand_macro(s: &str) -> Option<&'static str> { 78 match s { 79 "@yearly" | "@annually" => Some("0 0 1 1 *"), 80 "@monthly" => Some("0 0 1 * *"), 81 "@weekly" => Some("0 0 * * 0"), 82 "@daily" | "@midnight" => Some("0 0 * * *"), 83 "@hourly" => Some("0 * * * *"), 84 _ => None, 85 } 86} ``` -------------------------------- ### Define Cron Field Kinds Source: https://docs.rs/cronspeak/latest/src/cronspeak/segment.rs.html The Kind enum identifies the specific cron field being parsed, which determines valid ranges and name resolution. ```rust #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Kind { Minute, Hour, Dom, Month, Dow, } ``` -------------------------------- ### Define Cron Field Segments Source: https://docs.rs/cronspeak/latest/src/cronspeak/segment.rs.html The Segment enum represents different types of cron field components, such as all values, steps, single values, or ranges. ```rust #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Segment { /// `*` — every value in the field. All, /// `*/n` — every `n`th value across the whole field. Step(u32), /// A single value `a`. Value(u32), /// An inclusive range `a-b`. Range(u32, u32), /// A stepped range `a-b/n` (or `a/n`, where `b` is the field maximum). RangeStep(u32, u32, u32), } ``` -------------------------------- ### TimeFormat Enum Definition Source: https://docs.rs/cronspeak/latest/cronspeak/enum.TimeFormat.html The definition of the TimeFormat enum, which is marked as non-exhaustive to allow for future variants. ```rust #[non_exhaustive] pub enum TimeFormat { H12, H24, } ``` -------------------------------- ### CronError Enum Definition Source: https://docs.rs/cronspeak/latest/cronspeak/enum.CronError.html The definition of the CronError enum, which is marked as non-exhaustive to allow for future variants. ```rust #[non_exhaustive] pub enum CronError { Empty, FieldCount { expected: usize, got: usize, }, InvalidStep(String), InvalidField(String), } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.