use std::fmt;
use sim_kernel::Symbol;
use crate::{DevicePlacement, DeviceProfile, DeviceSite, DeviceSiteLocality, PlacementError};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GlassesAdapterKind {
Reprojector,
Glance,
Mirror,
ControllerIntent,
}
impl GlassesAdapterKind {
pub const fn token(self) -> &'static str {
match self {
Self::Reprojector => "glasses-reprojector",
Self::Glance => "glasses-glance-adapter",
Self::Mirror => "glasses-mirror-adapter",
Self::ControllerIntent => "glasses-controller-intent",
}
}
pub fn surface_codec_id(self) -> Symbol {
let name = match self {
Self::Reprojector => "scene-spatial",
Self::Glance => "scene-glance",
Self::Mirror => "scene-mirror",
Self::ControllerIntent => "intent",
};
Symbol::qualified("codec", name)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GlassesPlacementRequest {
pub route: Symbol,
pub profile: DeviceProfile,
pub encoder_locality: DeviceSiteLocality,
pub adapter: GlassesAdapterKind,
}
impl GlassesPlacementRequest {
pub fn new(
route: Symbol,
profile: DeviceProfile,
encoder_locality: DeviceSiteLocality,
adapter: GlassesAdapterKind,
) -> Self {
Self {
route,
profile,
encoder_locality,
adapter,
}
}
pub fn resolve(self) -> Result<ResolvedGlassesPlacement, PlacementError> {
let codec = self.adapter.surface_codec_id();
let encoder = DeviceSite::new(
route_site_symbol(&self.route, "encoder"),
self.profile.clone(),
codec.clone(),
self.encoder_locality,
);
let adapter = DeviceSite::edge_local(
Symbol::qualified("device/site", self.adapter.token()),
self.profile,
codec,
);
let placement = DevicePlacement::new(encoder, adapter);
placement.validate()?;
Ok(ResolvedGlassesPlacement {
route: self.route,
adapter: self.adapter,
placement,
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResolvedGlassesPlacement {
pub route: Symbol,
pub adapter: GlassesAdapterKind,
pub placement: DevicePlacement,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GlassesPlacementError {
UnknownRoute(Symbol),
InvalidPlacement(PlacementError),
}
impl fmt::Display for GlassesPlacementError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnknownRoute(route) => write!(f, "unknown glasses route: {route}"),
Self::InvalidPlacement(error) => error.fmt(f),
}
}
}
impl std::error::Error for GlassesPlacementError {}
impl From<PlacementError> for GlassesPlacementError {
fn from(error: PlacementError) -> Self {
Self::InvalidPlacement(error)
}
}
pub fn resolve_glasses_placement(
route: Symbol,
profile: DeviceProfile,
) -> Result<ResolvedGlassesPlacement, GlassesPlacementError> {
if route.namespace.as_deref() != Some("device/route") {
return Err(GlassesPlacementError::UnknownRoute(route));
}
let (encoder_locality, adapter) = match route.name.as_ref() {
"direct-linux" | "android-usb" => (
DeviceSiteLocality::HostLocal,
GlassesAdapterKind::Reprojector,
),
"neckband-local" => (
DeviceSiteLocality::EdgeLocal,
GlassesAdapterKind::Reprojector,
),
"neckband-relay" => (DeviceSiteLocality::Remote, GlassesAdapterKind::Reprojector),
"mobile-dock-display" => (DeviceSiteLocality::HostLocal, GlassesAdapterKind::Mirror),
"ble-direct" | "web-bluetooth" => {
(DeviceSiteLocality::HostLocal, GlassesAdapterKind::Glance)
}
"phone-relay" => (DeviceSiteLocality::Remote, GlassesAdapterKind::Glance),
"controller-hid" => (
DeviceSiteLocality::HostLocal,
GlassesAdapterKind::ControllerIntent,
),
_ => return Err(GlassesPlacementError::UnknownRoute(route)),
};
GlassesPlacementRequest::new(route, profile, encoder_locality, adapter)
.resolve()
.map_err(Into::into)
}
fn route_site_symbol(route: &Symbol, role: &str) -> Symbol {
Symbol::qualified("device/site", format!("{}-{role}", route.name))
}