Revision control

Copy as Markdown

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
extension Bool {
/// Convert a bool to its byte equivalent.
func toByte() -> UInt8 {
return self ? 1 : 0
}
}
extension UInt8 {
/// Convert a byte to its Bool equivalen.
func toBool() -> Bool {
return self != 0
}
}
extension Datetime {
init(from components: DateComponents) {
self.init(
year: Int32(components.year ?? 0),
month: UInt32(components.month ?? 0),
day: UInt32(components.day ?? 0),
hour: UInt32(components.hour ?? 0),
minute: UInt32(components.minute ?? 0),
second: UInt32(components.second ?? 0),
nanosecond: UInt32(components.nanosecond ?? 0),
offsetSeconds: Int32(components.timeZone!.secondsFromGMT(for: components.date!))
)
}
}
/// Turn a string into an error, so that it can be thrown as an exception.
///
/// This should only be used in tests.
extension String: Swift.Error {
/// The string itself is the error description.
public var errorDescription: String? { return self }
}
/// Format specifiers for different ISO8601 timestamps
let dateFormatPatterns: [TimeUnit: String] = [
.nanosecond: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
.microsecond: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
.millisecond: "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
.second: "yyyy-MM-dd'T'HH:mm:ssZZZZZ",
.minute: "yyyy-MM-dd'T'HH:mmZZZZZ",
.hour: "yyyy-MM-dd'T'HHZZZZZ",
.day: "yyyy-MM-ddZZZZZ"
]
extension Date {
/// Convenience function to convert ISO8601 string to a Date.
///
/// Note that passing in a `dateString` that has more precision than the `precision` parameter will
/// result in a `nil` value being returned. Passing in a `dateString` with less precision than `precision`
/// will still result in a valid `Date` being returned.
///
/// - parameters:
/// * dateString: The `String` representing the date to convert, i.e.: `2004-12-09T08:03-08:00`
/// * precision: The `TimeUnit` precision to use for selecting the correct format to parse against
static func fromISO8601String(dateString: String, precision: TimeUnit) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormatPatterns[precision]
return dateFormatter.date(from: dateString)
}
/// Convenience function to convert a Date to an ISO8601 string
///
/// - returns: An ISO8601 `String` representing the current value of the `Date` object
func toISO8601String(precision: TimeUnit) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormatPatterns[precision]
return dateFormatter.string(from: self)
}
/// Overloads the operator so that subtraction between two dates results in a TimeInterval representing
/// the difference between them
static func - (lhs: Date, rhs: Date) -> TimeInterval {
return lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate
}
}
/// Helper function to retrieve the application's Application Support directory for persistent file storage.
///
/// - returns: `URL` of the Application Support directory
func getGleanDirectory() -> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory.appendingPathComponent("glean_data")
}
/// Check if the data path provided is valid and writable.
///
/// - parameters
/// * customDataPath: An override for manually setting the data directory.
func canWriteToDatabasePath(_ customDataPath: String) -> Bool {
// Do not allow empty strings.
if customDataPath.isEmpty {
return false
}
// If the file exists we need to ensure we can write to it.
if FileManager.default.fileExists(atPath: customDataPath) {
if !FileManager.default.isWritableFile(atPath: customDataPath) {
return false
}
}
// The database path is valid and writable.
return true
}
/// This struct creates a Boolean with atomic or synchronized access.
///
/// This makes use of synchronization tools from Grand Central Dispatch (GCD)
/// in order to synchronize access.
struct AtomicBoolean {
private var semaphore = DispatchSemaphore(value: 1)
private var val: Bool
var value: Bool {
get {
semaphore.wait()
let tmp = val
semaphore.signal()
return tmp
}
set {
semaphore.wait()
val = newValue
semaphore.signal()
}
}
init(_ initialValue: Bool = false) {
val = initialValue
}
}
/// Get a timestamp in nanos.
///
/// This is a monotonic clock.
func timestampNanos() -> UInt64 {
var info = mach_timebase_info()
guard mach_timebase_info(&info) == KERN_SUCCESS else { return 0 }
let currentTime = mach_absolute_time()
let nanos = currentTime * UInt64(info.numer) / UInt64(info.denom)
return nanos
}
/// Gets a gecko-compatible locale string (e.g. "es-ES")
/// If the locale can't be determined on the system, the value is "und",
/// to indicate "undetermined".
///
/// - returns: a locale string that supports custom injected locale/languages.
func getLocaleTag() -> String {
if NSLocale.current.languageCode == nil {
return "und"
} else {
if NSLocale.current.regionCode == nil {
return NSLocale.current.languageCode!
} else {
return "\(NSLocale.current.languageCode!)-\(NSLocale.current.regionCode!)"
}
}
}
/// Gather information about the running application
struct AppInfo {
/// The application's identifier name
public static var name: String {
return Bundle.main.bundleIdentifier!
}
/// The application's display version string
public static var displayVersion: String {
return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
}
/// The application's build ID
public static var buildId: String {
return Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
}
}