forked from LoopKit/LoopKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGuardrail.swift
More file actions
87 lines (76 loc) · 3.94 KB
/
Guardrail.swift
File metadata and controls
87 lines (76 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//
// Guardrail.swift
// LoopKit
//
// Created by Michael Pangburn on 4/10/20.
// Copyright © 2020 LoopKit Authors. All rights reserved.
//
import HealthKit
public enum SafetyClassification: Equatable {
public enum Threshold: Equatable {
case minimum
case belowRecommended
case aboveRecommended
case maximum
}
case withinRecommendedRange
case outsideRecommendedRange(Threshold)
}
public struct Guardrail<Value: Comparable> {
public let absoluteBounds: ClosedRange<Value>
public let recommendedBounds: ClosedRange<Value>
public let startingSuggestion: Value?
public init(absoluteBounds: ClosedRange<Value>, recommendedBounds: ClosedRange<Value>, startingSuggestion: Value? = nil) {
precondition(absoluteBounds.lowerBound <= recommendedBounds.lowerBound, "The minimum value must be less than or equal to the smallest recommended value")
precondition(absoluteBounds.upperBound >= recommendedBounds.upperBound, "The maximum value must be greater than or equal to the greatest recommended value")
if let startingSuggestion = startingSuggestion {
precondition(recommendedBounds.contains(startingSuggestion))
}
self.absoluteBounds = absoluteBounds
self.recommendedBounds = recommendedBounds
self.startingSuggestion = startingSuggestion
}
public func classification(for value: Value) -> SafetyClassification {
switch value {
case ...absoluteBounds.lowerBound where absoluteBounds.lowerBound != recommendedBounds.lowerBound:
return .outsideRecommendedRange(.minimum)
case ..<recommendedBounds.lowerBound:
return .outsideRecommendedRange(.belowRecommended)
case ...recommendedBounds.upperBound:
return .withinRecommendedRange
case ..<absoluteBounds.upperBound:
return .outsideRecommendedRange(.aboveRecommended)
case absoluteBounds.upperBound...:
return .outsideRecommendedRange(.maximum)
default:
preconditionFailure("Unreachable")
}
}
}
extension Guardrail where Value: Strideable {
public func allValues(stridingBy increment: Value.Stride) -> StrideThrough<Value> {
stride(from: absoluteBounds.lowerBound, through: absoluteBounds.upperBound, by: increment)
}
}
extension Guardrail where Value == HKQuantity {
public init(absoluteBounds: ClosedRange<Double>, recommendedBounds: ClosedRange<Double>, unit: HKUnit, startingSuggestion: Double? = nil) {
let absoluteBoundsWithUnit = HKQuantity(unit: unit, doubleValue: absoluteBounds.lowerBound)...HKQuantity(unit: unit, doubleValue: absoluteBounds.upperBound)
let recommendedBoundsWithUnit = HKQuantity(unit: unit, doubleValue: recommendedBounds.lowerBound)...HKQuantity(unit: unit, doubleValue: recommendedBounds.upperBound)
let startingSuggestionQuantity: HKQuantity?
if let startingSuggestion = startingSuggestion {
startingSuggestionQuantity = HKQuantity(unit: unit, doubleValue: startingSuggestion)
} else {
startingSuggestionQuantity = nil
}
self.init(absoluteBounds: absoluteBoundsWithUnit, recommendedBounds: recommendedBoundsWithUnit, startingSuggestion: startingSuggestionQuantity)
}
/// if fractionDigits is nil, defaults to the unit maxFractionDigits
public func allQuantities(forUnit unit: HKUnit, usingFractionDigits fractionDigits: Int? = nil) -> [HKQuantity] {
allValues(forUnit: unit, usingFractionDigits: fractionDigits ?? unit.maxFractionDigits)
.map { HKQuantity(unit: unit, doubleValue: $0) }
}
/// if fractionDigits is nil, defaults to the unit maxFractionDigits
public func allValues(forUnit unit: HKUnit, usingFractionDigits fractionDigits: Int? = nil) -> [Double] {
unit.allValues(from: absoluteBounds.lowerBound, through: absoluteBounds.upperBound, usingFractionDigits: fractionDigits ?? unit.maxFractionDigits)
}
}