wai-rs gains the feature floor (the mandatory floor of SPEC 5: PNG, FLAC and zstd, decoded with no system codec library) and quickstart (the floor, and the receipt checks hello and verify run). The wai command line gains four verbs, one module each, named in one table (dx::VERBS): - hello: name, dispatch, rebuild and receipt on a picture made here, the receipt laid out as http-delivery 8 lays receipts beside an object; - create: a new project from one of the examples, whose files the binary holds; tests/cli_dx.rs holds them to wai/examples/ byte for byte; - dev: the relay, the room service and the room page on one loopback origin, as a production front serves them; --check checks each piece through it; - verify: the receipt indexes under a directory, checked offline. The project file a template writes, wai.json, is read in one place (dx/project.rs). wai/examples/ holds video-room, watch-together, messaging, encoder-ingest, package-and-deliver, verify-receipts, apple-app, android-app and remote-desktop, each with a README and a test that runs it through wai create and wai dev. wai/quickstart/ builds the room client and the archive that holds wai, the relay, the room service, the remote host and the packager. Co-Authored-By: Claude Opus 5.5 <[email protected]>
47 lines
2.4 KiB
Swift
47 lines
2.4 KiB
Swift
// A recorded playback session, replayed through the sink call by call: the playlists with their
|
|
// receipts, the objects the player received, its observations, its end. The bundle the sink
|
|
// signs at the end must verify as the stream's owner verifies it, and state what was recorded.
|
|
import Foundation
|
|
import WaiSink
|
|
|
|
enum Replay {
|
|
static func run(path: String) throws -> Bool {
|
|
let doc = try JSONSerialization.jsonObject(with: Data(contentsOf: URL(fileURLWithPath: path))) as! [String: Any]
|
|
let objects = doc["objects"] as! [String: String]
|
|
func bytes(_ name: Any?) -> Data? { (name as? String).map { hex(objects[$0]!) } }
|
|
let sink = try WaiSink(configJSON: doc["config"] as! [String: Any], seed: hex(doc["seed"] as! String), startUs: (doc["start_us"] as! NSNumber).uint64Value)
|
|
var bundle = Data()
|
|
var verified = 0
|
|
for d in doc["calls"] as! [[String: Any]] {
|
|
let t = { (d["t_us"] as! NSNumber).uint64Value }
|
|
switch d["call"] as! String {
|
|
case "playlist":
|
|
_ = try sink.playlist(url: d["url"] as! String, bytes: bytes(d["body"])!, receipt: bytes(d["receipt"]))
|
|
verified += 1
|
|
case "object":
|
|
_ = try sink.object(meta: d["meta"] as! [String: Any], bytes: bytes(d["body"])!)
|
|
verified += 1
|
|
case "event": try sink.event(d["name"] as! String, atUs: t())
|
|
case "observe": _ = try sink.observe(d["obs"] as! [String: Any])
|
|
case "end": bundle = try sink.end(WaiSink.Ended(rawValue: d["ended"] as! String)!, atUs: t())
|
|
default: break
|
|
}
|
|
}
|
|
let out = try WaiSink.verifyBundle(bundle, owner: hex(doc["owner"] as! String), channels: doc["channels"] as! [String])
|
|
let want = doc["outcome"] as! [String: Any]
|
|
let ok = out["outcome"] as? String == "accept" && out["digest"] as? String == want["digest"] as? String
|
|
print("replay: \(verified) playlists and objects through the sink; the bundle \(ok ? "verifies" : "does NOT verify") as its owner verifies it (\(out["outcome"] ?? "?"), \(out["presented_ms"] ?? "?") ms presented)")
|
|
return ok
|
|
}
|
|
|
|
static func hex(_ s: String) -> Data {
|
|
var d = Data(capacity: s.count / 2)
|
|
var i = s.startIndex
|
|
while i < s.endIndex {
|
|
let j = s.index(i, offsetBy: 2)
|
|
d.append(UInt8(s[i..<j], radix: 16)!)
|
|
i = j
|
|
}
|
|
return d
|
|
}
|
|
}
|