A Rust library for managing complex time-based rules and schedules with priority-based overrides.
availability
helps you manage temporal rules and schedules in your application. It's particularly useful for:
- Store opening hours with holiday exceptions
- Service availability windows
- Resource scheduling with priority overrides
- Event calendars with recurring patterns
- Maintenance windows and blackout periods
- Based on ISO 8601 time formatting with milliseconds excluded.
- YYYY-MM-DD HH:MM:SS
- Day starts at 00:00:00
- Day ends at 23:59:59 or 23:59:60 for leap seconds
The library uses a hierarchical priority system for managing time rules:
- Priority 0: Contains a non-modifiable base rule that spans all allowed date-times, serving as the foundation
- Priority 1+: Contains a mix of user specified rules, either relative or absolute. Absolute can not overlap their start-end, but relative rules can if their days don't match.
The animation demonstrates how rules interact:
- Higher priority rules take precedence over lower ones
- Each priority level can contain multiple non-overlapping rules
- Relative rules are converted to absolute rules.
When all rules are added, they are resolved and converted into a sequence of non-overlapping, time sorted frames. This results in a simple, sequential representation of availability.
- Priority-Based Rules: Define base schedules and override them with higher-priority rules
- Flexible Rule Types:
- Absolute rules (specific dates/times)
- Relative rules (recurring on specific weekdays)
- Generic Payloads: Attach custom, serializable, data to each time window
- Conflict Prevention: Built-in validation prevents overlapping rules at the same priority level
- Frame Generation: Automatically resolves overlapping rules from different priorities into non-overlapping frames
use availability::{availability::Availability, rulebuilder::RuleBuilder};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
struct StoreHours {
staff_count: u32,
manager_on_duty: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1) Create an empty Availability struct to store your schedule.
let mut store_availability: Availability<StoreHours> = Availability::new();
// 2) Define rules using RuleBuilder (string-based times in "YYYY-MM-DD HH:MM:SS" format).
// - Priority 1 (lowest): Open Mon-Fri from 09:00 to 17:00 (Jan 1 - Jan 31)
let weekday_rule = RuleBuilder::new()
.start_time_str("2024-01-01 09:00:00") // 2024-01-01 09:00:00
.end_time_str("2024-01-31 17:00:00") // 2024-01-31 17:00:00
.monday()
.tuesday()
.wednesday()
.thursday()
.friday()
.payload(StoreHours {
staff_count: 3,
manager_on_duty: "Regular Manager".to_string(),
})
.build()?; // Validate & finalize
// - Priority 2: Special sale hours override (Jan 1 - Jan 7), open until 20:00
let sale_rule = RuleBuilder::new()
.start_time_str("2024-01-01 09:00:00") // 2024-01-01 09:00:00
.end_time_str("2024-01-07 20:00:00") // 2024-01-07 20:00:00
.weekdays(&["mon", "tue", "wed", "thu", "fri"])
.payload(StoreHours {
staff_count: 5,
manager_on_duty: "Sale Team".to_string(),
})
.build()?;
// - Priority 3: Complete closure for inventory (Jan 15 - Jan 16)
let inventory_rule = RuleBuilder::new()
.start_time_str("2024-01-05 00:00:00") // 2024-01-15 00:00:00
.end_time_str("2024-01-06 00:00:00") // 2024-01-16 00:00:00
.off(true) // Off => store closed
.payload(StoreHours {
staff_count: 2,
manager_on_duty: "Inventory Team".to_string(),
})
.build()?;
// 3) Add the rules by ascending priority.
store_availability.add_rule(weekday_rule, 1)?;
store_availability.add_rule(sale_rule, 2)?;
store_availability.add_rule(inventory_rule, 3)?;
// 4) Convert the layered rules into "frames" that cover only the specified date range.
store_availability.to_frames_in_range_str("2024-01-01 00:00:00", "2024-01-24 23:59:59");
// Optional) Print out the resulting frames:
println!("Store Schedule Overview:");
println!("=======================");
println!("{}", store_availability);
// Optional) Get frame from datetime:
println!("Get frames from datetime:");
println!("=======================");
let frame = store_availability
.get_frame_from_str("2024-01-01 09:00:00")
.unwrap();
println!("Frame at 2024-01-01 09:00:00 is: {}", frame.off);
if let Some(payload) = &frame.payload {
println!("Staff Count: {}", payload.staff_count);
println!("Manager on Duty: {}", payload.manager_on_duty);
}
Ok(())
}
- Rule Builder: Fluent API for creating rules with validation
- Weekday Patterns: Easy specification of recurring weekly schedules
- Custom Payloads: Attach any serializable data to your schedule windows
- Conflict Detection: Automatic validation of rule overlaps
Add this to your Cargo.toml
:
[dependencies]
availability = "0.1.0"
Licensed under the MIT license.