import Foundation protocol ConfigureActionProtocol { var action: String { get } var fps: Int? { get } var outputs: OutputFormatSetting? { get } var backend: URL? { get } var backendOutput: OutputFormatSetting? { get } var keepLocalFiles: Bool? { get } } protocol RecordActionProtocol { var action: String { get } var x: Int? { get } var y: Int? { get } var width: Int? { get } var height: Int? { get } var preventResize: Bool? { get } var preventMove: Bool? { get } var fps: Int? { get } var backend: URL? { get } var outputs: OutputFormatSetting? { get } var backendOutput: OutputFormatSetting? { get } var keepLocalFiles: Bool? { get } var autoStart: Bool? { get } } // The concrete implementations struct ConfigureAction: ConfigureActionProtocol { let action: String var fps: Int? var outputs: OutputFormatSetting? var backend: URL? var backendOutput: OutputFormatSetting? var keepLocalFiles: Bool? } struct RecordAction: RecordActionProtocol { let action: String var x: Int? var y: Int? var width: Int? var height: Int? var preventResize: Bool? var preventMove: Bool? var fps: Int? var outputs: OutputFormatSetting? var backend: URL? var backendOutput: OutputFormatSetting? var keepLocalFiles: Bool? var autoStart: Bool? } enum CapturaAction { case record(RecordAction) case configure(ConfigureAction) } struct CapturaURLDecoder { static func decodeParams(url: URL) -> CapturaAction? { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let params = components.queryItems else { return nil } var paramsDict = [String: Any]() params.forEach { item in paramsDict[item.name] = item.value } guard let action = paramsDict["action"] as? String else { return nil } switch action { case "configure": var fps = Int(paramsDict["fps"] as? String ?? "") let backend = URL(string: paramsDict["backend"] as? String ?? "") let keepLocalFiles = Bool(paramsDict["keep_local_files"] as? String ?? "") let outputs = OutputFormatSetting(paramsDict["outputs"] as? String ?? "") var backendOutput = OutputFormatSetting(paramsDict["backend_output"] as? String ?? "") if fps != nil { fps = min(10, max(4, fps!)) } if backendOutput == .all { backendOutput = .gifOnly } return .configure(ConfigureAction( action: action, fps: fps, outputs: outputs, backend: backend, backendOutput: backendOutput, keepLocalFiles: keepLocalFiles )) case "record": let x = Int(paramsDict["x"] as? String ?? "") let y = Int(paramsDict["y"] as? String ?? "") let width = Int(paramsDict["width"] as? String ?? "") let height = Int(paramsDict["height"] as? String ?? "") let preventResize = Bool(paramsDict["prevent_resize"] as? String ?? "") let preventMove = Bool(paramsDict["prevent_move"] as? String ?? "") var fps = Int(paramsDict["fps"] as? String ?? "") let backend = URL(string: paramsDict["backend"] as? String ?? "") let keepLocalFiles = Bool(paramsDict["keep_local_files"] as? String ?? "") let outputs = OutputFormatSetting(paramsDict["outputs"] as? String ?? "") var backendOutput = OutputFormatSetting(paramsDict["backend_output"] as? String ?? "") if fps != nil { fps = min(10, max(4, fps!)) } if backendOutput == .all { backendOutput = .gifOnly } return .record(RecordAction( action: action, x: x, y: y, width: width, height: height, preventResize: preventResize, preventMove: preventMove, fps: fps, outputs: outputs, backend: backend, backendOutput: backendOutput, keepLocalFiles: keepLocalFiles )) default: return nil } } }