Add Engine.send_file(name, mime, data) and MessageHandler.on_file. Files stream over each open RTC data channel as binary frames — M<json meta> · C<16-byte id><16KB chunk>… · E<id> — and reassemble on the receiver in arrival order (the channel is reliable+ordered). The data channel now distinguishes string (clipboard) from binary (file) messages. Files are direct-only — never relayed, matching prefer-direct. Transport trait gains start(...files) + a default-noop send_file (only RTC implements it). The agent saves received files to ~/Downloads; the apps stub on_file (receive UI is a follow-up). All MessageHandler impls updated. Proven by examples/file_demo.rs: A sends a 50KB blob (4 chunks), B reassembles it to identical bytes over RTC. Follow-ups: file transfer over LAN (same-network without RTC); attach/preview UI; progress + size cap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
42 lines
1.5 KiB
Rust
42 lines
1.5 KiB
Rust
//! Proves AirDrop-style discovery: two engines in DIFFERENT rooms still see each
|
|
//! other in nearby() with friendly names (mDNS, no server).
|
|
//! cargo run --example nearby
|
|
|
|
use std::time::Duration;
|
|
|
|
use tethercore::{Engine, MessageHandler, Message};
|
|
|
|
struct Noop;
|
|
impl MessageHandler for Noop {
|
|
fn on_message(&self, _m: Message) {}
|
|
fn on_status(&self, _c: bool) {}
|
|
fn on_file(&self, _name: String, _mime: String, _data: Vec<u8>) {}
|
|
}
|
|
|
|
fn main() {
|
|
let dead = "http://127.0.0.1:1".to_string();
|
|
let a = Engine::new(dead.clone(), "room-A".into(), "alpha".into(), "macos".into(), "Patrick's MacBook".into(), "acct-X".into());
|
|
let b = Engine::new(dead, "room-B".into(), "bravo".into(), "ios".into(), "Patrick's iPhone".into(), "acct-X".into());
|
|
a.start(Box::new(Noop));
|
|
b.start(Box::new(Noop));
|
|
|
|
eprintln!("browsing mDNS for ~5s…");
|
|
std::thread::sleep(Duration::from_secs(5));
|
|
|
|
println!("A.nearby():");
|
|
for p in a.nearby() {
|
|
println!(" • {} [{}] room={}", p.name, p.source, p.room);
|
|
}
|
|
println!("B.nearby():");
|
|
for p in b.nearby() {
|
|
println!(" • {} [{}] room={}", p.name, p.source, p.room);
|
|
}
|
|
|
|
let ok = a.nearby().iter().any(|p| p.name == "Patrick's iPhone" && p.room == "room-B")
|
|
&& b.nearby().iter().any(|p| p.name == "Patrick's MacBook" && p.room == "room-A");
|
|
println!("{}", if ok { "✅ cross-room discovery by name works" } else { "❌ did not discover" });
|
|
a.stop();
|
|
b.stop();
|
|
if !ok { std::process::exit(1); }
|
|
}
|