1//! `/icon`: the picture for one fact. 2 3use ::log::{debug, info, warn}; 4use whiskers_core::wire::{IconReply, IconRequest}; 5use whiskers_ports::{Clock, HasClock, HasEmbedder, HasIcons, HasJudge, pick_icon}; 6 7use super::{Route, sealed}; 8use crate::Response; 9 10/// `POST /icon`: the best admitted picture for the words of a fact, or that none fits. Needs the embedder (to 11/// shortlist by meaning), the judge (to choose), the cache of the picture names' vectors and the clock. 12pub struct PickIcon; 13 14impl sealed::Sealed for PickIcon {} 15 16impl<C: HasEmbedder + HasJudge + HasIcons + HasClock> Route<C> for PickIcon { 17 const PATHS: &'static [&'static str] = &["/icon"]; 18 async fn answer(&self, adapter: &C, _: &str, body: &str) -> Response { 19 let reply = match serde_json::from_str::<IconRequest>(body) { 20 Ok(req) => { 21 debug!("/icon: a fact of {} chars", req.text.len()); 22 let started = adapter.clock().now(); 23 let answer = pick_icon(adapter.embedder(), adapter.judge(), adapter.icon_vectors(), &req.text).await; 24 let ms = adapter.clock().now().get().saturating_sub(started.get()); 25 match answer { 26 Ok(Some(icon)) => { 27 info!("/icon: a picture chosen in {ms} ms"); 28 IconReply::Icon(icon) 29 } 30 Ok(None) => { 31 info!("/icon: none fits ({ms} ms)"); 32 IconReply::NoneFits 33 } 34 Err(e) => { 35 warn!("/icon unavailable after {ms} ms: {e}"); 36 IconReply::Unavailable(e.to_string()) 37 } 38 } 39 } 40 Err(e) => { 41 warn!("/icon: bad request: {}", super::kind_of(&e)); 42 IconReply::Unavailable(format!("bad request: {e}")) 43 } 44 }; 45 Response::of(&reply) 46 } 47}