What Whiskers' service does with a request, generic over the capabilities it runs on. See README.md.
3#![forbid(unsafe_code)]
Whiskers' service for one household: an adapter, and the routes it has been given.
A new service serves nothing. Routes are given with serve, and a route can be given only
to an adapter that has the capabilities it needs, which the compiler checks. An adapter that holds a
memory hub can be given /memory/sync:
use whiskers_conformance::memory::MemMemoryHub;
use whiskers_ports::HasMemory;
use whiskers_service::{Service, routes::MemorySync};
struct Hubs(MemMemoryHub);
impl HasMemory for Hubs {
type Memory = MemMemoryHub;
fn memory(&self) -> &MemMemoryHub { &self.0 }
}
let _ = Service::new(Hubs(MemMemoryHub::default())).serve(MemorySync);
One that has only a clock cannot. There is no "not configured" answer to forget: the route does not exist.
use whiskers_ports::{HasClock, ManualClock};
use whiskers_service::{Service, routes::MemorySync};
struct OnlyAClock(ManualClock);
impl HasClock for OnlyAClock {
type Clock = ManualClock;
fn clock(&self) -> &ManualClock { &self.0 }
}
// error[E0277]: the trait bound `OnlyAClock: HasMemory` is not satisfied
let _ = Service::new(OnlyAClock(ManualClock::default())).serve(MemorySync);
[Service::complete] gives every route to an adapter that has every capability (a [Backend]).
56impl<C> Service<C> {
A service on adapter that serves no route yet.
Every route, in the order [Service::complete] gives them. Naming it is how code that holds a complete
service (whiskersd) writes its type.
65pub type Complete = Then<PickIcon, Then<Messages, Then<Speak, Then<Usage, Then<Pictures, Then<Journal, Then<ChatSync, Then<HouseholdSync, Then<MemorySync, Then<Rerank, Then<Embed, Then<Check, ()>>>>>>>>>>>>;
67impl<C: Backend> Service<C, Complete> {
The service with every route, which only an adapter with every capability can have. A capability added
to [Backend] is a compile error here until the adapter has it.
70 pub fn complete(adapter: C) -> Self { 71 Service::<C>::new(adapter) 72 .serve(Check) 73 .serve(Embed) 74 .serve(Rerank) 75 .serve(MemorySync) 76 .serve(HouseholdSync) 77 .serve(ChatSync) 78 .serve(Journal) 79 .serve(Pictures) 80 .serve(Usage) 81 .serve(Speak::default()) 82 .serve(Messages) 83 .serve(PickIcon) 84 } 85}
87impl<C, R> Service<C, R> {
Gives the service route. The route's own Route<C> impl names the capabilities it needs, so this
does not compile for an adapter that lacks one of them.
100impl<C, R: Routes<C>> Service<C, R> {
Answers one request. Every outcome is a [Response]; nothing here panics on what a device sends. A
path no given route answers is a 404.
103 pub async fn handle(&self, request: Request) -> Response { 104 let Request { method, path, body } = request; 105 if method != Method::Post { 106 warn!("{method:?} {path} is not a POST"); 107 return Response::empty(405); 108 } 109 let body = match body { 110 Body::Text(text) => text, 111 Body::Unreadable(why) => { 112 warn!("{path}: the request body could not be read (a device that dropped off wifi?): {why}"); 113 return Response::empty(400); 114 } 115 }; 116 if body.len() > MAX_BODY { 117 warn!("{path}: the request body is over the {MAX_BODY} byte limit"); 118 return Response::empty(400); 119 } 120 match self.routes.dispatch(&self.adapter, &path, &body).await { 121 Some(response) => response, 122 None => { 123 warn!("no such route: {path}"); 124 Response::empty(404) 125 } 126 } 127 } 128}