use crate::base::{Dependency, Entity, SettingType};
use crate::handler::base::Error;
use crate::ingress::registration::{Registrant, Registrar};
use crate::job::source::Seeder;
use fidl_fuchsia_settings::{
AccessibilityRequestStream, AudioRequestStream, DisplayRequestStream,
DoNotDisturbRequestStream, FactoryResetRequestStream, InputRequestStream, IntlRequestStream,
KeyboardRequestStream, LightRequestStream, NightModeRequestStream, PrivacyRequestStream,
SetupRequestStream,
};
use fuchsia_component::server::{ServiceFsDir, ServiceObjLocal};
use serde::Deserialize;
impl From<Error> for zx::Status {
fn from(error: Error) -> zx::Status {
match error {
Error::UnhandledType(_) => zx::Status::UNAVAILABLE,
_ => zx::Status::INTERNAL,
}
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Interface {
Accessibility,
Audio,
Display(display::InterfaceFlags),
DoNotDisturb,
FactoryReset,
Input,
Intl,
Keyboard,
Light,
NightMode,
Privacy,
Setup,
}
#[derive(Clone, Deserialize, PartialEq, Eq, Hash, Debug)]
pub enum InterfaceSpec {
Accessibility,
Audio,
Display(Vec<display::InterfaceSpec>),
DoNotDisturb,
FactoryReset,
Input,
Intl,
Keyboard,
Light,
NightMode,
Privacy,
Setup,
}
impl From<InterfaceSpec> for Interface {
fn from(spec: InterfaceSpec) -> Self {
match spec {
InterfaceSpec::Audio => Interface::Audio,
InterfaceSpec::Accessibility => Interface::Accessibility,
InterfaceSpec::Display(variants) => Interface::Display(variants.into()),
InterfaceSpec::DoNotDisturb => Interface::DoNotDisturb,
InterfaceSpec::FactoryReset => Interface::FactoryReset,
InterfaceSpec::Input => Interface::Input,
InterfaceSpec::Intl => Interface::Intl,
InterfaceSpec::Keyboard => Interface::Keyboard,
InterfaceSpec::Light => Interface::Light,
InterfaceSpec::NightMode => Interface::NightMode,
InterfaceSpec::Privacy => Interface::Privacy,
InterfaceSpec::Setup => Interface::Setup,
}
}
}
pub mod display {
use bitflags::bitflags;
use serde::Deserialize;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InterfaceFlags: u64 {
const BASE = 1 << 0;
}
}
#[derive(Copy, Clone, Deserialize, PartialEq, Eq, Hash, Debug)]
pub enum InterfaceSpec {
Base,
}
impl From<Vec<InterfaceSpec>> for InterfaceFlags {
fn from(variants: Vec<InterfaceSpec>) -> Self {
variants.into_iter().fold(InterfaceFlags::empty(), |flags, variant| {
flags
| match variant {
InterfaceSpec::Base => InterfaceFlags::BASE,
}
})
}
}
}
pub(crate) type Register =
Box<dyn for<'a> FnOnce(&Seeder, &mut ServiceFsDir<'_, ServiceObjLocal<'a, ()>>)>;
impl Interface {
fn dependencies(self) -> Vec<Dependency> {
match self {
Interface::Accessibility => {
vec![Dependency::Entity(Entity::Handler(SettingType::Accessibility))]
}
Interface::Audio => {
vec![Dependency::Entity(Entity::Handler(SettingType::Audio))]
}
Interface::Display(interfaces) => {
let mut dependencies = Vec::new();
if interfaces.contains(display::InterfaceFlags::BASE) {
dependencies.push(Dependency::Entity(Entity::Handler(SettingType::Display)));
}
if dependencies.is_empty() {
panic!("A valid interface flag must be specified with Interface::Display");
}
dependencies
}
Interface::DoNotDisturb => {
vec![Dependency::Entity(Entity::Handler(SettingType::DoNotDisturb))]
}
Interface::FactoryReset => {
vec![Dependency::Entity(Entity::Handler(SettingType::FactoryReset))]
}
Interface::Input => {
vec![Dependency::Entity(Entity::Handler(SettingType::Input))]
}
Interface::Intl => {
vec![Dependency::Entity(Entity::Handler(SettingType::Intl))]
}
Interface::Keyboard => {
vec![Dependency::Entity(Entity::Handler(SettingType::Keyboard))]
}
Interface::Light => {
vec![Dependency::Entity(Entity::Handler(SettingType::Light))]
}
Interface::NightMode => {
vec![Dependency::Entity(Entity::Handler(SettingType::NightMode))]
}
Interface::Privacy => {
vec![Dependency::Entity(Entity::Handler(SettingType::Privacy))]
}
Interface::Setup => {
vec![Dependency::Entity(Entity::Handler(SettingType::Setup))]
}
}
}
fn registration_fn(self) -> Register {
Box::new(
move |seeder: &Seeder, service_dir: &mut ServiceFsDir<'_, ServiceObjLocal<'_, ()>>| {
match self {
Interface::Audio => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(move |stream: AudioRequestStream| {
seeder.seed(stream);
});
}
Interface::Accessibility => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(
move |stream: AccessibilityRequestStream| {
seeder.seed(stream);
},
);
}
Interface::Display(_) => {
let seeder = seeder.clone();
let _ =
service_dir.add_fidl_service(move |stream: DisplayRequestStream| {
seeder.seed(stream);
});
}
Interface::DoNotDisturb => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(
move |stream: DoNotDisturbRequestStream| {
seeder.seed(stream);
},
);
}
Interface::FactoryReset => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(
move |stream: FactoryResetRequestStream| {
seeder.seed(stream);
},
);
}
Interface::Input => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(move |stream: InputRequestStream| {
seeder.seed(stream);
});
}
Interface::Intl => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(move |stream: IntlRequestStream| {
seeder.seed(stream);
});
}
Interface::Keyboard => {
let seeder = seeder.clone();
let _ =
service_dir.add_fidl_service(move |stream: KeyboardRequestStream| {
seeder.seed(stream);
});
}
Interface::Light => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(move |stream: LightRequestStream| {
seeder.seed(stream);
});
}
Interface::NightMode => {
let seeder = seeder.clone();
let _ =
service_dir.add_fidl_service(move |stream: NightModeRequestStream| {
seeder.seed(stream);
});
}
Interface::Privacy => {
let seeder = seeder.clone();
let _ =
service_dir.add_fidl_service(move |stream: PrivacyRequestStream| {
seeder.seed(stream);
});
}
Interface::Setup => {
let seeder = seeder.clone();
let _ = service_dir.add_fidl_service(move |stream: SetupRequestStream| {
seeder.seed(stream);
});
}
}
},
)
}
pub(crate) fn registrant(self) -> Registrant {
Registrant::new(
format!("{self:?}"),
Registrar::Fidl(self.registration_fn()),
self.dependencies().into_iter().collect(),
)
}
}
#[cfg(test)]
mod tests {
use fidl_fuchsia_settings::PrivacyMarker;
use fuchsia_async as fasync;
use fuchsia_component::server::ServiceFs;
use futures::StreamExt;
use assert_matches::assert_matches;
use crate::base::{Dependency, Entity, SettingType};
use crate::handler::base::{Payload, Request};
use crate::ingress::registration::Registrant;
use crate::job::manager::Manager;
use crate::job::source::Seeder;
use crate::message::base::MessengerType;
use crate::service;
use super::Interface;
#[fuchsia::test(allow_stalls = false)]
async fn test_fidl_seeder_bringup() {
let mut fs = ServiceFs::new_local();
let delegate = service::MessageHub::create_hub();
let job_manager_signature = Manager::spawn(&delegate).await;
let job_seeder = Seeder::new(&delegate, job_manager_signature).await;
let setting_type = SettingType::Privacy;
let registrant: Registrant = Interface::Privacy.registrant();
assert!(registrant
.get_dependencies()
.contains(&Dependency::Entity(Entity::Handler(setting_type))));
let mut rx = delegate
.create(MessengerType::Addressable(service::Address::Handler(setting_type)))
.await
.expect("messenger should be created")
.1;
registrant.register(&job_seeder, &mut fs.root_dir());
let connector = fs.create_protocol_connector().expect("should create connector");
fasync::Task::local(fs.collect()).detach();
let privacy_proxy =
connector.connect_to_protocol::<PrivacyMarker>().expect("should connect to protocol");
fasync::Task::local(async move {
let _ = privacy_proxy.watch().await;
})
.detach();
assert_matches!(rx.next_of::<Payload>().await, Ok((Payload::Request(Request::Listen), _)));
}
}