use std::path::Path;
use serde_json::Value;
use super::{OWN, holds};
use crate::channel::material::{self, REMEDY};
use crate::channel::{Channel, entries};
use crate::envelope;
pub struct Routed {
pub down: crate::ui::Channel,
pub sent: Result<(Channel, Value), String>,
}
pub fn route(data_root: &Path, envelope: &Value) -> Routed {
let asked = envelope::workspace(envelope);
let named = asked.as_ref().and_then(|name| {
entries::read_dir(&entries::dir(data_root))
.into_iter()
.find(|held| &held.leaf == name)
});
match (named, asked) {
(Some(entry), _) => seated(entry, envelope),
(None, None) => own(data_root, envelope),
(None, Some(name)) => unresolved(data_root, envelope, &name),
}
}
fn seated(entry: crate::channel::entries::Entry, envelope: &Value) -> Routed {
let carried = if entry.workspace == entry.leaf {
envelope.clone()
} else {
envelope::with_workspace(envelope, &entry.workspace)
};
let opened = entry.open();
Routed {
down: stamp(entry.leaf, Some(entry.workspace), opened.as_ref().ok()),
sent: opened.map(|channel| (channel, carried)),
}
}
fn own(data_root: &Path, envelope: &Value) -> Routed {
let opened = flat(data_root);
Routed {
down: stamp(OWN.to_owned(), None, opened.as_ref().ok()),
sent: opened.map(|channel| (channel, envelope.clone())),
}
}
fn stamp(
name: String,
named_there: Option<String>,
opened: Option<&Channel>,
) -> crate::ui::Channel {
crate::ui::Channel {
name,
named_there,
dials: opened.map(Channel::address),
}
}
fn unresolved(data_root: &Path, envelope: &Value, name: &str) -> Routed {
let selector = envelope
.get(envelope::OP)
.and_then(Value::as_str)
.and_then(crate::verbs::find)
.is_some_and(|verb| !verb.addresses_a_workspace());
if selector {
return Routed {
down: stamp(name.to_owned(), None, None),
sent: Err(format!(
"this box holds no channel named {name:?}: that op takes no \
workspace, so the name can only name a channel to ask. It holds \
{}. {}",
holds::names(data_root),
holds::rename(data_root, name)
)),
};
}
let fell = own(data_root, envelope);
Routed {
down: fell.down,
sent: fell.sent.map_err(|refusal| {
format!(
"no entry here holds {name:?}, so the gesture went to this box's \
own engine (REMOTE §8.2), which answers: {refusal}. This box holds \
{}. {}",
holds::names(data_root),
holds::rename(data_root, name)
)
}),
}
}
pub(super) fn flat(data_root: &Path) -> Result<Channel, String> {
let dir = entries::flat(data_root);
match material::read_dir(&dir)? {
Some(held) if held.address.ends_with(":0") => Err(format!(
"{} names {} — a kernel-chosen port only that engine's own window is \
told; a seat wants a stated address",
dir.join(material::ADDRESS).display(),
held.address
)),
Some(held) => Channel::open(&held),
None => Err(format!(
"no wire provisioned at {}: {REMEDY}",
dir.display()
)),
}
}