wire.rsannotatedwire.rssource106 lines · 3.9 KB · raw
1//! What the tablet and the service say to each other on the Jev-backed routes (`/check`, `/embed`,
2//! `/rerank`). It lives in the core, not in the guard, because both ends need it and the service
3//! must be able to build for a Worker, which the guard's HTTP client cannot.
4
5use serde::{Deserialize, Serialize};
6
7use crate::profile::Age;
8use crate::types::{Direction, Verdict};
9
10/// What the tablet sends the guard service: one message, which way it is going, and the age of
11/// the child it is judged for. There is no name on the wire: the service does not need one.
12#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub struct CheckRequest {
14    pub direction: Direction,
15    /// Optional; absent, the service judges for the youngest supported age, the strictest. An age outside the supported range does not parse, and so is refused.
16    #[serde(default)]
17    pub age: Option<Age>,
18    pub text: String,
19}
20
21impl CheckRequest {
22    pub fn age(&self) -> Age {
23        self.age.unwrap_or(Age::YOUNGEST)
24    }
25}
26
27/// The service answers a verdict, or says it could not decide. "Could not
28/// decide" is not an `Allow`: the client turns it into a `GuardError`.
29#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30pub enum CheckReply {
31    Verdict(Verdict),
32    Unavailable(String),
33}
34
35/// Texts to embed.
36#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
37pub struct EmbedRequest {
38    pub texts: Vec<String>,
39}
40
41#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
42pub enum EmbedReply {
43    Vectors(Vec<Vec<f32>>),
44    Unavailable(String),
45}
46
47/// A message and the shortlist of memories to order for it.
48#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
49pub struct RankRequest {
50    pub query: String,
51    pub candidates: Vec<String>,
52}
53
54#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
55pub enum RankReply {
56    Probabilities(Vec<f32>),
57    Unavailable(String),
58}
59
60/// The text of one fact, to be given a picture. A fact is what Whiskers remembers about the child, so the
61/// request carries no age and no name: the picture is chosen for the words alone.
62#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
63pub struct IconRequest {
64    pub text: String,
65}
66
67/// The picture chosen for a fact, or that none of the admitted ones fits, or that it could not be decided.
68/// "Could not decide" is not "none fits": the first is tried again later, the second is an answer.
69#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
70pub enum IconReply {
71    Icon(whiskers_icons::IconId),
72    NoneFits,
73    Unavailable(String),
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn an_icon_reply_names_an_admitted_icon_or_it_does_not_parse() {
82        let r: IconReply = serde_json::from_str(r#"{"Icon":"cat"}"#).unwrap();
83        assert_eq!(r, IconReply::Icon(whiskers_icons::IconId::new("cat").unwrap()));
84        assert_eq!(serde_json::from_str::<IconReply>(r#""NoneFits""#).unwrap(), IconReply::NoneFits);
85        assert!(serde_json::from_str::<IconReply>(r#"{"Icon":"beer"}"#).is_err());
86        assert_eq!(serde_json::to_string(&IconRequest { text: "x".into() }).unwrap(), r#"{"text":"x"}"#);
87    }
88
89    #[test]
90    fn a_request_without_an_age_is_judged_for_the_youngest() {
91        let r: CheckRequest = serde_json::from_str(r#"{"direction":"FromChild","text":"hi"}"#).unwrap();
92        assert_eq!(r.age(), Age::YOUNGEST);
93    }
94
95    #[test]
96    fn an_age_outside_the_range_does_not_parse() {
97        assert!(serde_json::from_str::<CheckRequest>(r#"{"direction":"FromChild","age":2,"text":"hi"}"#).is_err());
98        assert!(serde_json::from_str::<CheckRequest>(r#"{"direction":"FromChild","age":200,"text":"hi"}"#).is_err());
99    }
100
101    #[test]
102    fn the_request_carries_the_age_and_nothing_that_could_be_a_name() {
103        let r = CheckRequest { direction: Direction::ToChild, age: Some(Age::new(6).unwrap()), text: "hi".into() };
104        assert_eq!(serde_json::to_string(&r).unwrap(), r#"{"direction":"ToChild","age":6,"text":"hi"}"#);
105    }
106}