Make diction of docs and api naming more corresponding with official api.
This commit is contained in:
parent
4cd608d123
commit
d4f25b6031
|
@ -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 */,
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!
|
||||||
|
|
|
@ -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,48 +52,54 @@ 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
|
||||||
|
}
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
period = regex.stringByReplacingMatches(in: period, range: NSRange(location: 0, length: period.count), withTemplate: "$")
|
|
||||||
|
|
||||||
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(
|
||||||
|
years: years.clampedAdding(other.years),
|
||||||
months: months.clampedAdding(other.months),
|
months: months.clampedAdding(other.months),
|
||||||
days: days.clampedAdding(other.days),
|
days: days.clampedAdding(other.days),
|
||||||
hours: hours.clampedAdding(other.hours),
|
hours: hours.clampedAdding(other.hours),
|
||||||
|
@ -101,54 +108,57 @@ public struct Period {
|
||||||
nanoseconds: nanoseconds.clampedAdding(other.nanoseconds))
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = [:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = [
|
||||||
|
|
|
@ -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)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue