moonbit-slotplan

    Deterministic interval and multi-resource booking algorithms for MoonBit

    scheduling
    booking
    availability
    interval
    resource-allocation
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    4 days ago
    Downloads
    6

    Dependencies

    #MoonBit SlotPlan

    MoonBit SlotPlan is a small, deterministic scheduling core for rooms, equipment, people, or finite-capacity workers. It is deliberately not a calendar parser or UI library: an application supplies normalized availability on an integer timeline and SlotPlan finds a safe booking.

    #Why it exists

    Date/time libraries, cron parsers, and iCalendar readers answer different problems. Applications still need a portable algorithm that can answer:

    • Which room with capacity for six is free first?
    • When are a room and a required projector free at the same time?
    • Does setup/cleanup time make an otherwise visible event unsafe?
    • Can an application simulate a booking before it changes its own database?

    SlotPlan models time as integer Tick values, usually UTC minutes. This keeps time-zone conversion, persistence, authentication, recurrence parsing, and calendar imports at the application boundary instead of hard-wiring any one product's rules into the allocator.

    #Included capabilities

    • Half-open intervals ([start, end)) with validation, overlap checks and clipping.
    • Normalized interval sets with union, intersection, subtraction and first-fit lookup.
    • Resource calendars with capacity, availability and existing blocked ranges.
    • Booking requests with a visible event duration plus setup/cleanup buffers.
    • Earliest-slot selection across alternatives, or simultaneous reservation of a named group of resources.
    • Immutable planning results: reserve_earliest returns a successor planner, so callers can preview changes safely.
    • Human-readable explanations for unavailable requests.
    • Batch scheduling with deterministic priority policies and atomic simulation.
    • Cancellation, rescheduling and an append-only in-memory booking ledger.
    • Alternative all-of resource groups for interchangeable equipment.
    • Weekly availability templates, temporary availability exceptions and reports.
    • Timeline lane layout, overlap detection and concurrency segments for UIs.

    #Run locally

    MoonBit toolchain is the only prerequisite.

    moon test moon check --deny-warn moon run cmd/main

    The runnable example schedules a 45-minute design review requiring a room and a projector. It prints both the user-visible event interval and its longer reserved envelope.

    #Browser demonstration

    cmd/web is a small Rabbita front end compiled to JavaScript. It uses the same Planner::suggest and Planner::reserve_earliest calls as library consumers, so changing a placement strategy or confirming a reservation recomputes the real scheduling state.

    moon build cmd/web --target js --release

    Open web/index.html after the build. The demonstration is intentionally local-only: it has no account, database, or network service. The user can adjust the request duration and projector requirement before calculating or confirming a suggested booking.

    #Minimal library example

    ///|
    let window = Interval::new(540, 720).unwrap()

    ///|
    let room = ResourceCalendar::new(
    "atlas-room",
    10,
    IntervalSet::from_ranges([window]),
    ).unwrap()

    ///|
    let planner = Planner::new([room]).unwrap()

    ///|
    let request = BookingRequest::new(
    "standup",
    30,
    Interval::new(600, 700).unwrap(),
    capacity_needed=6,
    ).unwrap()

    ///|
    let allocation = planner.find_earliest(request).unwrap()

    allocation.event() is the meeting itself. allocation.reserved() includes the configured before/after buffers, so a later booking cannot intrude into turnover time.

    #Scope and non-goals

    The package is intentionally a pure scheduling algorithm library. Version 0.1 does not parse ICS, RRULE, cron, time zones, holiday datasets, accounts or databases. Those are integration concerns, and keeping them out means the same library can serve a classroom booking tool, a laboratory instrument queue, a CI-worker coordinator, or a small appointment service.

    #License

    Apache-2.0. See LICENSE.

    Tick

    type Tick = Int

    caller chooses the unit (usually UTC minutes or integer ticks).

    Allocation

    pub struct Allocation {
    request_id : String
    event : Interval
    reserved : Interval
    resource_ids : Array[String]
    } derive(
    Debug
    )

    cleanup/setup buffers while event is what an end user sees.

    Allocation::event

    fn Allocation::event(self : Allocation) -> Interval

    Allocation::render

    fn Allocation::render(self : Allocation) -> String

    Allocation::request_id

    fn Allocation::request_id(self : Allocation) -> String

    Allocation::reserved

    fn Allocation::reserved(self : Allocation) -> Interval

    Allocation::resource_ids

    fn Allocation::resource_ids(self : Allocation) -> Array[String]

    AvailabilityOverlay

    pub struct AvailabilityOverlay {
    baseline : IntervalSet
    openings : IntervalSet
    closures : IntervalSet
    } derive(
    Debug
    )

    are recorded as small interval sets.

    AvailabilityOverlay::baseline

    AvailabilityOverlay::close

    protecting a maintenance window even if an overtime opening overlaps it.

    AvailabilityOverlay::close_many

    AvailabilityOverlay::closures

    AvailabilityOverlay::explain

    fn AvailabilityOverlay::explain(self : AvailabilityOverlay, tick : Int) -> String

    AvailabilityOverlay::is_open

    fn AvailabilityOverlay::is_open(self : AvailabilityOverlay, tick : Int) -> Bool

    AvailabilityOverlay::new

    AvailabilityOverlay::open

    compare tentative availability policies before choosing one.

    AvailabilityOverlay::open_many

    AvailabilityOverlay::openings

    AvailabilityOverlay::resolve

    compact and deterministic: (baseline ∪ openings) − closures.

    AvailabilityOverlay::resolve_within

    fn AvailabilityOverlay::resolve_within(self : AvailabilityOverlay, horizon : Interval) -> IntervalSet

    BatchError

    pub enum BatchError {
    DuplicateBatchKey(String)
    ItemFailure(key~ : String, error~ : PlanError)
    } derive(Eq,
    Debug
    )

    BatchError::render

    fn BatchError::render(self : BatchError) -> String

    BatchItem

    pub struct BatchItem {
    key : String
    request : BookingRequest
    priority : Int
    } derive(
    Debug
    )

    the default input-order behavior.

    BatchItem::key

    fn BatchItem::key(self : BatchItem) -> String

    BatchItem::new

    fn BatchItem::new(key : String, request : BookingRequest, priority? : Int) -> Result[BatchItem, BatchItemError]

    BatchItem::priority

    fn BatchItem::priority(self : BatchItem) -> Int

    BatchItem::request

    fn BatchItem::request(self : BatchItem) -> BookingRequest

    BatchItemError

    pub enum BatchItemError {
    EmptyBatchKey
    } derive(Eq,
    Debug
    )

    BatchOutcome

    pub struct BatchOutcome {
    key : String
    request_id : String
    status : BatchStatus
    } derive(
    Debug
    )

    BatchOutcome::key

    fn BatchOutcome::key(self : BatchOutcome) -> String

    BatchOutcome::request_id

    fn BatchOutcome::request_id(self : BatchOutcome) -> String

    BatchOutcome::status

    fn BatchOutcome::status(self : BatchOutcome) -> BatchStatus

    BatchPlan

    pub struct BatchPlan {
    outcomes : Array[BatchOutcome]
    planner : Planner
    policy : BatchPolicy
    } derive(
    Debug
    )

    BatchPlan::outcomes

    fn BatchPlan::outcomes(self : BatchPlan) -> Array[BatchOutcome]

    BatchPlan::planner

    fn BatchPlan::planner(self : BatchPlan) -> Planner

    BatchPlan::policy

    fn BatchPlan::policy(self : BatchPlan) -> BatchPolicy

    BatchPlan::rejected_count

    fn BatchPlan::rejected_count(self : BatchPlan) -> Int

    BatchPlan::scheduled_count

    fn BatchPlan::scheduled_count(self : BatchPlan) -> Int

    BatchPlan::summary

    fn BatchPlan::summary(self : BatchPlan) -> String

    BatchPolicy

    pub enum BatchPolicy {
    InputOrder
    PriorityThenEarliest
    ShortestJobFirst
    } derive(Eq,
    Debug
    )

    are deterministic so a caller can reproduce and explain a batch result.

    BatchPolicy::render

    fn BatchPolicy::render(self : BatchPolicy) -> String

    BatchStatus

    pub enum BatchStatus {
    Scheduled(Allocation)
    Rejected(PlanError)
    } derive(
    Debug
    )

    which is more useful to a booking service than an unexplained omission.

    BatchStatus::is_scheduled

    fn BatchStatus::is_scheduled(self : BatchStatus) -> Bool

    BatchStatus::render

    fn BatchStatus::render(self : BatchStatus) -> String

    BookingLedger

    pub struct BookingLedger {
    planner : Planner
    records : Array[BookingRecord]
    events : Array[LedgerEvent]
    } derive(
    Debug
    )

    persist without duplicating cancellation and rescheduling behavior.

    BookingLedger::active_records

    fn BookingLedger::active_records(self : BookingLedger) -> Array[BookingRecord]

    BookingLedger::book

    fn BookingLedger::book(self : BookingLedger, booking_id : String, request : BookingRequest) -> Result[LedgerChange, LedgerError]

    Create and retain the earliest allocation for one application-level ID.

    BookingLedger::cancel

    fn BookingLedger::cancel(self : BookingLedger, booking_id : String) -> Result[LedgerChange, LedgerError]

    Cancel an active record and retain its original allocation in history.

    BookingLedger::events

    BookingLedger::new

    fn BookingLedger::new(planner : Planner) -> BookingLedger

    BookingLedger::planner

    fn BookingLedger::planner(self : BookingLedger) -> Planner

    BookingLedger::records

    BookingLedger::reschedule

    fn BookingLedger::reschedule(self : BookingLedger, booking_id : String, request : BookingRequest) -> Result[LedgerChange, LedgerError]

    Planner::reschedule is immutable.

    BookingRecord

    pub struct BookingRecord {
    booking_id : String
    allocation : Allocation
    state : BookingState
    } derive(
    Debug
    )

    A named allocation held in the ledger.

    BookingRecord::allocation

    fn BookingRecord::allocation(self : BookingRecord) -> Allocation

    BookingRecord::booking_id

    fn BookingRecord::booking_id(self : BookingRecord) -> String

    BookingRecord::is_active

    fn BookingRecord::is_active(self : BookingRecord) -> Bool

    BookingRecord::render

    fn BookingRecord::render(self : BookingRecord) -> String

    BookingRecord::state

    BookingRequest

    pub struct BookingRequest {
    id : String
    duration : Int
    horizon : Interval
    capacity_needed : Int
    buffer_before : Int
    buffer_after : Int
    required_resources : Array[String]
    } derive(
    Debug
    )

    A request to reserve one or more resources at the same time.

    BookingRequest::capacity_needed

    fn BookingRequest::capacity_needed(self : BookingRequest) -> Int

    BookingRequest::duration

    fn BookingRequest::duration(self : BookingRequest) -> Int

    BookingRequest::event_from_reserved

    fn BookingRequest::event_from_reserved(self : BookingRequest, reserved : Interval) -> Interval

    Convert a reserved envelope back to the customer-visible appointment.

    BookingRequest::horizon

    fn BookingRequest::horizon(self : BookingRequest) -> Interval

    BookingRequest::id

    fn BookingRequest::id(self : BookingRequest) -> String

    BookingRequest::new

    fn BookingRequest::new(id : String, duration : Int, horizon : Interval, capacity_needed? : Int, buffer_before? : Int, buffer_after? : Int, required_resources? : Array[String]) -> Result[BookingRequest, RequestError]

    BookingRequest::render

    fn BookingRequest::render(self : BookingRequest) -> String

    BookingRequest::required_resources

    fn BookingRequest::required_resources(self : BookingRequest) -> Array[String]

    BookingRequest::reserved_duration

    fn BookingRequest::reserved_duration(self : BookingRequest) -> Int

    BookingState

    pub enum BookingState {
    Active
    Cancelled
    } derive(Eq,
    Debug
    )

    cancellation so an embedding application can preserve an audit trail.

    BookingState::render

    fn BookingState::render(self : BookingState) -> String

    ConcurrencySegment

    pub struct ConcurrencySegment {
    interval : Interval
    count : Int
    } derive(
    Debug
    )

    One interval with a constant number of active events.

    ConcurrencySegment::count

    fn ConcurrencySegment::count(self : ConcurrencySegment) -> Int

    ConcurrencySegment::interval

    ConflictPair

    pub struct ConflictPair {
    first : TimelineEvent
    second : TimelineEvent
    overlap : Interval
    } derive(
    Debug
    )

    conflict, which matches booking semantics elsewhere in the library.

    ConflictPair::first

    ConflictPair::overlap

    fn ConflictPair::overlap(self : ConflictPair) -> Interval

    ConflictPair::second

    FlexibleRequest

    pub struct FlexibleRequest {
    request : BookingRequest
    alternatives : Array[Array[String]]
    } derive(
    Debug
    )

    interchangeable rooms.

    FlexibleRequest::alternatives

    fn FlexibleRequest::alternatives(self : FlexibleRequest) -> Array[Array[String]]

    FlexibleRequest::new

    fn FlexibleRequest::new(request : BookingRequest, alternatives : Array[Array[String]]) -> Result[FlexibleRequest, FlexibleRequestError]

    FlexibleRequest::render

    fn FlexibleRequest::render(self : FlexibleRequest) -> String

    FlexibleRequest::request

    FlexibleRequestError

    pub enum FlexibleRequestError {
    NoAlternatives
    EmptyAlternative(Int)
    EmptyResourceId(Int, Int)
    DuplicateResourceInAlternative(Int, String)
    } derive(Eq,
    Debug
    )

    Interval

    pub struct Interval {
    start : Int
    end : Int
    } derive(Compare, Eq,
    Debug
    )

    A half-open interval [start, end). Half-open ranges make adjacent reservations compatible: [10, 20) and [20, 30) do not overlap.

    Interval::compare_start

    fn Interval::compare_start(a : Interval, b : Interval) -> Int

    Interval::contains

    fn Interval::contains(self : Interval, tick : Int) -> Bool

    Interval::contains_interval

    fn Interval::contains_interval(self : Interval, other : Interval) -> Bool

    Interval::duration

    fn Interval::duration(self : Interval) -> Int

    Interval::end

    fn Interval::end(self : Interval) -> Int

    Interval::intersect

    fn Interval::intersect(self : Interval, other : Interval) -> Interval?

    Interval::merge

    fn Interval::merge(self : Interval, other : Interval) -> Interval?

    Merge overlapping or immediately adjacent ranges. Disjoint ranges return None so callers cannot accidentally hide an unavailable gap.

    Interval::new

    fn Interval::new(start : Int, end : Int) -> Result[Interval, IntervalError]

    Interval::overlaps

    fn Interval::overlaps(self : Interval, other : Interval) -> Bool

    Interval::render

    fn Interval::render(self : Interval) -> String

    Interval::shift

    fn Interval::shift(self : Interval, delta : Int) -> Interval

    Shift an interval on the abstract timeline. Useful for before/after booking buffers; callers are responsible for choosing a sensible origin.

    Interval::start

    fn Interval::start(self : Interval) -> Int

    Interval::touches

    fn Interval::touches(self : Interval, other : Interval) -> Bool

    Interval::with_duration

    fn Interval::with_duration(start : Int, duration : Int) -> Result[Interval, IntervalError]

    IntervalError

    pub enum IntervalError {
    EmptyOrReversedRange(start~ : Int, end~ : Int)
    InvalidWindow(start~ : Int, end~ : Int)
    } derive(Eq,
    Debug
    )

    IntervalSet

    pub struct IntervalSet {
    ranges : Array[Interval]
    } derive(
    Debug
    )

    intentionally private so every consumer gets the same adjacency rules.

    IntervalSet::add

    fn IntervalSet::add(self : IntervalSet, range : Interval) -> IntervalSet

    IntervalSet::contains

    fn IntervalSet::contains(self : IntervalSet, tick : Int) -> Bool

    IntervalSet::empty

    fn IntervalSet::empty() -> IntervalSet

    IntervalSet::first_fit

    fn IntervalSet::first_fit(self : IntervalSet, duration : Int, not_before : Int) -> Interval?

    IntervalSet::from_ranges

    fn IntervalSet::from_ranges(ranges : Array[Interval]) -> IntervalSet

    IntervalSet::intersect

    fn IntervalSet::intersect(self : IntervalSet, other : IntervalSet) -> IntervalSet

    IntervalSet::is_empty

    fn IntervalSet::is_empty(self : IntervalSet) -> Bool

    IntervalSet::length

    fn IntervalSet::length(self : IntervalSet) -> Int

    IntervalSet::longest_duration

    fn IntervalSet::longest_duration(self : IntervalSet) -> Int

    IntervalSet::ranges

    fn IntervalSet::ranges(self : IntervalSet) -> Array[Interval]

    IntervalSet::render

    fn IntervalSet::render(self : IntervalSet) -> String

    IntervalSet::subtract

    fn IntervalSet::subtract(self : IntervalSet, blocked : IntervalSet) -> IntervalSet

    Return the parts of this set not covered by blocked.

    IntervalSet::total_duration

    fn IntervalSet::total_duration(self : IntervalSet) -> Int

    overlaps, this is also the exact covered duration rather than an estimate.

    IntervalSet::union

    fn IntervalSet::union(self : IntervalSet, other : IntervalSet) -> IntervalSet

    IntervalSet::within

    fn IntervalSet::within(self : IntervalSet, window : Interval) -> IntervalSet

    Clip every range to window; this is useful before presenting a search result to a caller that requested a finite horizon.

    LedgerChange

    pub struct LedgerChange {
    ledger : BookingLedger
    record : BookingRecord
    } derive(
    Debug
    )

    LedgerChange::ledger

    LedgerChange::record

    LedgerError

    pub enum LedgerError {
    EmptyBookingId
    DuplicateBookingId(String)
    UnknownBookingId(String)
    BookingNotActive(String)
    SchedulingFailed(PlanError)
    } derive(Eq,
    Debug
    )

    LedgerError::render

    fn LedgerError::render(self : LedgerError) -> String

    LedgerEvent

    pub struct LedgerEvent {
    sequence : Int
    booking_id : String
    kind : LedgerEventKind
    } derive(
    Debug
    )

    LedgerEvent::booking_id

    fn LedgerEvent::booking_id(self : LedgerEvent) -> String

    LedgerEvent::kind

    LedgerEvent::sequence

    fn LedgerEvent::sequence(self : LedgerEvent) -> Int

    LedgerEventKind

    pub enum LedgerEventKind {
    Created(Allocation)
    Cancelled(Allocation)
    Rescheduled(previous~ : Allocation, replacement~ : Allocation)
    } derive(
    Debug
    )

    UI, CLI or persistence adapter to map into its own storage format.

    OverlayResourceError

    pub enum OverlayResourceError {
    EmptyResolvedAvailability
    ExistingReservationOutsideAvailability(Interval)
    } derive(Eq,
    Debug
    )

    PlacementStrategy

    pub enum PlacementStrategy {
    Earliest
    Latest
    MinimizeFragmentation
    } derive(Eq,
    Debug
    )

    Strategy for selecting a start within every feasible free interval.

    PlacementStrategy::render

    fn PlacementStrategy::render(self : PlacementStrategy) -> String

    PlanError

    pub enum PlanError {
    DuplicateResourceId(String)
    UnknownResource(String)
    InsufficientCapacity(Int)
    NoFeasibleSlot(String)
    CannotReserve(String)
    CannotRelease(String)
    } derive(Eq,
    Debug
    )

    Failures that are meaningful to a booking UI or command-line adapter.

    PlanError::render

    fn PlanError::render(self : PlanError) -> String

    Planner

    pub struct Planner {
    resources : Array[ResourceCalendar]
    } derive(
    Debug
    )

    zone conversion deliberately stay outside this portable core library.

    Planner::cancel

    fn Planner::cancel(self : Planner, allocation : Allocation) -> Result[Planner, PlanError]

    successor is built, preventing a partial release of a multi-resource slot.

    Planner::explain_unavailability

    fn Planner::explain_unavailability(self : Planner, request : BookingRequest) -> String

    Planner::find_earliest

    fn Planner::find_earliest(self : Planner, request : BookingRequest) -> Result[Allocation, PlanError]

    Find the earliest feasible choice. With no required resource, each resource that meets capacity_needed is considered independently. With required resources, all listed resources must be free at the same time.

    Planner::find_flexible_earliest

    fn Planner::find_flexible_earliest(self : Planner, flexible : FlexibleRequest) -> Result[Allocation, PlanError]

    already mandatory in the underlying request are included in every group.

    Planner::new

    fn Planner::new(resources : Array[ResourceCalendar]) -> Result[Planner, PlanError]

    Planner::report

    fn Planner::report(self : Planner, horizon : Interval) -> PlannerReport

    Planner::reschedule

    fn Planner::reschedule(self : Planner, allocation : Allocation, replacement : BookingRequest) -> Result[RescheduleResult, PlanError]

    replacement attempt does not mutate the caller's original planner.

    Planner::reserve_earliest

    fn Planner::reserve_earliest(self : Planner, request : BookingRequest) -> Result[Reservation, PlanError]

    Reserve the earliest matching slot and return a successor planner. Every target calendar is validated before the successor is returned, preventing partial multi-resource bookings.

    Planner::reserve_flexible_earliest

    fn Planner::reserve_flexible_earliest(self : Planner, flexible : FlexibleRequest) -> Result[Reservation, PlanError]

    successor planner.

    Planner::resources

    fn Planner::resources(self : Planner) -> Array[ResourceCalendar]

    Planner::schedule_batch

    fn Planner::schedule_batch(self : Planner, items : Array[BatchItem], policy? : BatchPolicy) -> Result[BatchPlan, BatchError]

    result and optionally retry rejected requests later.

    Planner::schedule_batch_atomically

    fn Planner::schedule_batch_atomically(self : Planner, items : Array[BatchItem], policy? : BatchPolicy) -> Result[BatchPlan, BatchError]

    the failing item and retains the exact original planner state.

    Planner::suggest

    fn Planner::suggest(self : Planner, request : BookingRequest, strategy? : PlacementStrategy, limit? : Int) -> Result[Array[Suggestion], PlanError]

    picking a single booking. limit <= 0 means no suggestions.

    PlannerReport

    pub struct PlannerReport {
    horizon : Interval
    resources : Array[ResourceReport]
    available_minutes : Int
    occupied_minutes : Int
    free_minutes : Int
    } derive(
    Debug
    )

    Aggregated report over every resource in one planner snapshot.

    PlannerReport::available_minutes

    fn PlannerReport::available_minutes(self : PlannerReport) -> Int

    PlannerReport::bottlenecks

    fn PlannerReport::bottlenecks(self : PlannerReport, minimum_percent : Int) -> Array[ResourceReport]

    fleet without embedding a product-specific policy.

    PlannerReport::free_minutes

    fn PlannerReport::free_minutes(self : PlannerReport) -> Int

    PlannerReport::horizon

    fn PlannerReport::horizon(self : PlannerReport) -> Interval

    PlannerReport::most_utilized

    fn PlannerReport::most_utilized(self : PlannerReport) -> ResourceReport?

    PlannerReport::occupied_minutes

    fn PlannerReport::occupied_minutes(self : PlannerReport) -> Int

    PlannerReport::render_bottlenecks

    fn PlannerReport::render_bottlenecks(self : PlannerReport, minimum_percent : Int) -> String

    PlannerReport::render_summary

    fn PlannerReport::render_summary(self : PlannerReport) -> String

    card. Detailed per-resource values remain available through resources.

    PlannerReport::resource_count

    fn PlannerReport::resource_count(self : PlannerReport) -> Int

    PlannerReport::resources

    PlannerReport::utilization_percent

    fn PlannerReport::utilization_percent(self : PlannerReport) -> Int

    RequestError

    pub enum RequestError {
    EmptyRequestId
    NonPositiveDuration(Int)
    NonPositiveCapacity(Int)
    NegativeBuffer(Int)
    EmptyRequiredResource
    } derive(Eq,
    Debug
    )

    RescheduleResult

    pub struct RescheduleResult {
    previous : Allocation
    replacement : Allocation
    planner : Planner
    } derive(
    Debug
    )

    resource state.

    RescheduleResult::planner

    RescheduleResult::previous

    RescheduleResult::replacement

    fn RescheduleResult::replacement(self : RescheduleResult) -> Allocation

    Reservation

    pub struct Reservation {
    allocation : Allocation
    planner : Planner
    } derive(
    Debug
    )

    includes the reservation; the original planner remains unchanged.

    Reservation::allocation

    fn Reservation::allocation(self : Reservation) -> Allocation

    Reservation::planner

    fn Reservation::planner(self : Reservation) -> Planner

    ResolvedAvailability

    pub struct ResolvedAvailability {
    overlay : AvailabilityOverlay
    available : IntervalSet
    } derive(
    Debug
    )

    able to show users why a resource is unavailable.

    ResolvedAvailability::available

    ResolvedAvailability::overlay

    ResourceCalendar

    pub struct ResourceCalendar {
    id : String
    capacity : Int
    available : IntervalSet
    blocked : IntervalSet
    } derive(
    Debug
    )

    availability has been supplied by the embedding application.

    ResourceCalendar::availability

    fn ResourceCalendar::availability(self : ResourceCalendar) -> IntervalSet

    ResourceCalendar::blocked

    ResourceCalendar::capacity

    fn ResourceCalendar::capacity(self : ResourceCalendar) -> Int

    ResourceCalendar::free_within

    fn ResourceCalendar::free_within(self : ResourceCalendar, horizon : Interval) -> IntervalSet

    ResourceCalendar::has_reservation

    fn ResourceCalendar::has_reservation(self : ResourceCalendar, range : Interval) -> Bool

    True only when one normalized blocked range completely covers range.

    ResourceCalendar::id

    fn ResourceCalendar::id(self : ResourceCalendar) -> String

    ResourceCalendar::is_free

    fn ResourceCalendar::is_free(self : ResourceCalendar, range : Interval) -> Bool

    ResourceCalendar::new

    fn ResourceCalendar::new(id : String, capacity : Int, available : IntervalSet) -> Result[ResourceCalendar, ResourceError]

    ResourceCalendar::release

    availability that was never held.

    ResourceCalendar::report

    fn ResourceCalendar::report(self : ResourceCalendar, horizon : Interval) -> ResourceReport

    ResourceCalendar::reserve

    Return a new calendar with the range marked unavailable. The immutable value style lets callers evaluate alternatives without hidden mutation.

    ResourceCalendar::with_availability_overlay

    fn ResourceCalendar::with_availability_overlay(self : ResourceCalendar, overlay : AvailabilityOverlay) -> Result[ResourceCalendar, OverlayResourceError]

    range, forcing the host to cancel or reschedule it deliberately first.

    ResourceCalendar::with_blocked

    fn ResourceCalendar::with_blocked(self : ResourceCalendar, blocked : IntervalSet) -> ResourceCalendar

    ResourceError

    pub enum ResourceError {
    EmptyResourceId
    NonPositiveCapacity(Int)
    EmptyAvailability
    ReservationOutsideAvailability
    ReservationNotFound
    } derive(Eq,
    Debug
    )

    Errors returned while constructing or changing a resource calendar.

    ResourceReport

    pub struct ResourceReport {
    resource_id : String
    capacity : Int
    available_minutes : Int
    occupied_minutes : Int
    free_minutes : Int
    longest_free_minutes : Int
    utilization_percent : Int
    band : UtilizationBand
    } derive(
    Debug
    )

    A resource-level report limited to one caller-supplied horizon.

    ResourceReport::available_minutes

    fn ResourceReport::available_minutes(self : ResourceReport) -> Int

    ResourceReport::band

    ResourceReport::capacity

    fn ResourceReport::capacity(self : ResourceReport) -> Int

    ResourceReport::capacity_minutes

    fn ResourceReport::capacity_minutes(self : ResourceReport) -> Int

    potential person-time as a single-seat resource.

    ResourceReport::free_minutes

    fn ResourceReport::free_minutes(self : ResourceReport) -> Int

    ResourceReport::free_percent

    fn ResourceReport::free_percent(self : ResourceReport) -> Int

    ResourceReport::longest_free_minutes

    fn ResourceReport::longest_free_minutes(self : ResourceReport) -> Int

    ResourceReport::occupied_minutes

    fn ResourceReport::occupied_minutes(self : ResourceReport) -> Int

    ResourceReport::render

    fn ResourceReport::render(self : ResourceReport) -> String

    ResourceReport::resource_id

    fn ResourceReport::resource_id(self : ResourceReport) -> String

    ResourceReport::utilization_percent

    fn ResourceReport::utilization_percent(self : ResourceReport) -> Int

    Suggestion

    pub struct Suggestion {
    allocation : Allocation
    free_window : Interval
    fragment_count : Int
    } derive(
    Debug
    )

    two means it cuts a window in two.

    Suggestion::allocation

    fn Suggestion::allocation(self : Suggestion) -> Allocation

    Suggestion::fragment_count

    fn Suggestion::fragment_count(self : Suggestion) -> Int

    Suggestion::free_window

    fn Suggestion::free_window(self : Suggestion) -> Interval

    TimelineError

    pub enum TimelineError {
    EmptyEventId
    DuplicateEventId(String)
    } derive(Eq,
    Debug
    )

    TimelineEvent

    pub struct TimelineEvent {
    id : String
    interval : Interval
    label : String
    } derive(
    Debug
    )

    well as reservations produced by SlotPlan.

    TimelineEvent::from_allocation

    fn TimelineEvent::from_allocation(allocation : Allocation) -> TimelineEvent

    TimelineEvent::id

    fn TimelineEvent::id(self : TimelineEvent) -> String

    TimelineEvent::interval

    fn TimelineEvent::interval(self : TimelineEvent) -> Interval

    TimelineEvent::label

    fn TimelineEvent::label(self : TimelineEvent) -> String

    TimelineEvent::new

    fn TimelineEvent::new(id : String, interval : Interval, label? : String) -> Result[TimelineEvent, TimelineError]

    TimelineLayout

    pub struct TimelineLayout {
    positions : Array[TimelinePosition]
    lane_count : Int
    conflicts : Array[ConflictPair]
    concurrency : Array[ConcurrencySegment]
    } derive(
    Debug
    )

    Full result of a greedy interval-partitioning layout.

    TimelineLayout::concurrency

    TimelineLayout::conflicts

    TimelineLayout::lane_count

    fn TimelineLayout::lane_count(self : TimelineLayout) -> Int

    TimelineLayout::peak_concurrency

    fn TimelineLayout::peak_concurrency(self : TimelineLayout) -> Int

    TimelineLayout::positions

    TimelinePosition

    pub struct TimelinePosition {
    event : TimelineEvent
    lane_index : Int
    lane_count : Int
    } derive(
    Debug
    )

    lane_index / lane_count as a stable horizontal fraction.

    TimelinePosition::event

    TimelinePosition::lane_count

    fn TimelinePosition::lane_count(self : TimelinePosition) -> Int

    TimelinePosition::lane_index

    fn TimelinePosition::lane_index(self : TimelinePosition) -> Int

    TimelinePosition::left_percent

    fn TimelinePosition::left_percent(self : TimelinePosition) -> Int

    TimelinePosition::width_percent

    fn TimelinePosition::width_percent(self : TimelinePosition) -> Int

    UtilizationBand

    pub enum UtilizationBand {
    Idle
    Light
    Moderate
    Busy
    Saturated
    } derive(Eq,
    Debug
    )

    it is not technically full.

    UtilizationBand::render

    fn UtilizationBand::render(self : UtilizationBand) -> String

    Weekday

    pub enum Weekday {
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
    Sunday
    } derive(Eq,
    Debug
    )

    to the host application.

    Weekday::index

    fn Weekday::index(self : Weekday) -> Int

    Weekday::render

    fn Weekday::render(self : Weekday) -> String

    WeeklyError

    pub enum WeeklyError {
    WindowOutsideDay(Interval)
    HorizonBeforeWeekStart
    NonPositiveWeekCount(Int)
    } derive(Eq,
    Debug
    )

    WeeklyResourceError

    pub enum WeeklyResourceError {
    Weekly(WeeklyError)
    Resource(ResourceError)
    } derive(Eq,
    Debug
    )

    WeeklyTemplate

    pub struct WeeklyTemplate {
    windows : Array[WeeklyWindow]
    } derive(
    Debug
    )

    joined so expansion never produces duplicate availability.

    WeeklyTemplate::availability_within

    fn WeeklyTemplate::availability_within(self : WeeklyTemplate, week_start : Int, horizon : Interval) -> Result[IntervalSet, WeeklyError]

    than it, which avoids silently guessing how to handle negative weeks.

    WeeklyTemplate::expand_weeks

    fn WeeklyTemplate::expand_weeks(self : WeeklyTemplate, week_start : Int, week_count : Int) -> Result[IntervalSet, WeeklyError]

    00:00 tick chosen by the embedding application.

    WeeklyTemplate::is_open

    fn WeeklyTemplate::is_open(self : WeeklyTemplate, day : Weekday, minute : Int) -> Bool

    WeeklyTemplate::new

    WeeklyTemplate::windows

    WeeklyTemplate::windows_for

    fn WeeklyTemplate::windows_for(self : WeeklyTemplate, day : Weekday) -> IntervalSet

    WeeklyWindow

    pub struct WeeklyWindow {
    day : Weekday
    window : Interval
    } derive(
    Debug
    )

    A within-day availability window expressed in minutes after midnight.

    WeeklyWindow::day

    fn WeeklyWindow::day(self : WeeklyWindow) -> Weekday

    WeeklyWindow::new

    fn WeeklyWindow::new(day : Weekday, start_minute : Int, end_minute : Int) -> Result[WeeklyWindow, WeeklyError]

    WeeklyWindow::render

    fn WeeklyWindow::render(self : WeeklyWindow) -> String

    WeeklyWindow::window

    fn WeeklyWindow::window(self : WeeklyWindow) -> Interval

    batch_policies

    fn batch_policies() -> Array[BatchPolicy]

    layout_timeline

    fn layout_timeline(events : Array[TimelineEvent]) -> Result[TimelineLayout, TimelineError]

    the peak concurrent-event count.

    placement_strategies

    fn placement_strategies() -> Array[PlacementStrategy]

    Enumerate supported strategies for a command-line selector or UI menu.

    resolve_availability

    fn resolve_availability(overlay : AvailabilityOverlay) -> ResolvedAvailability

    resource_from_weekly_template

    fn resource_from_weekly_template(id : String, capacity : Int, template : WeeklyTemplate, week_start : Int, horizon : Interval) -> Result[ResourceCalendar, WeeklyResourceError]

    ResourceCalendar::with_blocked.

    resource_with_weekly_overlay

    fn resource_with_weekly_overlay(id : String, capacity : Int, template : WeeklyTemplate, week_start : Int, horizon : Interval, openings : Array[Interval], closures : Array[Interval]) -> Result[ResourceCalendar, WeeklyResourceError]

    Convenience bridge from a weekly template to an exception-aware resource.

    utilization_band

    fn utilization_band(percent : Int) -> UtilizationBand

    weekdays

    fn weekdays() -> Array[Weekday]