// // RulePolicy.swift // Severed // import Foundation /// Gates every mutation of a rule. This is where Hard Mode is enforced: /// while a hard-mode rule is actively blocking, nothing about it can be /// weakened until the window ends. enum RulePolicy { /// True while the rule is actively blocking with Hard Mode on. static func isHardLocked( _ rule: BlockingRule, at now: Date = .now, calendar: Calendar = .current ) -> Bool { rule.hardMode && rule.status(at: now, calendar: calendar).isActive } static func canEdit( _ rule: BlockingRule, at now: Date = .now, calendar: Calendar = .current ) -> Bool { !isHardLocked(rule, at: now, calendar: calendar) } static func canDisable( _ rule: BlockingRule, at now: Date = .now, calendar: Calendar = .current ) -> Bool { !isHardLocked(rule, at: now, calendar: calendar) } static func canDelete( _ rule: BlockingRule, at now: Date = .now, calendar: Calendar = .current ) -> Bool { !isHardLocked(rule, at: now, calendar: calendar) } /// Whether the user may lift the current block early ("Unblock"). /// Requires an active window and Hard Mode off. static func canUnblock( _ rule: BlockingRule, at now: Date = .now, calendar: Calendar = .current ) -> Bool { rule.status(at: now, calendar: calendar).isActive && !rule.hardMode } /// Hard Mode can always be turned on, but never off while the rule is /// actively blocking — that is the whole point of a hard block. static func canTurnOffHardMode( _ rule: BlockingRule, at now: Date = .now, calendar: Calendar = .current ) -> Bool { !isHardLocked(rule, at: now, calendar: calendar) } /// Pauses the rule's current window. Returns false (and changes nothing) /// when unblocking is not allowed. @discardableResult static func unblock( _ rule: BlockingRule, at now: Date = .now, calendar: Calendar = .current ) -> Bool { guard canUnblock(rule, at: now, calendar: calendar), let window = rule.schedule.activeWindow(containing: now, calendar: calendar) else { return false } rule.pausedUntil = window.end return true } }