Make diction of docs and api naming more corresponding with official api.

This commit is contained in:
Quentin Jin 2019-04-04 12:34:34 +08:00
parent 4cd608d123
commit d4f25b6031
21 changed files with 344 additions and 388 deletions

View File

@ -24,7 +24,6 @@
OBJ_49 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; }; OBJ_49 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; };
OBJ_50 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Bag.swift */; }; OBJ_50 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Bag.swift */; };
OBJ_51 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* DeinitObserver.swift */; }; OBJ_51 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* DeinitObserver.swift */; };
OBJ_52 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Deprecated.swift */; };
OBJ_53 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Extensions.swift */; }; OBJ_53 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Extensions.swift */; };
OBJ_54 /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* Interval.swift */; }; OBJ_54 /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* Interval.swift */; };
OBJ_55 /* Monthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* Monthday.swift */; }; OBJ_55 /* Monthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* Monthday.swift */; };
@ -70,7 +69,6 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
OBJ_10 /* Bag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = "<group>"; }; OBJ_10 /* Bag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = "<group>"; };
OBJ_11 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; }; OBJ_11 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; };
OBJ_12 /* Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = "<group>"; };
OBJ_13 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; }; OBJ_13 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
OBJ_14 /* Interval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interval.swift; sourceTree = "<group>"; }; OBJ_14 /* Interval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interval.swift; sourceTree = "<group>"; };
OBJ_15 /* Monthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Monthday.swift; sourceTree = "<group>"; }; OBJ_15 /* Monthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Monthday.swift; sourceTree = "<group>"; };
@ -157,7 +155,7 @@
name = Products; name = Products;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
OBJ_5 /* */ = { OBJ_5 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
OBJ_6 /* Package.swift */, OBJ_6 /* Package.swift */,
@ -170,7 +168,6 @@
OBJ_42 /* README.md */, OBJ_42 /* README.md */,
OBJ_43 /* README.zh_cn.md */, OBJ_43 /* README.zh_cn.md */,
); );
name = "";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
OBJ_7 /* Sources */ = { OBJ_7 /* Sources */ = {
@ -187,7 +184,6 @@
OBJ_9 /* Atomic.swift */, OBJ_9 /* Atomic.swift */,
OBJ_10 /* Bag.swift */, OBJ_10 /* Bag.swift */,
OBJ_11 /* DeinitObserver.swift */, OBJ_11 /* DeinitObserver.swift */,
OBJ_12 /* Deprecated.swift */,
OBJ_13 /* Extensions.swift */, OBJ_13 /* Extensions.swift */,
OBJ_14 /* Interval.swift */, OBJ_14 /* Interval.swift */,
OBJ_15 /* Monthday.swift */, OBJ_15 /* Monthday.swift */,
@ -271,7 +267,7 @@
English, English,
en, en,
); );
mainGroup = OBJ_5 /* */; mainGroup = OBJ_5;
productRefGroup = OBJ_36 /* Products */; productRefGroup = OBJ_36 /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@ -292,7 +288,6 @@
OBJ_49 /* Atomic.swift in Sources */, OBJ_49 /* Atomic.swift in Sources */,
OBJ_50 /* Bag.swift in Sources */, OBJ_50 /* Bag.swift in Sources */,
OBJ_51 /* DeinitObserver.swift in Sources */, OBJ_51 /* DeinitObserver.swift in Sources */,
OBJ_52 /* Deprecated.swift in Sources */,
OBJ_53 /* Extensions.swift in Sources */, OBJ_53 /* Extensions.swift in Sources */,
OBJ_54 /* Interval.swift in Sources */, OBJ_54 /* Interval.swift in Sources */,
OBJ_55 /* Monthday.swift in Sources */, OBJ_55 /* Monthday.swift in Sources */,

View File

@ -1,31 +1,38 @@
import Foundation import Foundation
/// Represents a box that can read and write the underlying value atomically. /// An atomic box that can read and write the underlying value atomically.
final class Atomic<T> { final class Atomic<T> {
private var v: T private var val: T
private let lock = NSLock() private let lock = NSLock()
/// Init with the underlying value. /// Create an atomic box with the given initial value.
init(_ value: T) {
self.v = value
}
/// Creates a snapshot of the value nonatomically.
@inline(__always) @inline(__always)
func snapshot() -> T { init(_ value: T) {
return v self.val = value
} }
/// Reads the value atomically. /// Reads the current value atomically.
@inline(__always) @inline(__always)
func read<U>(_ body: (T) -> U) -> U { func read<U>(_ body: (T) -> U) -> U {
return lock.withLock { body(v) } return lock.withLock { body(val) }
} }
/// Writes the value atomically. /// Reads the current value atomically.
@inline(__always)
func readVoid(_ body: (T) -> Void) {
lock.withLockVoid { body(val) }
}
/// Writes the current value atomically.
@inline(__always) @inline(__always)
func write<U>(_ body: (inout T) -> U) -> U { func write<U>(_ body: (inout T) -> U) -> U {
return lock.withLock { body(&v) } return lock.withLock { body(&val) }
}
/// Writes the current value atomically.
@inline(__always)
func writeVoid(_ body: (inout T) -> Void) {
lock.withLockVoid { body(&val) }
} }
} }

View File

@ -1,6 +1,6 @@
import Foundation import Foundation
/// A unique key used to operate the corresponding element from a bag. /// A unique key for removing an element from a bag.
struct BagKey: Equatable { struct BagKey: Equatable {
fileprivate let i: UInt64 fileprivate let i: UInt64
@ -15,7 +15,7 @@ struct BagKey: Equatable {
} }
} }
/// A generator used to generate a sequence of unique `BagKey`. /// A generator that can generate a sequence of unique `BagKey`.
/// ///
/// let k1 = gen.next() /// let k1 = gen.next()
/// let k2 = gen.next() /// let k2 = gen.next()
@ -26,8 +26,8 @@ struct BagKeyGenerator: Sequence, IteratorProtocol {
private var k = BagKey(underlying: 0) private var k = BagKey(underlying: 0)
/// Advances to the next element and returns it, or nil if no next element exists. /// Advances to the next key and returns it, or nil if no next key exists.
mutating func next() -> Element? { mutating func next() -> BagKey? {
if k.i == UInt64.max { if k.i == UInt64.max {
return nil return nil
} }
@ -36,7 +36,7 @@ struct BagKeyGenerator: Sequence, IteratorProtocol {
} }
} }
/// A data structure used to store a sequence of elements. /// An ordered sequence.
/// ///
/// let k1 = bag.append(e1) /// let k1 = bag.append(e1)
/// let k2 = bag.append(e2) /// let k2 = bag.append(e2)
@ -46,48 +46,48 @@ struct BagKeyGenerator: Sequence, IteratorProtocol {
/// // -> e2 /// // -> e2
/// } /// }
/// ///
/// bag.delete(k1) /// bag.removeValue(for: k1)
struct Bag<Element> { struct Bag<Element> {
private typealias Entry = (key: BagKey, element: Element) private typealias Entry = (key: BagKey, val: Element)
private var keys = BagKeyGenerator() private var keyGen = BagKeyGenerator()
private var entries: [Entry] = [] private var entries: [Entry] = []
/// Pushes the given element on to the end of this container. /// Appends a new element at the end of this bag.
@discardableResult @discardableResult
mutating func append(_ new: Element) -> BagKey { mutating func append(_ new: Element) -> BagKey {
let key = keys.next()! let key = keyGen.next()!
let entry = (key: key, element: new) let entry = (key: key, val: new)
entries.append(entry) entries.append(entry)
return key return key
} }
/// Returns the element for key if key is in this container. /// Returns the element associated with a given key.
func get(_ key: BagKey) -> Element? { func value(for key: BagKey) -> Element? {
if let entry = entries.first(where: { $0.key == key }) { if let entry = entries.first(where: { $0.key == key }) {
return entry.element return entry.val
} }
return nil return nil
} }
/// Deletes the element with the given key and returns this element. /// Removes the given key and its associated element from this bag.
@discardableResult @discardableResult
mutating func delete(_ key: BagKey) -> Element? { mutating func removeValue(for key: BagKey) -> Element? {
if let i = entries.firstIndex(where: { $0.key == key }) { if let i = entries.firstIndex(where: { $0.key == key }) {
return entries.remove(at: i).element return entries.remove(at: i).val
} }
return nil return nil
} }
/// Removes all elements from this containers. /// Removes all elements from this bag.
mutating func clear() { mutating func removeAll() {
entries.removeAll() entries.removeAll()
} }
/// The number of elements in this containers. /// The number of elements in this bag.
var count: Int { var count: Int {
return entries.count return entries.count
} }
@ -95,11 +95,12 @@ struct Bag<Element> {
extension Bag: Sequence { extension Bag: Sequence {
/// Returns an iterator over the elements of this containers. /// Returns an iterator over the elements of this bag.
@inline(__always)
func makeIterator() -> AnyIterator<Element> { func makeIterator() -> AnyIterator<Element> {
var iterator = entries.makeIterator() var iterator = entries.makeIterator()
return AnyIterator<Element> { return AnyIterator<Element> {
return iterator.next()?.element return iterator.next()?.val
} }
} }
} }

View File

@ -2,9 +2,7 @@ import Foundation
#if canImport(ObjectiveC) #if canImport(ObjectiveC)
private var DEINIT_OBSERVER_KEY: Void = () /// An observer that receives deinit event of the object.
/// Used to observe object deinit.
/// ///
/// let observer = DeinitObserver.observe(target) { /// let observer = DeinitObserver.observe(target) {
/// print("\(target) deinit") /// print("\(target) deinit")
@ -13,38 +11,40 @@ private var DEINIT_OBSERVER_KEY: Void = ()
/// observer.cancel() /// observer.cancel()
class DeinitObserver { class DeinitObserver {
private var associateKey: Void = ()
private(set) weak var observed: AnyObject? private(set) weak var observed: AnyObject?
private var action: (() -> Void)? private var block: (() -> Void)?
private init(_ action: @escaping () -> Void) { private init(_ block: @escaping () -> Void) {
self.action = action self.block = block
} }
/// Add observer. /// Observe deinit event of the object.
@discardableResult @discardableResult
static func observe( static func observe(
_ object: AnyObject, _ object: AnyObject,
onDeinit action: @escaping () -> Void using block: @escaping () -> Void
) -> DeinitObserver { ) -> DeinitObserver {
let observer = DeinitObserver(action) let observer = DeinitObserver(block)
observer.observed = object observer.observed = object
objc_setAssociatedObject(object, &DEINIT_OBSERVER_KEY, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_setAssociatedObject(object, &observer.associateKey, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return observer return observer
} }
/// Remove observer. /// Cancel observing.
func cancel() { func cancel() {
action = nil block = nil
if let o = observed { if let o = observed {
objc_setAssociatedObject(o, &DEINIT_OBSERVER_KEY, nil, .OBJC_ASSOCIATION_ASSIGN) objc_setAssociatedObject(o, &associateKey, nil, .OBJC_ASSOCIATION_ASSIGN)
} }
} }
deinit { deinit {
action?() block?()
} }
} }

View File

@ -1,59 +0,0 @@
//
// Deprecated.swift
// Schedule
//
// Created by Quentin Jin on 2019/3/4.
// Copyright © 2019 Schedule. All rights reserved.
//
import Foundation
extension Monthday {
/// A Boolean value indicating whether today is the monthday.
@available(*, deprecated, message: "Use date.is(_:Monthday)")
public var isToday: Bool {
let components = toDateComponents()
let m = Calendar.gregorian.component(.month, from: Date())
let d = Calendar.gregorian.component(.day, from: Date())
return m == components.month && d == components.day
}
}
extension Weekday {
/// A Boolean value indicating whether today is the weekday.
@available(*, deprecated, message: "Use date.is(_:Weekday)")
public var isToday: Bool {
return Calendar.gregorian.component(.weekday, from: Date()) == rawValue
}
}
extension Time {
/// The interval between this time and zero o'clock.
@available(*, deprecated, renamed: "intervalSinceStartOfDay")
public var intervalSinceZeroClock: Interval {
return hour.hours + minute.minutes + second.seconds + nanosecond.nanoseconds
}
}
extension Plan {
/// Creates a plan from an interval sequence.
/// The task will be executed after each interval in the sequence.
@available(*, deprecated, message: "Use Plan.of")
public static func from<S>(_ sequence: S) -> Plan where S: Sequence, S.Element == Interval {
return Plan.of(sequence)
}
/// Creates a plan from a date sequence.
/// The task will be executed at each date in the sequence.
/// - Note: Returns `Plan.never` if given no parameters.
@available(*, deprecated, message: "Use Plan.of")
public static func from<S>(_ sequence: S) -> Plan where S: Sequence, S.Element == Date {
return Plan.make(sequence.makeIterator)
}
}

View File

@ -2,6 +2,7 @@ import Foundation
extension Double { extension Double {
/// Returns a value of this number clamped to `Int.min...Int.max`.
func clampedToInt() -> Int { func clampedToInt() -> Int {
if self >= Double(Int.max) { return Int.max } if self >= Double(Int.max) { return Int.max }
if self <= Double(Int.min) { return Int.min } if self <= Double(Int.min) { return Int.min }
@ -11,13 +12,11 @@ extension Double {
extension Int { extension Int {
/// Returns the sum of the two given values, in case of any overflow,
/// the result will be clamped to int.
func clampedAdding(_ other: Int) -> Int { func clampedAdding(_ other: Int) -> Int {
return (Double(self) + Double(other)).clampedToInt() return (Double(self) + Double(other)).clampedToInt()
} }
func clampedSubtracting(_ other: Int) -> Int {
return clampedAdding(-other)
}
} }
extension Calendar { extension Calendar {
@ -47,9 +46,9 @@ extension NSLocking {
return try body() return try body()
} }
/// Executes a closure returning a value while acquiring the lock. /// Executes a closure while acquiring the lock.
@inline(__always) @inline(__always)
func withLock(_ body: () throws -> Void) rethrows { func withLockVoid(_ body: () throws -> Void) rethrows {
lock(); defer { unlock() } lock(); defer { unlock() }
try body() try body()
} }

View File

@ -1,6 +1,6 @@
import Foundation import Foundation
/// `Interval` represents a length of time. /// Type used to represent a time-based amount of time, such as '34.5 seconds'.
public struct Interval { public struct Interval {
/// The length of this interval in nanoseconds. /// The length of this interval in nanoseconds.
@ -13,36 +13,26 @@ public struct Interval {
} }
// MARK: - Describing // MARK: - Describing
extension Interval { extension Interval {
/// A boolean value indicating whether this interval is less than zero. /// A Boolean value indicating whether this interval is less than zero.
/// ///
/// An interval can be negative:
/// ///
/// - The interval from 6:00 to 7:00 is `1.hour`, /// An Interval represents a directed distance between two points
/// but the interval from 7:00 to 6:00 is `-1.hour`. /// on the time-line and can therefore be positive, zero or negative.
/// In this case, `-1.hour` means **one hour ago**.
///
/// - The interval comparing `3.hour` to `1.hour` is `2.hour`,
/// but the interval comparing `1.hour` to `3.hour` is `-2.hour`.
/// In this case, `-2.hour` means **two hours shorter**
public var isNegative: Bool { public var isNegative: Bool {
return nanoseconds < 0 return nanoseconds < 0
} }
/// See `isNegative`. /// A copy of this duration with a positive length.
public var isPositive: Bool { public var abs: Interval {
return nanoseconds > 0 return Interval(nanoseconds: Swift.abs(nanoseconds))
} }
/// The absolute value of the length of this interval in nanoseconds. /// A copy of this interval with the length negated.
public var magnitude: Double { public var negated: Interval {
return nanoseconds.magnitude return Interval(nanoseconds: -nanoseconds)
}
/// The additive inverse of this interval.
public var opposite: Interval {
return (-nanoseconds).nanoseconds
} }
} }
@ -75,18 +65,22 @@ extension Interval: CustomDebugStringConvertible {
} }
// MARK: - Comparing // MARK: - Comparing
extension Interval: Comparable { extension Interval: Comparable {
/// Compares two intervals. /// Compares two intervals and returns a comparison result value
/// that indicates the sort order of two intervals.
/// ///
/// The comparison is magnitude independent, a positive interval is /// A positive interval is always ordered ascending to a negative interval.
/// always ordered ascending to a negative interval.
public func compare(_ other: Interval) -> ComparisonResult { public func compare(_ other: Interval) -> ComparisonResult {
let now = Date() let d = nanoseconds - other.nanoseconds
return now.adding(self).compare(now.adding(other))
if d < 0 { return .orderedAscending }
if d > 0 { return .orderedDescending }
return .orderedSame
} }
/// Returns a boolean value indicating whether the first interval is /// Returns a Boolean value indicating whether the first interval is
/// less than the second interval. /// less than the second interval.
/// ///
/// A negative interval is always less than a positive interval. /// A negative interval is always less than a positive interval.
@ -94,43 +88,23 @@ extension Interval: Comparable {
return lhs.compare(rhs) == .orderedAscending return lhs.compare(rhs) == .orderedAscending
} }
/// Returns a boolean value indicating whether this interval is longer /// Returns a Boolean value indicating whether this interval is longer
/// than the given value. /// than the given interval.
public func isLonger(than other: Interval) -> Bool { public func isLonger(than other: Interval) -> Bool {
return magnitude > other.magnitude return abs > other.abs
} }
/// Returns a boolean value indicating whether this interval is shorter /// Returns a Boolean value indicating whether this interval is shorter
/// than the given value. /// than the given interval.
public func isShorter(than other: Interval) -> Bool { public func isShorter(than other: Interval) -> Bool {
return magnitude < other.magnitude return abs < other.abs
}
/// Returns the longest interval of the given values.
public static func longest(_ intervals: Interval...) -> Interval {
return longest(intervals)!
}
/// Returns the longest interval of the given values.
public static func longest(_ intervals: [Interval]) -> Interval? {
guard !intervals.isEmpty else { return nil }
return intervals.sorted(by: { $0.magnitude > $1.magnitude })[0]
}
/// Returns the shortest interval of the given values.
public static func shortest(_ intervals: Interval...) -> Interval {
return shortest(intervals)!
}
/// Returns the shortest interval of the given values.
public static func shortest(_ intervals: [Interval]) -> Interval? {
guard !intervals.isEmpty else { return nil }
return intervals.sorted(by: { $0.magnitude < $1.magnitude })[0]
} }
} }
// MARK: - Adding & Subtracting // MARK: - Adding & Subtracting
extension Interval { extension Interval {
/// Returns a new interval by multipling this interval by the given number. /// Returns a new interval by multipling this interval by the given number.
/// ///
/// 1.hour * 2 == 2.hours /// 1.hour * 2 == 2.hours
@ -144,13 +118,6 @@ extension Interval {
public func adding(_ other: Interval) -> Interval { public func adding(_ other: Interval) -> Interval {
return Interval(nanoseconds: nanoseconds + other.nanoseconds) return Interval(nanoseconds: nanoseconds + other.nanoseconds)
} }
/// Returns a new interval by subtracting the given interval from this interval.
///
/// 2.hours - 1.hour == 1.hour
public func subtracting(_ other: Interval) -> Interval {
return Interval(nanoseconds: nanoseconds - other.nanoseconds)
}
} }
// MARK: - Operators // MARK: - Operators
@ -174,21 +141,22 @@ extension Interval {
/// ///
/// 2.hours - 1.hour == 1.hour /// 2.hours - 1.hour == 1.hour
public static func - (lhs: Interval, rhs: Interval) -> Interval { public static func - (lhs: Interval, rhs: Interval) -> Interval {
return lhs.subtracting(rhs) return lhs.adding(rhs.negated)
} }
/// Adds two intervals and stores the result in the first interval. /// Adds two intervals and stores the result in the left interval.
public static func += (lhs: inout Interval, rhs: Interval) { public static func += (lhs: inout Interval, rhs: Interval) {
lhs = lhs.adding(rhs) lhs = lhs.adding(rhs)
} }
/// Returns the additive inverse of the specified interval. /// Returns the additive inverse of the specified interval.
public prefix static func - (interval: Interval) -> Interval { public prefix static func - (interval: Interval) -> Interval {
return interval.opposite return interval.negated
} }
} }
// MARK: - Sugars // MARK: - Sugars
extension Interval { extension Interval {
/// Creates an interval from the given number of seconds. /// Creates an interval from the given number of seconds.
@ -245,6 +213,7 @@ public protocol IntervalConvertible {
extension Int: IntervalConvertible { extension Int: IntervalConvertible {
/// Creates an interval from this amount of nanoseconds.
public var nanoseconds: Interval { public var nanoseconds: Interval {
return Interval(nanoseconds: Double(self)) return Interval(nanoseconds: Double(self))
} }
@ -252,6 +221,7 @@ extension Int: IntervalConvertible {
extension Double: IntervalConvertible { extension Double: IntervalConvertible {
/// Creates an interval from this amount of nanoseconds.
public var nanoseconds: Interval { public var nanoseconds: Interval {
return Interval(nanoseconds: self) return Interval(nanoseconds: self)
} }
@ -259,80 +229,96 @@ extension Double: IntervalConvertible {
extension IntervalConvertible { extension IntervalConvertible {
// Alias for `nanoseconds`.
public var nanosecond: Interval { public var nanosecond: Interval {
return nanoseconds return nanoseconds
} }
// Alias for `microseconds`.
public var microsecond: Interval { public var microsecond: Interval {
return microseconds return microseconds
} }
/// Creates an interval from this amount of microseconds.
public var microseconds: Interval { public var microseconds: Interval {
return nanoseconds * pow(10, 3) return nanoseconds * pow(10, 3)
} }
/// Alias for `milliseconds`.
public var millisecond: Interval { public var millisecond: Interval {
return milliseconds return milliseconds
} }
/// Creates an interval from this amount of milliseconds.
public var milliseconds: Interval { public var milliseconds: Interval {
return nanoseconds * pow(10, 6) return nanoseconds * pow(10, 6)
} }
/// Alias for `second`.
public var second: Interval { public var second: Interval {
return seconds return seconds
} }
/// Creates an interval from this amount of seconds.
public var seconds: Interval { public var seconds: Interval {
return nanoseconds * pow(10, 9) return nanoseconds * pow(10, 9)
} }
/// Alias for `minute`.
public var minute: Interval { public var minute: Interval {
return minutes return minutes
} }
/// Creates an interval from this amount of minutes.
public var minutes: Interval { public var minutes: Interval {
return seconds * 60 return seconds * 60
} }
/// Alias for `hours`.
public var hour: Interval { public var hour: Interval {
return hours return hours
} }
/// Creates an interval from this amount of hours.
public var hours: Interval { public var hours: Interval {
return minutes * 60 return minutes * 60
} }
/// Alias for `days`.
public var day: Interval { public var day: Interval {
return days return days
} }
/// Creates an interval from this amount of days.
public var days: Interval { public var days: Interval {
return hours * 24 return hours * 24
} }
/// Alias for `weeks`.
public var week: Interval { public var week: Interval {
return weeks return weeks
} }
/// Creates an interval from this amount of weeks.
public var weeks: Interval { public var weeks: Interval {
return days * 7 return days * 7
} }
} }
// MARK: - Date // MARK: - Date
extension Date { extension Date {
/// The interval between this date and the current date and time. /// The interval between this date and the current date and time.
/// ///
/// If this date is earlier than now, this interval will be negative. /// If this date is earlier than now, the interval will be negative.
public var intervalSinceNow: Interval { public var intervalSinceNow: Interval {
return timeIntervalSinceNow.seconds return timeIntervalSinceNow.seconds
} }
/// Returns the interval between this date and the given date. /// Returns the interval between this date and the given date.
/// ///
/// If this date is earlier than the given date, this interval will be negative. /// If this date is earlier than the given date, the interval will be negative.
public func interval(since date: Date) -> Interval { public func interval(since date: Date) -> Interval {
return timeIntervalSince(date).seconds return timeIntervalSince(date).seconds
} }
@ -342,18 +328,19 @@ extension Date {
return addingTimeInterval(interval.asSeconds()) return addingTimeInterval(interval.asSeconds())
} }
/// Returns a date with an interval added to it. /// Returns a new date by adding an interval to the date.
public static func + (lhs: Date, rhs: Interval) -> Date { public static func + (lhs: Date, rhs: Interval) -> Date {
return lhs.adding(rhs) return lhs.adding(rhs)
} }
} }
// MARK: - DispatchSourceTimer // MARK: - DispatchSourceTimer
extension DispatchSourceTimer { extension DispatchSourceTimer {
/// Schedule this timer later. /// Schedule this timer later.
func schedule(after timeout: Interval) { func schedule(after timeout: Interval) {
guard !timeout.isNegative else { return } if timeout.isNegative { return }
let ns = timeout.nanoseconds.clampedToInt() let ns = timeout.nanoseconds.clampedToInt()
schedule(wallDeadline: .now() + DispatchTimeInterval.nanoseconds(ns)) schedule(wallDeadline: .now() + DispatchTimeInterval.nanoseconds(ns))
} }

View File

@ -1,6 +1,6 @@
import Foundation import Foundation
/// `Monthday` represents a day of a month. /// `Monthday` represents the combination of a month and day-of-month.
public enum Monthday { public enum Monthday {
case january(Int) case january(Int)
@ -27,9 +27,9 @@ public enum Monthday {
case december(Int) case december(Int)
/// Returns a dateComponenets of the monthday, using gregorian calender and /// Returns a dateComponenets of this monthday, using gregorian calender and
/// current time zone. /// current time zone.
public func toDateComponents() -> DateComponents { public func asDateComponents() -> DateComponents {
var month, day: Int var month, day: Int
switch self { switch self {
case .january(let n): month = 1; day = n case .january(let n): month = 1; day = n
@ -49,16 +49,15 @@ public enum Monthday {
calendar: Calendar.gregorian, calendar: Calendar.gregorian,
timeZone: TimeZone.current, timeZone: TimeZone.current,
month: month, month: month,
day: day day: day)
)
} }
} }
extension Date { extension Date {
/// Returns a boolean value indicating whether today is the monthday. /// Returns a Boolean value indicating whether this date is the monthday.
public func `is`(_ monthday: Monthday) -> Bool { public func `is`(_ monthday: Monthday) -> Bool {
let components = monthday.toDateComponents() let components = monthday.asDateComponents()
let m = Calendar.gregorian.component(.month, from: self) let m = Calendar.gregorian.component(.month, from: self)
let d = Calendar.gregorian.component(.day, from: self) let d = Calendar.gregorian.component(.day, from: self)
@ -72,7 +71,7 @@ extension Monthday: CustomStringConvertible {
/// ///
/// "Monthday: May 1st" /// "Monthday: May 1st"
public var description: String { public var description: String {
let components = toDateComponents() let components = asDateComponents()
let m = components.month! let m = components.month!
let d = components.day! let d = components.day!

View File

@ -1,6 +1,7 @@
import Foundation import Foundation
/// `Period` represents a period of time defined in terms of fields. /// Type used to represents a date-based amount of time in the ISO-8601 calendar system,
/// such as '2 years, 3 months and 4 days'.
/// ///
/// It's a little different from `Interval`: /// It's a little different from `Interval`:
/// ///
@ -51,104 +52,113 @@ public struct Period {
/// Period.registerQuantifier("fifty", for: 15) /// Period.registerQuantifier("fifty", for: 15)
/// let period = Period("fifty minutes") /// let period = Period("fifty minutes")
public static func registerQuantifier(_ word: String, for number: Int) { public static func registerQuantifier(_ word: String, for number: Int) {
quantifiers.write { $0[word] = number } quantifiers.writeVoid { $0[word] = number }
} }
/// Creates a period from a natural expression. /// Initializes a period from a natural expression.
/// ///
/// Period("one second") => Period(seconds: 1) /// Period("one second") -> Period(seconds: 1)
/// Period("two hours and ten minutes") => Period(hours: 2, minutes: 10) /// Period("two hours and ten minutes") -> Period(hours: 2, minutes: 10)
/// Period("1 year, 2 months and 3 days") => Period(years: 1, months: 2, days: 3) /// Period("1 year, 2 months and 3 days") -> Period(years: 1, months: 2, days: 3)
public init?(_ string: String) { public init?(_ string: String) {
var period = string var str = string
for (word, number) in Period.quantifiers.read({ $0 }) { for (word, number) in Period.quantifiers.read({ $0 }) {
period = period.replacingOccurrences(of: word, with: "\(number)") str = str.replacingOccurrences(of: word, with: "\(number)")
} }
guard let regex = try? NSRegularExpression(pattern: "( and |, )") else { guard let regexp = try? NSRegularExpression(pattern: "( and |, )") else {
return nil return nil
} }
period = regex.stringByReplacingMatches(in: period, range: NSRange(location: 0, length: period.count), withTemplate: "$") str = regexp.stringByReplacingMatches(in: str, range: NSRange(location: 0, length: str.count), withTemplate: "$")
var period = 0.year
for pair in str.split(separator: "$").map({ $0.split(separator: " ") }) {
guard
pair.count == 2,
let number = Int(pair[0])
else {
return nil
}
var result = 0.year
for pair in period.split(separator: "$").map({ $0.split(separator: " ") }) {
guard pair.count == 2 else { return nil }
guard let number = Int(pair[0]) else { return nil }
var unit = String(pair[1]) var unit = String(pair[1])
if unit.last == "s" { unit.removeLast() } if unit.last == "s" { unit.removeLast() }
switch unit { switch unit {
case "year": result = result + number.years case "year": period = period + number.years
case "month": result = result + number.months case "month": period = period + number.months
case "day": result = result + number.days case "day": period = period + number.days
case "week": result = result + (number * 7).days case "week": period = period + (number * 7).days
case "hour": result = result + number.hours case "hour": period = period + number.hours
case "minute": result = result + number.minutes case "minute": period = period + number.minutes
case "second": result = result + number.second case "second": period = period + number.second
case "nanosecond": result = result + number.nanosecond case "nanosecond": period = period + number.nanosecond
default: break default: break
} }
} }
self = result.tidied(to: .day) self = period
} }
/// Returns a new period by adding a period to this period. /// Returns a new period by adding the given period to this period.
public func adding(_ other: Period) -> Period { public func adding(_ other: Period) -> Period {
return Period(years: years.clampedAdding(other.years), return Period(
months: months.clampedAdding(other.months), years: years.clampedAdding(other.years),
days: days.clampedAdding(other.days), months: months.clampedAdding(other.months),
hours: hours.clampedAdding(other.hours), days: days.clampedAdding(other.days),
minutes: minutes.clampedAdding(other.minutes), hours: hours.clampedAdding(other.hours),
seconds: seconds.clampedAdding(other.seconds), minutes: minutes.clampedAdding(other.minutes),
nanoseconds: nanoseconds.clampedAdding(other.nanoseconds)) seconds: seconds.clampedAdding(other.seconds),
nanoseconds: nanoseconds.clampedAdding(other.nanoseconds))
} }
/// Returns a new period by adding an interval to this period. /// Returns a new period by adding an interval to the period.
public func adding(_ interval: Interval) -> Period { public func adding(_ interval: Interval) -> Period {
return Period( return Period(
years: years, months: months, days: days, years: years, months: months, days: days,
hours: hours, minutes: minutes, seconds: seconds, hours: hours, minutes: minutes, seconds: seconds,
nanoseconds: nanoseconds.clampedAdding(interval.nanoseconds.clampedToInt()) nanoseconds: nanoseconds.clampedAdding(interval.nanoseconds.clampedToInt()))
).tidied(to: .day)
} }
/// Adds two periods and produces their sum. /// Returns a new period by adding the right period to the left period.
///
/// Period(days: 1) + Period(days: 1) -> Period(days: 2)
public static func + (lhs: Period, rhs: Period) -> Period { public static func + (lhs: Period, rhs: Period) -> Period {
return lhs.adding(rhs) return lhs.adding(rhs)
} }
/// Returns a period with an interval added to it. /// Returns a new period by adding an interval to the period.
public static func + (lhs: Period, rhs: Interval) -> Period { public static func + (lhs: Period, rhs: Interval) -> Period {
return lhs.adding(rhs) return lhs.adding(rhs)
} }
/// Type to be used as tidying parameter. /// Represents the tidy level.
public enum Unit { public enum TideLevel {
case day, hour, minute, second, nanosecond case day, hour, minute, second, nanosecond
} }
/// Returns a tidied period. /// Returns the tidied period.
/// ///
/// Period(hours: 25).tidied(to .day) => Period(days: 1, hours: 1) /// Period(hours: 25).tidied(to .day) => Period(days: 1, hours: 1)
public func tidied(to unit: Unit) -> Period { public func tidied(to level: TideLevel) -> Period {
var period = self var period = self
if case .nanosecond = unit { return period }
if case .nanosecond = level { return period }
if period.nanoseconds.magnitude >= UInt(1.second.nanoseconds) { if period.nanoseconds.magnitude >= UInt(1.second.nanoseconds) {
period.seconds += period.nanoseconds / Int(1.second.nanoseconds) period.seconds += period.nanoseconds / Int(1.second.nanoseconds)
period.nanoseconds %= Int(1.second.nanoseconds) period.nanoseconds %= Int(1.second.nanoseconds)
} }
if case .second = level { return period }
if case .second = unit { return period }
if period.seconds.magnitude >= 60 { if period.seconds.magnitude >= 60 {
period.minutes += period.seconds / 60 period.minutes += period.seconds / 60
period.seconds %= 60 period.seconds %= 60
} }
if case .minute = level { return period }
if case .minute = unit { return period }
if period.minutes.magnitude >= 60 { if period.minutes.magnitude >= 60 {
period.hours += period.minutes / 60 period.hours += period.minutes / 60
period.minutes %= 60 period.minutes %= 60
} }
if case .hour = level { return period }
if case .hour = unit { return period }
if period.hours.magnitude >= 24 { if period.hours.magnitude >= 24 {
period.days += period.hours / 24 period.days += period.hours / 24
period.hours %= 24 period.hours %= 24
@ -156,9 +166,9 @@ public struct Period {
return period return period
} }
/// Returns a dateComponenets of the period, using gregorian calender and /// Returns a dateComponenets of this period, using gregorian calender and
/// current time zone. /// current time zone.
public func toDateComponents() -> DateComponents { public func asDateComponents() -> DateComponents {
return DateComponents(year: years, month: months, day: days, return DateComponents(year: years, month: months, day: days,
hour: hours, minute: minutes, second: seconds, hour: hours, minute: minutes, second: seconds,
nanosecond: nanoseconds) nanosecond: nanoseconds)
@ -169,10 +179,10 @@ extension Date {
/// Returns a new date by adding a period to this date. /// Returns a new date by adding a period to this date.
public func adding(_ period: Period) -> Date { public func adding(_ period: Period) -> Date {
return Calendar.gregorian.date(byAdding: period.toDateComponents(), to: self) ?? .distantFuture return Calendar.gregorian.date(byAdding: period.asDateComponents(), to: self) ?? .distantFuture
} }
/// Returns a date with a period added to it. /// Returns a new date by adding a period to this date.
public static func + (lhs: Date, rhs: Period) -> Date { public static func + (lhs: Date, rhs: Period) -> Date {
return lhs.adding(rhs) return lhs.adding(rhs)
} }
@ -180,18 +190,22 @@ extension Date {
extension Int { extension Int {
/// Creates a period from this amount of years.
public var years: Period { public var years: Period {
return Period(years: self) return Period(years: self)
} }
/// Alias for `years`.
public var year: Period { public var year: Period {
return years return years
} }
/// Creates a period from this amount of month.
public var months: Period { public var months: Period {
return Period(months: self) return Period(months: self)
} }
/// Alias for `month`.
public var month: Period { public var month: Period {
return months return months
} }

View File

@ -302,7 +302,7 @@ extension Plan {
var last: Date! var last: Date!
return AnyIterator { return AnyIterator {
last = last ?? Date() last = last ?? Date()
guard let next = calendar.date(byAdding: period.toDateComponents(), guard let next = calendar.date(byAdding: period.asDateComponents(),
to: last) else { to: last) else {
return nil return nil
} }
@ -410,7 +410,7 @@ extension Plan {
} else if Date().is(weekday) { } else if Date().is(weekday) {
date = Date().startOfToday date = Date().startOfToday
} else { } else {
let components = weekday.toDateComponents() let components = weekday.asDateComponents()
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict) date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict)
} }
return date return date
@ -450,7 +450,7 @@ extension Plan {
} else if Date().is(monthday) { } else if Date().is(monthday) {
date = Date().startOfToday date = Date().startOfToday
} else { } else {
let components = monthday.toDateComponents() let components = monthday.asDateComponents()
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict) date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict)
} }
return date return date

View File

@ -94,7 +94,7 @@ open class Task {
} }
private func scheduleNext() { private func scheduleNext() {
_mutex.withLock { _mutex.withLockVoid {
let now = Date() let now = Date()
var estimated = _timeline.estimatedNextExecution ?? now var estimated = _timeline.estimatedNextExecution ?? now
repeat { repeat {
@ -155,7 +155,7 @@ open class Task {
/// Reschedules this task with the new plan. /// Reschedules this task with the new plan.
public func reschedule(_ new: Plan) { public func reschedule(_ new: Plan) {
_mutex.withLock { _mutex.withLockVoid {
_iterator = new.makeIterator() _iterator = new.makeIterator()
} }
scheduleNext() scheduleNext()
@ -170,7 +170,7 @@ open class Task {
/// Suspends this task. /// Suspends this task.
public func suspend() { public func suspend() {
_mutex.withLock { _mutex.withLockVoid {
if _suspensions < UInt64.max { if _suspensions < UInt64.max {
_timer.suspend() _timer.suspend()
_suspensions += 1 _suspensions += 1
@ -180,7 +180,7 @@ open class Task {
/// Resumes this task. /// Resumes this task.
public func resume() { public func resume() {
_mutex.withLock { _mutex.withLockVoid {
if _suspensions > 0 { if _suspensions > 0 {
_timer.resume() _timer.resume()
_suspensions -= 1 _suspensions -= 1
@ -190,7 +190,7 @@ open class Task {
/// Cancels this task. /// Cancels this task.
public func cancel() { public func cancel() {
_mutex.withLock { _mutex.withLockVoid {
_timer.cancel() _timer.cancel()
} }
TaskCenter.default.remove(self) TaskCenter.default.remove(self)
@ -234,7 +234,9 @@ open class Task {
/// - Returns: `true` if set successfully, `false` if not. /// - Returns: `true` if set successfully, `false` if not.
@discardableResult @discardableResult
public func setLifetime(_ interval: Interval) -> Bool { public func setLifetime(_ interval: Interval) -> Bool {
guard restOfLifetime.isPositive else { return false } if restOfLifetime.isNegative {
return false
}
_mutex.lock() _mutex.lock()
let age = Date().interval(since: _timeline.initialization) let age = Date().interval(since: _timeline.initialization)
@ -258,10 +260,10 @@ open class Task {
@discardableResult @discardableResult
public func addLifetime(_ interval: Interval) -> Bool { public func addLifetime(_ interval: Interval) -> Bool {
var rest = restOfLifetime var rest = restOfLifetime
guard rest.isPositive else { return false } if rest.isNegative { return false }
rest += interval rest += interval
guard rest.isPositive else { return false } if rest.isNegative { return false }
_mutex.withLock { _mutex.withLockVoid {
_lifetime += interval _lifetime += interval
_lifetimeTimer.schedule(after: rest) _lifetimeTimer.schedule(after: rest)
} }
@ -276,7 +278,7 @@ open class Task {
/// - Returns: `true` if set successfully, `false` if not. /// - Returns: `true` if set successfully, `false` if not.
@discardableResult @discardableResult
public func subtractLifetime(_ interval: Interval) -> Bool { public func subtractLifetime(_ interval: Interval) -> Bool {
return addLifetime(interval.opposite) return addLifetime(interval.negated)
} }
// MARK: - Action // MARK: - Action
@ -298,15 +300,15 @@ open class Task {
/// Removes action by key from this task. /// Removes action by key from this task.
public func removeAction(byKey key: ActionKey) { public func removeAction(byKey key: ActionKey) {
_mutex.withLock { _mutex.withLockVoid {
_ = _onElapseActions.delete(key.bagKey) _ = _onElapseActions.removeValue(for: key.bagKey)
} }
} }
/// Removes all actions from this task. /// Removes all actions from this task.
public func removeAllActions() { public func removeAllActions() {
_mutex.withLock { _mutex.withLockVoid {
_onElapseActions.clear() _onElapseActions.removeAll()
} }
} }

View File

@ -53,7 +53,7 @@ open class TaskCenter {
task.taskCenterMutex.unlock() task.taskCenterMutex.unlock()
mutex.withLock { mutex.withLockVoid {
let box = TaskBox(task) let box = TaskBox(task)
tagMap[box] = [] tagMap[box] = []
} }
@ -75,7 +75,7 @@ open class TaskCenter {
task.taskCenterMutex.unlock() task.taskCenterMutex.unlock()
mutex.withLock { mutex.withLockVoid {
let box = TaskBox(task) let box = TaskBox(task)
if let tags = self.tagMap[box] { if let tags = self.tagMap[box] {
for tag in tags { for tag in tags {
@ -99,7 +99,7 @@ open class TaskCenter {
open func addTags(_ tags: [String], to task: Task) { open func addTags(_ tags: [String], to task: Task) {
guard task.taskCenter === self else { return } guard task.taskCenter === self else { return }
mutex.withLock { mutex.withLockVoid {
let box = TaskBox(task) let box = TaskBox(task)
if tagMap[box] == nil { if tagMap[box] == nil {
tagMap[box] = [] tagMap[box] = []
@ -127,7 +127,7 @@ open class TaskCenter {
open func removeTags(_ tags: [String], from task: Task) { open func removeTags(_ tags: [String], from task: Task) {
guard task.taskCenter === self else { return } guard task.taskCenter === self else { return }
mutex.withLock { mutex.withLockVoid {
let box = TaskBox(task) let box = TaskBox(task)
for tag in tags { for tag in tags {
tagMap[box]?.remove(tag) tagMap[box]?.remove(tag)
@ -170,7 +170,7 @@ open class TaskCenter {
/// Removes all tasks in this center. /// Removes all tasks in this center.
open func clear() { open func clear() {
mutex.withLock { mutex.withLockVoid {
tagMap = [:] tagMap = [:]
taskMap = [:] taskMap = [:]
} }

View File

@ -15,9 +15,7 @@ public struct Time {
/// Nanosecond of second. /// Nanosecond of second.
public let nanosecond: Int public let nanosecond: Int
/// Creates a time with `hour`, `minute`, `second` and `nanosecond` fields. /// Initializes a time with `hour`, `minute`, `second` and `nanosecond`.
///
/// If any parameter is illegal, return nil.
/// ///
/// Time(hour: 11, minute: 11) => "11:11:00.000" /// Time(hour: 11, minute: 11) => "11:11:00.000"
/// Time(hour: 25) => nil /// Time(hour: 25) => nil
@ -36,34 +34,29 @@ public struct Time {
self.nanosecond = nanosecond self.nanosecond = nanosecond
} }
/// Creates a time with a string. /// Initializes a time with a string.
/// ///
/// If the parameter is illegal, return nil. /// Time("11") -> Time(hour: 11)
/// /// Time("11:12") -> Time(hour: 11, minute: 12)
/// Time("11") == Time(hour: 11) /// Time("11:12:13") -> Time(hour: 11, minute: 12, second: 13)
/// Time("11:12") == Time(hour: 11, minute: 12) /// Time("11:12:13.123") -> Time(hour: 11, minute: 12, second: 13, nanosecond: 123000000)
/// Time("11:12:13") == Time(hour: 11, minute: 12, second: 13)
/// Time("11:12:13.123") == Time(hour: 11, minute: 12, second: 13, nanosecond: 123000000)
/// ///
/// Time("-1.0") == nil /// Time("-1.0") == nil
/// ///
/// Any of the previous examples can have a period suffix("am", "AM", "pm", "PM"),
/// separated by a space.
///
/// Time("11 pm") == Time(hour: 23) /// Time("11 pm") == Time(hour: 23)
/// Time("11:12:13 PM") == Time(hour: 23, minute: 12, second: 13) /// Time("11:12:13 PM") == Time(hour: 23, minute: 12, second: 13)
public init?(_ string: String) { public init?(_ string: String) {
let pattern = "^(\\d{1,2})(:(\\d{1,2})(:(\\d{1,2})(.(\\d{1,3}))?)?)?( (am|AM|pm|PM))?$" let pattern = "^(\\d{1,2})(:(\\d{1,2})(:(\\d{1,2})(.(\\d{1,3}))?)?)?( (am|AM|pm|PM))?$"
guard let regexp = try? NSRegularExpression(pattern: pattern, options: []) else { return nil } guard let regexp = try? NSRegularExpression(pattern: pattern, options: []) else { return nil }
guard let result = regexp.matches(in: string, options: [], range: NSRange(location: 0, length: string.count)).first else { return nil } guard let matches = regexp.matches(in: string, options: [], range: NSRange(location: 0, length: string.count)).first else { return nil }
var hasAM = false var hasAM = false
var hasPM = false var hasPM = false
var values: [Int] = [] var values: [Int] = []
for i in 0..<result.numberOfRanges { for i in 0..<matches.numberOfRanges {
let range = result.range(at: i) let range = matches.range(at: i)
if range.length == 0 { continue } if range.length == 0 { continue }
let captured = NSString(string: string).substring(with: range) let captured = NSString(string: string).substring(with: range)
hasAM = ["am", "AM"].contains(captured) hasAM = ["am", "AM"].contains(captured)
@ -94,7 +87,7 @@ public struct Time {
/// Returns a dateComponenets of the time, using gregorian calender and /// Returns a dateComponenets of the time, using gregorian calender and
/// current time zone. /// current time zone.
public func toDateComponents() -> DateComponents { public func asDateComponents() -> DateComponents {
return DateComponents(calendar: Calendar.gregorian, return DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.current, timeZone: TimeZone.current,
hour: hour, hour: hour,

View File

@ -7,18 +7,17 @@ public enum Weekday: Int {
/// Returns dateComponenets of the weekday, using gregorian calender and /// Returns dateComponenets of the weekday, using gregorian calender and
/// current time zone. /// current time zone.
public func toDateComponents() -> DateComponents { public func asDateComponents() -> DateComponents {
return DateComponents( return DateComponents(
calendar: Calendar.gregorian, calendar: Calendar.gregorian,
timeZone: TimeZone.current, timeZone: TimeZone.current,
weekday: rawValue weekday: rawValue)
)
} }
} }
extension Date { extension Date {
/// Returns a boolean value indicating whether this day is the weekday. /// Returns a Boolean value indicating whether this date is the weekday.
public func `is`(_ weekday: Weekday) -> Bool { public func `is`(_ weekday: Weekday) -> Bool {
return Calendar.gregorian.component(.weekday, from: self) == weekday.rawValue return Calendar.gregorian.component(.weekday, from: self) == weekday.rawValue
} }

View File

@ -3,29 +3,42 @@ import XCTest
final class AtomicTests: XCTestCase { final class AtomicTests: XCTestCase {
func testSnapshot() {
let i = Atomic<Int>(1)
XCTAssertEqual(i.snapshot(), 1)
}
func testRead() { func testRead() {
let i = Atomic<Int>(1) let i = Atomic<Int>(1)
i.read { let val = i.read { $0 }
XCTAssertEqual($0, 1) XCTAssertEqual(val, 1)
} }
func testReadVoid() {
let i = Atomic<Int>(1)
var val = 0
i.read { val = $0 }
XCTAssertEqual(val, 1)
} }
func testWrite() { func testWrite() {
let i = Atomic<Int>(1) let i = Atomic<Int>(1)
let val = i.write { v -> Int in
v += 1
return v
}
XCTAssertEqual(i.read { $0 }, val)
}
func testWriteVoid() {
let i = Atomic<Int>(1)
var val = 0
i.write { i.write {
$0 += 1 $0 += 1
val = $0
} }
XCTAssertEqual(i.snapshot(), 2) XCTAssertEqual(i.read { $0 }, val)
} }
static var allTests = [ static var allTests = [
("testSnapshot", testSnapshot),
("testRead", testRead), ("testRead", testRead),
("testWrite", testWrite) ("testReadVoid", testReadVoid),
("testWrite", testWrite),
("testWriteVoid", testWriteVoid),
] ]
} }

View File

@ -15,66 +15,80 @@ final class BagTests: XCTestCase {
} }
func testAppend() { func testAppend() {
var cabinet = Bag<Fn>() var bag = Bag<Fn>()
cabinet.append { 1 } bag.append { 1 }
cabinet.append { 2 } bag.append { 2 }
XCTAssertEqual(cabinet.count, 2) XCTAssertEqual(bag.count, 2)
} }
func testGet() { func testValueForKey() {
var cabinet = Bag<Fn>() var bag = Bag<Fn>()
let k1 = cabinet.append { 1 } let k1 = bag.append { 1 }
let k2 = cabinet.append { 2 } let k2 = bag.append { 2 }
guard let fn1 = bag.value(for: k1)
let fn1 = cabinet.get(k1),
let fn2 = cabinet.get(k2)
else {
XCTFail()
return
}
XCTAssertEqual(fn1(), 1)
XCTAssertEqual(fn2(), 2)
}
func testDelete() {
var cabinet = Bag<Fn>()
let k1 = cabinet.append { 1 }
let k2 = cabinet.append { 2 }
XCTAssertEqual(cabinet.count, 2)
let fn1 = cabinet.delete(k1)
XCTAssertNotNil(fn1) XCTAssertNotNil(fn1)
let fn2 = cabinet.delete(k2) let fn2 = bag.value(for: k2)
XCTAssertNotNil(fn2) XCTAssertNotNil(fn2)
XCTAssertEqual(cabinet.count, 0) guard let _fn1 = fn1, let _fn2 = fn2 else { return }
XCTAssertEqual(_fn1(), 1)
XCTAssertEqual(_fn2(), 2)
} }
func testClear() { func testRemoveValueForKey() {
var cabinet = Bag<Fn>() var bag = Bag<Fn>()
cabinet.append { 1 } let k1 = bag.append { 1 }
cabinet.append { 2 } let k2 = bag.append { 2 }
XCTAssertEqual(cabinet.count, 2) let fn1 = bag.removeValue(for: k1)
XCTAssertNotNil(fn1)
cabinet.clear() let fn2 = bag.removeValue(for: k2)
XCTAssertEqual(cabinet.count, 0) XCTAssertNotNil(fn2)
guard let _fn1 = fn1, let _fn2 = fn2 else { return }
XCTAssertEqual(_fn1(), 1)
XCTAssertEqual(_fn2(), 2)
}
func testCount() {
var bag = Bag<Fn>()
let k1 = bag.append { 1 }
let k2 = bag.append { 2 }
XCTAssertEqual(bag.count, 2)
bag.removeValue(for: k1)
bag.removeValue(for: k2)
XCTAssertEqual(bag.count, 0)
}
func testRemoveAll() {
var bag = Bag<Fn>()
bag.append { 1 }
bag.append { 2 }
bag.removeAll()
XCTAssertEqual(bag.count, 0)
} }
func testSequence() { func testSequence() {
var cabinet = Bag<Fn>() var bag = Bag<Fn>()
cabinet.append { 0 } bag.append { 0 }
cabinet.append { 1 } bag.append { 1 }
cabinet.append { 2 } bag.append { 2 }
var i = 0 var i = 0
for fn in cabinet { for fn in bag {
XCTAssertEqual(fn(), i) XCTAssertEqual(fn(), i)
i += 1 i += 1
} }
@ -83,9 +97,10 @@ final class BagTests: XCTestCase {
static var allTests = [ static var allTests = [
("testBagKey", testBagKey), ("testBagKey", testBagKey),
("testAppend", testAppend), ("testAppend", testAppend),
("testGet", testGet), ("testValueForKey", testValueForKey),
("testDelete", testDelete), ("testRemoveValueForKey", testRemoveValueForKey),
("testClear", testClear), ("testCount", testCount),
("testRemoveAll", testRemoveAll),
("testSequence", testSequence) ("testSequence", testSequence)
] ]
} }

View File

@ -6,9 +6,8 @@ final class DateTimeTests: XCTestCase {
func testInterval() { func testInterval() {
XCTAssertTrue((-1).second.isNegative) XCTAssertTrue((-1).second.isNegative)
XCTAssertTrue(1.second.isPositive) XCTAssertEqual(1.1.second.abs, 1.1.second)
XCTAssertEqual(1.1.second.magnitude, 1.1.second.nanoseconds) XCTAssertEqual(1.second.negated, (-1).second)
XCTAssertEqual(1.second.opposite, (-1).second)
XCTAssertEqual(7.day.hashValue, 1.week.hashValue) XCTAssertEqual(7.day.hashValue, 1.week.hashValue)
XCTAssertEqual(7.day, 1.week) XCTAssertEqual(7.day, 1.week)
@ -21,10 +20,6 @@ final class DateTimeTests: XCTestCase {
XCTAssertTrue(1.1.second.isLonger(than: 1.0.second)) XCTAssertTrue(1.1.second.isLonger(than: 1.0.second))
XCTAssertTrue(3.days.isShorter(than: 1.week)) XCTAssertTrue(3.days.isShorter(than: 1.week))
XCTAssertEqual(Interval.longest(1.hour, 1.day, 1.week), 1.week)
XCTAssertEqual(Interval.longest([]), nil)
XCTAssertEqual(Interval.shortest(1.hour, 59.minutes, 2999.seconds), 2999.seconds)
XCTAssertEqual(Interval.shortest([]), nil)
XCTAssertEqual(1.second * 60, 1.minute) XCTAssertEqual(1.second * 60, 1.minute)
XCTAssertEqual(59.minutes + 60.seconds, 1.hour) XCTAssertEqual(59.minutes + 60.seconds, 1.hour)
@ -55,18 +50,18 @@ final class DateTimeTests: XCTestCase {
let d = Date(year: 2019, month: 1, day: 1) let d = Date(year: 2019, month: 1, day: 1)
XCTAssertTrue(d.is(.january(1))) XCTAssertTrue(d.is(.january(1)))
XCTAssertEqual(Monthday.january(1).toDateComponents().month, 1) XCTAssertEqual(Monthday.january(1).asDateComponents().month, 1)
XCTAssertEqual(Monthday.february(1).toDateComponents().month, 2) XCTAssertEqual(Monthday.february(1).asDateComponents().month, 2)
XCTAssertEqual(Monthday.march(1).toDateComponents().month, 3) XCTAssertEqual(Monthday.march(1).asDateComponents().month, 3)
XCTAssertEqual(Monthday.april(1).toDateComponents().month, 4) XCTAssertEqual(Monthday.april(1).asDateComponents().month, 4)
XCTAssertEqual(Monthday.may(1).toDateComponents().month, 5) XCTAssertEqual(Monthday.may(1).asDateComponents().month, 5)
XCTAssertEqual(Monthday.june(1).toDateComponents().month, 6) XCTAssertEqual(Monthday.june(1).asDateComponents().month, 6)
XCTAssertEqual(Monthday.july(1).toDateComponents().month, 7) XCTAssertEqual(Monthday.july(1).asDateComponents().month, 7)
XCTAssertEqual(Monthday.august(1).toDateComponents().month, 8) XCTAssertEqual(Monthday.august(1).asDateComponents().month, 8)
XCTAssertEqual(Monthday.september(1).toDateComponents().month, 9) XCTAssertEqual(Monthday.september(1).asDateComponents().month, 9)
XCTAssertEqual(Monthday.october(1).toDateComponents().month, 10) XCTAssertEqual(Monthday.october(1).asDateComponents().month, 10)
XCTAssertEqual(Monthday.november(1).toDateComponents().month, 11) XCTAssertEqual(Monthday.november(1).asDateComponents().month, 11)
XCTAssertEqual(Monthday.december(1).toDateComponents().month, 12) XCTAssertEqual(Monthday.december(1).asDateComponents().month, 12)
} }
func testPeriod() { func testPeriod() {
@ -113,7 +108,7 @@ final class DateTimeTests: XCTestCase {
XCTAssertTrue(i.isAlmostEqual(to: (0.456.second.nanoseconds).nanoseconds, leeway: 0.001.seconds)) XCTAssertTrue(i.isAlmostEqual(to: (0.456.second.nanoseconds).nanoseconds, leeway: 0.001.seconds))
} }
let components = t1?.toDateComponents() let components = t1?.asDateComponents()
XCTAssertEqual(components?.hour, 11) XCTAssertEqual(components?.hour, 11)
XCTAssertEqual(components?.minute, 12) XCTAssertEqual(components?.minute, 12)
XCTAssertEqual(components?.second, 13) XCTAssertEqual(components?.second, 13)
@ -140,7 +135,7 @@ final class DateTimeTests: XCTestCase {
let d = Date(year: 2019, month: 1, day: 1) let d = Date(year: 2019, month: 1, day: 1)
XCTAssertTrue(d.is(.tuesday)) XCTAssertTrue(d.is(.tuesday))
XCTAssertEqual(Weekday.monday.toDateComponents().weekday!, 2) XCTAssertEqual(Weekday.monday.asDateComponents().weekday!, 2)
} }
static var allTests = [ static var allTests = [

View File

@ -5,9 +5,9 @@ import XCTest
final class DeinitObserverTests: XCTestCase { final class DeinitObserverTests: XCTestCase {
func testObserver() { func testObserve() {
var i = 0 var i = 0
var fn = { let fn = {
let obj = NSObject() let obj = NSObject()
DeinitObserver.observe(obj) { DeinitObserver.observe(obj) {
i += 1 i += 1
@ -15,8 +15,11 @@ final class DeinitObserverTests: XCTestCase {
} }
fn() fn()
XCTAssertEqual(i, 1) XCTAssertEqual(i, 1)
}
fn = { func testCancel() {
var i = 0
let fn = {
let obj = NSObject() let obj = NSObject()
let observer = DeinitObserver.observe(obj) { let observer = DeinitObserver.observe(obj) {
i += 1 i += 1
@ -24,11 +27,12 @@ final class DeinitObserverTests: XCTestCase {
observer.cancel() observer.cancel()
} }
fn() fn()
XCTAssertEqual(i, 1) XCTAssertEqual(i, 0)
} }
static var allTests = [ static var allTests = [
("testObserver", testObserver) ("testObserve", testObserve),
("testCancel", testCancel)
] ]
} }

View File

@ -16,11 +16,6 @@ final class ExtensionsTests: XCTestCase {
XCTAssertEqual(i.clampedAdding(1), Int.max) XCTAssertEqual(i.clampedAdding(1), Int.max)
} }
func testClampedSubtracting() {
let i = Int.min
XCTAssertEqual(i.clampedSubtracting(1), Int.min)
}
func testStartOfToday() { func testStartOfToday() {
let components = Date().startOfToday.dateComponents let components = Date().startOfToday.dateComponents
guard guard
@ -39,7 +34,6 @@ final class ExtensionsTests: XCTestCase {
static var allTests = [ static var allTests = [
("testClampedToInt", testClampedToInt), ("testClampedToInt", testClampedToInt),
("testClampedAdding", testClampedAdding), ("testClampedAdding", testClampedAdding),
("testClampedSubtracting", testClampedSubtracting),
("testStartOfToday", testStartOfToday) ("testStartOfToday", testStartOfToday)
] ]
} }

View File

@ -26,7 +26,7 @@ extension Date {
extension Interval { extension Interval {
func isAlmostEqual(to interval: Interval, leeway: Interval) -> Bool { func isAlmostEqual(to interval: Interval, leeway: Interval) -> Bool {
return (interval - self).magnitude <= leeway.magnitude return (interval - self).abs <= leeway.abs
} }
} }

View File

@ -9,8 +9,6 @@ final class PlanTests: XCTestCase {
let intervals = [1.second, 2.hours, 3.days, 4.weeks] let intervals = [1.second, 2.hours, 3.days, 4.weeks]
let s0 = Plan.of(intervals[0], intervals[1], intervals[2], intervals[3]) let s0 = Plan.of(intervals[0], intervals[1], intervals[2], intervals[3])
XCTAssertTrue(s0.makeIterator().isAlmostEqual(to: intervals, leeway: leeway)) XCTAssertTrue(s0.makeIterator().isAlmostEqual(to: intervals, leeway: leeway))
let s1 = Plan.from(intervals)
XCTAssertTrue(s1.makeIterator().isAlmostEqual(to: intervals, leeway: leeway))
let d0 = Date() + intervals[0] let d0 = Date() + intervals[0]
let d1 = d0 + intervals[1] let d1 = d0 + intervals[1]
@ -42,15 +40,15 @@ final class PlanTests: XCTestCase {
func testConcat() { func testConcat() {
let s0: [Interval] = [1.second, 2.minutes, 3.hours] let s0: [Interval] = [1.second, 2.minutes, 3.hours]
let s1: [Interval] = [4.days, 5.weeks] let s1: [Interval] = [4.days, 5.weeks]
let s3 = Plan.from(s0).concat(Plan.from(s1)) let s3 = Plan.of(s0).concat(Plan.of(s1))
let s4 = Plan.from(s0 + s1) let s4 = Plan.of(s0 + s1)
XCTAssertTrue(s3.isAlmostEqual(to: s4, leeway: leeway)) XCTAssertTrue(s3.isAlmostEqual(to: s4, leeway: leeway))
} }
func testMerge() { func testMerge() {
let intervals0: [Interval] = [1.second, 2.minutes, 1.hour] let intervals0: [Interval] = [1.second, 2.minutes, 1.hour]
let intervals1: [Interval] = [2.seconds, 1.minutes, 1.seconds] let intervals1: [Interval] = [2.seconds, 1.minutes, 1.seconds]
let scheudle0 = Plan.from(intervals0).merge(Plan.from(intervals1)) let scheudle0 = Plan.of(intervals0).merge(Plan.of(intervals1))
let scheudle1 = Plan.of(1.second, 1.second, 1.minutes, 1.seconds, 58.seconds, 1.hour) let scheudle1 = Plan.of(1.second, 1.second, 1.minutes, 1.seconds, 58.seconds, 1.hour)
XCTAssertTrue(scheudle0.isAlmostEqual(to: scheudle1, leeway: leeway)) XCTAssertTrue(scheudle0.isAlmostEqual(to: scheudle1, leeway: leeway))
} }