Files
openclaw/apps/ios/Sources/Services/ExactOpaqueIdentifier.swift
Peter Steinberger 2a02ab6bbe refactor(apps): reuse Watch approval transport models (#105024)
* refactor(apps): reuse watch approval transport models

* fix(ios): satisfy exact identifier lint
2026-07-12 07:08:03 +01:00

106 lines
3.3 KiB
Swift

import Foundation
struct ExactOpaqueIdentifierKey: Hashable, Sendable {
let rawValue: String
private let bytes: [UInt8]
init(_ rawValue: String) {
self.rawValue = rawValue
self.bytes = Array(rawValue.utf8)
}
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.bytes == rhs.bytes
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.bytes)
}
var notificationComponent: String {
let hexDigits = Array("0123456789ABCDEF".utf8)
// Keep dots encoded so gateway and approval components cannot collapse into
// the same notification identifier when their separator moves.
let encoded = self.bytes.flatMap { byte -> [UInt8] in
switch byte {
case 0x30...0x39, 0x41...0x5A, 0x61...0x7A, 0x2D, 0x5F, 0x7E:
[byte]
default:
[0x25, hexDigits[Int(byte >> 4)], hexDigits[Int(byte & 0x0F)]]
}
}
return String(encoded.map { Character(UnicodeScalar($0)) })
}
}
enum ExactOpaqueIdentifier {
static func exact(_ value: String?) -> String? {
guard let value, !value.isEmpty else { return nil }
return value
}
static func key(_ value: String?) -> ExactOpaqueIdentifierKey? {
self.exact(value).map(ExactOpaqueIdentifierKey.init)
}
}
enum ExecApprovalIdentifier {
typealias Key = ExactOpaqueIdentifierKey
static func exact(_ value: String?) -> String? {
guard let value = ExactOpaqueIdentifier.exact(value), value != ".", value != ".." else {
return nil
}
return value
}
static func key(_ value: String?) -> Key? {
self.exact(value).map(Key.init)
}
static func matches(_ lhs: String, _ rhs: String) -> Bool {
guard let lhsKey = self.key(lhs), let rhsKey = self.key(rhs) else { return false }
return lhsKey == rhsKey
}
static func sortsBefore(_ lhs: String, _ rhs: String) -> Bool {
Array(lhs.utf8).lexicographicallyPrecedes(Array(rhs.utf8))
}
}
enum GatewayStableIdentifier {
typealias Key = ExactOpaqueIdentifierKey
static func exact(_ value: String?) -> String? {
ExactOpaqueIdentifier.exact(value)
}
static func key(_ value: String?) -> Key? {
ExactOpaqueIdentifier.key(value)
}
static func matches(_ lhs: String, _ rhs: String) -> Bool {
guard let lhsKey = self.key(lhs), let rhsKey = self.key(rhs) else { return false }
return lhsKey == rhsKey
}
static func matches(_ lhs: String?, _ rhs: String?) -> Bool {
guard let lhsKey = self.key(lhs), let rhsKey = self.key(rhs) else { return false }
return lhsKey == rhsKey
}
static func sortsBefore(_ lhs: String, _ rhs: String) -> Bool {
Array(lhs.utf8).lexicographicallyPrecedes(Array(rhs.utf8))
}
/// Storage attributes can apply Unicode equivalence. Encode the original UTF-8
/// bytes so canonically equivalent gateway owners remain separate persisted keys.
static func storageComponent(_ value: String) -> String? {
guard let value = self.exact(value) else { return nil }
return Data(value.utf8).base64EncodedString()
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
}