This commit is contained in:
Hariharan Mudaliar 2026-03-15 23:59:13 +05:30
commit bfb36cb9d1
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
7 changed files with 808 additions and 3 deletions

View file

@ -0,0 +1,184 @@
/*
* Atoll (DynamicIsland)
* Copyright (C) 2024-2026 Atoll Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Foundation
// MARK: - JSON-RPC 2.0 Message Types
/// Incoming JSON-RPC 2.0 request from a client.
struct RPCRequest: Codable {
let jsonrpc: String
let method: String
let params: RPCParams?
let id: String
}
/// Outgoing JSON-RPC 2.0 success response.
struct RPCSuccessResponse: Codable {
let jsonrpc: String
let result: [String: RPCValue]
let id: String
init(result: [String: RPCValue], id: String) {
self.jsonrpc = "2.0"
self.result = result
self.id = id
}
}
/// Outgoing JSON-RPC 2.0 error response.
struct RPCErrorResponse: Codable {
let jsonrpc: String
let error: RPCErrorObject
let id: String?
init(error: RPCErrorObject, id: String?) {
self.jsonrpc = "2.0"
self.error = error
self.id = id
}
}
/// JSON-RPC error object.
struct RPCErrorObject: Codable {
let code: Int
let message: String
let data: String?
init(code: Int, message: String, data: String? = nil) {
self.code = code
self.message = message
self.data = data
}
}
/// Outgoing JSON-RPC 2.0 notification (server → client, no id).
struct RPCNotification: Codable {
let jsonrpc: String
let method: String
let params: [String: RPCValue]
init(method: String, params: [String: RPCValue]) {
self.jsonrpc = "2.0"
self.method = method
self.params = params
}
}
// MARK: - Standard Error Codes
enum RPCErrorCode {
static let parseError = -32700
static let invalidRequest = -32600
static let methodNotFound = -32601
static let invalidParams = -32602
static let internalError = -32603
// Application-specific codes
static let unauthorized = -32001
static let featureDisabled = -32002
static let capacityExceeded = -32003
static let descriptorInvalid = -32004
static let unsupported = -32005
}
// MARK: - Flexible JSON Value
/// Type-erased JSON value for RPC params and results.
enum RPCValue: Codable {
case string(String)
case int(Int)
case double(Double)
case bool(Bool)
case null
case object([String: RPCValue])
case array([RPCValue])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
self = .null
} else if let b = try? container.decode(Bool.self) {
self = .bool(b)
} else if let i = try? container.decode(Int.self) {
self = .int(i)
} else if let d = try? container.decode(Double.self) {
self = .double(d)
} else if let s = try? container.decode(String.self) {
self = .string(s)
} else if let obj = try? container.decode([String: RPCValue].self) {
self = .object(obj)
} else if let arr = try? container.decode([RPCValue].self) {
self = .array(arr)
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported JSON type")
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let s): try container.encode(s)
case .int(let i): try container.encode(i)
case .double(let d): try container.encode(d)
case .bool(let b): try container.encode(b)
case .null: try container.encodeNil()
case .object(let o): try container.encode(o)
case .array(let a): try container.encode(a)
}
}
var stringValue: String? {
if case .string(let s) = self { return s }
return nil
}
var boolValue: Bool? {
if case .bool(let b) = self { return b }
return nil
}
var objectValue: [String: RPCValue]? {
if case .object(let o) = self { return o }
return nil
}
}
// MARK: - Flexible Params
/// JSON-RPC params can be an object or individual values.
struct RPCParams: Codable {
let values: [String: RPCValue]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.values = try container.decode([String: RPCValue].self)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(values)
}
subscript(key: String) -> RPCValue? {
values[key]
}
/// Extract a JSON sub-object as raw Data for use with JSONDecoder.
func jsonData(for key: String) -> Data? {
guard let value = values[key] else { return nil }
return try? JSONEncoder().encode(value)
}
}