import Foundation struct Config { /// The port where the server runs let port: Int /// The name of the file in the `Resources` folder containing the device authentication token let keyFileName: String /// The seconds to wait for a response from the device let deviceTimeout: Int64 /// The authentication tokens to use for monitoring of the service let authenticationTokens: Set /// The path to the folder where the metric logs are stored /// /// If no path is provided, then a folder `logs` in the resources directory is created /// If the path is relative, then it is assumed relative to the resources directory let logPath: String? func logURL(possiblyRelativeTo resourcesDirectory: URL) -> URL { guard let logPath else { return resourcesDirectory.appendingPathComponent("logs") } guard !logPath.hasPrefix("/") else { return .init(fileURLWithPath: logPath) } return resourcesDirectory.appendingPathComponent(logPath) } } extension Config: Codable { } extension Config { init(loadFrom url: URL) throws { guard FileManager.default.fileExists(atPath: url.path) else { log("No configuration file found at \(url.path)") fatalError("No configuration file found") } let data: Data do { data = try Data(contentsOf: url) } catch { log("Failed to read config data: \(error)") throw error } do { self = try JSONDecoder().decode(Config.self, from: data) } catch { log("Failed to decode config data: \(error)") throw error } } }