| 1 | import Foundation |
| 2 | |
| 3 | struct CRDTProjection: Decodable, Sendable { |
| 4 | var children: [CRDTNode] |
| 5 | var canUndo: Bool |
| 6 | var canRedo: Bool |
| 7 | } |
| 8 | struct CRDTNode: Decodable, Identifiable, Sendable { |
| 9 | let id: String |
| 10 | let type: String |
| 11 | var attrs: [String: String]? |
| 12 | var children: [CRDTNode]? |
| 13 | var runs: [CRDTRun]? |
| 14 | var plainText: String { runs?.map(\.insert).joined() ?? children?.map(\.plainText).joined() ?? "" } |
| 15 | } |
| 16 | struct CRDTRun: Decodable, Sendable { |
| 17 | var insert: String |
| 18 | var marks: [String: JSONValue]? |
| 19 | } |
| 20 | indirect enum JSONValue: Codable, Sendable, Equatable { |
| 21 | case object([String: JSONValue]), array([JSONValue]), string(String), number(Double), bool(Bool), null |
| 22 | init(from decoder: any Decoder) throws { |
| 23 | let c = try decoder.singleValueContainer() |
| 24 | if c.decodeNil() { self = .null } |
| 25 | else if let v = try? c.decode(Bool.self) { self = .bool(v) } |
| 26 | else if let v = try? c.decode(String.self) { self = .string(v) } |
| 27 | else if let v = try? c.decode(Double.self) { self = .number(v) } |
| 28 | else if let v = try? c.decode([String: JSONValue].self) { self = .object(v) } |
| 29 | else { self = .array(try c.decode([JSONValue].self)) } |
| 30 | } |
| 31 | func encode(to encoder: any Encoder) throws { |
| 32 | var c = encoder.singleValueContainer() |
| 33 | switch self { |
| 34 | case .object(let v): try c.encode(v) |
| 35 | case .array(let v): try c.encode(v) |
| 36 | case .string(let v): try c.encode(v) |
| 37 | case .number(let v): try c.encode(v) |
| 38 | case .bool(let v): try c.encode(v) |
| 39 | case .null: try c.encodeNil() |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | |