1use std::fmt;
4
5use sim_kernel::Symbol;
6
7use crate::{DevicePlacement, DeviceProfile, DeviceSite, DeviceSiteLocality, PlacementError};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum GlassesAdapterKind {
12 Reprojector,
14 Glance,
16 Mirror,
18 ControllerIntent,
20}
21
22impl GlassesAdapterKind {
23 pub const fn token(self) -> &'static str {
25 match self {
26 Self::Reprojector => "glasses-reprojector",
27 Self::Glance => "glasses-glance-adapter",
28 Self::Mirror => "glasses-mirror-adapter",
29 Self::ControllerIntent => "glasses-controller-intent",
30 }
31 }
32
33 pub fn surface_codec_id(self) -> Symbol {
35 let name = match self {
36 Self::Reprojector => "scene-spatial",
37 Self::Glance => "scene-glance",
38 Self::Mirror => "scene-mirror",
39 Self::ControllerIntent => "intent",
40 };
41 Symbol::qualified("codec", name)
42 }
43}
44
45#[derive(Clone, Debug, PartialEq, Eq)]
47pub struct GlassesPlacementRequest {
48 pub route: Symbol,
50 pub profile: DeviceProfile,
52 pub encoder_locality: DeviceSiteLocality,
54 pub adapter: GlassesAdapterKind,
56}
57
58impl GlassesPlacementRequest {
59 pub fn new(
61 route: Symbol,
62 profile: DeviceProfile,
63 encoder_locality: DeviceSiteLocality,
64 adapter: GlassesAdapterKind,
65 ) -> Self {
66 Self {
67 route,
68 profile,
69 encoder_locality,
70 adapter,
71 }
72 }
73
74 pub fn resolve(self) -> Result<ResolvedGlassesPlacement, PlacementError> {
76 let codec = self.adapter.surface_codec_id();
77 let encoder = DeviceSite::new(
78 route_site_symbol(&self.route, "encoder"),
79 self.profile.clone(),
80 codec.clone(),
81 self.encoder_locality,
82 );
83 let adapter = DeviceSite::edge_local(
84 Symbol::qualified("device/site", self.adapter.token()),
85 self.profile,
86 codec,
87 );
88 let placement = DevicePlacement::new(encoder, adapter);
89 placement.validate()?;
90 Ok(ResolvedGlassesPlacement {
91 route: self.route,
92 adapter: self.adapter,
93 placement,
94 })
95 }
96}
97
98#[derive(Clone, Debug, PartialEq, Eq)]
100pub struct ResolvedGlassesPlacement {
101 pub route: Symbol,
103 pub adapter: GlassesAdapterKind,
105 pub placement: DevicePlacement,
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
111pub enum GlassesPlacementError {
112 UnknownRoute(Symbol),
114 InvalidPlacement(PlacementError),
116}
117
118impl fmt::Display for GlassesPlacementError {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 match self {
121 Self::UnknownRoute(route) => write!(f, "unknown glasses route: {route}"),
122 Self::InvalidPlacement(error) => error.fmt(f),
123 }
124 }
125}
126
127impl std::error::Error for GlassesPlacementError {}
128
129impl From<PlacementError> for GlassesPlacementError {
130 fn from(error: PlacementError) -> Self {
131 Self::InvalidPlacement(error)
132 }
133}
134
135pub fn resolve_glasses_placement(
137 route: Symbol,
138 profile: DeviceProfile,
139) -> Result<ResolvedGlassesPlacement, GlassesPlacementError> {
140 if route.namespace.as_deref() != Some("device/route") {
141 return Err(GlassesPlacementError::UnknownRoute(route));
142 }
143 let (encoder_locality, adapter) = match route.name.as_ref() {
144 "direct-linux" | "android-usb" => (
145 DeviceSiteLocality::HostLocal,
146 GlassesAdapterKind::Reprojector,
147 ),
148 "neckband-local" => (
149 DeviceSiteLocality::EdgeLocal,
150 GlassesAdapterKind::Reprojector,
151 ),
152 "neckband-relay" => (DeviceSiteLocality::Remote, GlassesAdapterKind::Reprojector),
153 "mobile-dock-display" => (DeviceSiteLocality::HostLocal, GlassesAdapterKind::Mirror),
154 "ble-direct" | "web-bluetooth" => {
155 (DeviceSiteLocality::HostLocal, GlassesAdapterKind::Glance)
156 }
157 "phone-relay" => (DeviceSiteLocality::Remote, GlassesAdapterKind::Glance),
158 "controller-hid" => (
159 DeviceSiteLocality::HostLocal,
160 GlassesAdapterKind::ControllerIntent,
161 ),
162 _ => return Err(GlassesPlacementError::UnknownRoute(route)),
163 };
164 GlassesPlacementRequest::new(route, profile, encoder_locality, adapter)
165 .resolve()
166 .map_err(Into::into)
167}
168
169fn route_site_symbol(route: &Symbol, role: &str) -> Symbol {
170 Symbol::qualified("device/site", format!("{}-{role}", route.name))
171}