Skip to main content

roowho2_lib/server/
varlink_api.rs

1use anyhow::Context;
2use serde::{Deserialize, Serialize};
3use zlink::{ReplyError, service::MethodReply};
4
5use crate::{
6    proto::{WhodStatusUpdate, WhodUserEntry, finger_protocol::FingerResponse},
7    server::rwhod::RwhodStatusStore,
8};
9
10// Types for 'no.ntnu.pvv.roowho2.rwhod'
11
12#[zlink::proxy("no.ntnu.pvv.roowho2.rwhod")]
13pub trait VarlinkRwhodClientProxy {
14    async fn rwho(
15        &mut self,
16        all: bool,
17    ) -> zlink::Result<Result<VarlinkRwhoResponse, VarlinkRwhodClientError>>;
18
19    async fn ruptime(
20        &mut self,
21    ) -> zlink::Result<Result<VarlinkRuptimeResponse, VarlinkRwhodClientError>>;
22}
23
24#[derive(Debug, Deserialize)]
25#[serde(tag = "method", content = "parameters")]
26pub enum VarlinkRwhodClientRequest {
27    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Rwho")]
28    Rwho {
29        /// Retrieve all users, even those that have been idle for a long time.
30        all: bool,
31    },
32
33    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Ruptime")]
34    Ruptime,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize)]
38#[serde(untagged)]
39pub enum VarlinkRwhodClientResponse {
40    Rwho(VarlinkRwhoResponse),
41    Ruptime(VarlinkRuptimeResponse),
42}
43
44pub type VarlinkRwhoResponse = Vec<(String, WhodUserEntry)>;
45pub type VarlinkRuptimeResponse = Vec<WhodStatusUpdate>;
46
47#[derive(Debug, Clone, PartialEq, ReplyError)]
48#[zlink(interface = "no.ntnu.pvv.roowho2.rwhod")]
49pub enum VarlinkRwhodClientError {
50    InvalidRequest,
51}
52
53// Types for 'no.ntnu.pvv.roowho2.finger'
54
55#[zlink::proxy("no.ntnu.pvv.roowho2.finger")]
56pub trait VarlinkFingerClientProxy {
57    async fn finger(
58        &mut self,
59        user_queries: Vec<String>,
60    ) -> zlink::Result<Result<VarlinkFingerResponse, VarlinkFingerClientError>>;
61}
62
63#[derive(Debug, Deserialize)]
64#[serde(tag = "method", content = "parameters")]
65pub enum VarlinkFingerClientRequest {
66    #[serde(rename = "no.ntnu.pvv.roowho2.finger.Finger")]
67    Finger { user_queries: Vec<String> },
68}
69
70#[derive(Debug, Serialize)]
71#[serde(untagged)]
72pub enum VarlinkFingerClientResponse {
73    Finger(VarlinkFingerResponse),
74}
75
76pub type VarlinkFingerResponse = FingerResponse;
77
78#[derive(Debug, Clone, PartialEq, ReplyError)]
79#[zlink(interface = "no.ntnu.pvv.roowho2.finger")]
80pub enum VarlinkFingerClientError {
81    InvalidRequest,
82}
83
84// --------------------
85
86#[derive(Debug, Deserialize)]
87#[serde(untagged)]
88#[allow(unused)]
89pub enum VarlinkMethod {
90    Rwhod(VarlinkRwhodClientRequest),
91    Finger(VarlinkFingerClientRequest),
92}
93
94#[derive(Debug, Serialize)]
95#[serde(untagged)]
96#[allow(unused)]
97pub enum VarlinkReply {
98    Rwhod(VarlinkRwhodClientResponse),
99    Finger(VarlinkFingerClientResponse),
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize)]
103#[serde(untagged)]
104#[allow(unused)]
105pub enum VarlinkReplyError {
106    Rwhod(VarlinkRwhodClientError),
107    Finger(VarlinkFingerClientError),
108}
109
110#[derive(Debug, Clone)]
111pub struct VarlinkRoowhoo2ClientServer {
112    whod_status_store: RwhodStatusStore,
113}
114
115impl VarlinkRoowhoo2ClientServer {
116    pub fn new(whod_status_store: RwhodStatusStore) -> Self {
117        Self { whod_status_store }
118    }
119}
120
121impl VarlinkRoowhoo2ClientServer {
122    // TODO: handle 'all' parameter
123    async fn handle_rwho_request(&self, _all: bool) -> VarlinkRwhoResponse {
124        let store = self.whod_status_store.read().await;
125
126        let mut all_user_entries = Vec::with_capacity(store.len());
127        for status_update in store.values() {
128            all_user_entries.extend_from_slice(
129                &status_update
130                    .users
131                    .iter()
132                    .map(|user| (status_update.hostname.clone(), user.clone()))
133                    .collect::<Vec<(String, WhodUserEntry)>>(),
134            );
135        }
136
137        all_user_entries
138    }
139
140    async fn handle_ruptime_request(&self) -> VarlinkRuptimeResponse {
141        let store = self.whod_status_store.read().await;
142        store.values().cloned().collect()
143    }
144}
145
146impl zlink::Service for VarlinkRoowhoo2ClientServer {
147    type MethodCall<'de> = VarlinkMethod;
148    type ReplyParams<'se> = VarlinkReply;
149    type ReplyStreamParams = ();
150    type ReplyStream = futures_util::stream::Empty<zlink::Reply<()>>;
151    type ReplyError<'se> = VarlinkReplyError;
152
153    async fn handle<'service, Sock: zlink::connection::Socket>(
154        &'service mut self,
155        call: &'service zlink::Call<Self::MethodCall<'_>>,
156        _conn: &mut zlink::Connection<Sock>,
157    ) -> MethodReply<Self::ReplyParams<'service>, Self::ReplyStream, Self::ReplyError<'service>>
158    {
159        match call.method() {
160            VarlinkMethod::Rwhod(VarlinkRwhodClientRequest::Rwho { all }) => {
161                MethodReply::Single(Some(VarlinkReply::Rwhod(VarlinkRwhodClientResponse::Rwho(
162                    self.handle_rwho_request(*all).await,
163                ))))
164            }
165            VarlinkMethod::Rwhod(VarlinkRwhodClientRequest::Ruptime) => {
166                MethodReply::Single(Some(VarlinkReply::Rwhod(
167                    VarlinkRwhodClientResponse::Ruptime(self.handle_ruptime_request().await),
168                )))
169            }
170            VarlinkMethod::Finger(VarlinkFingerClientRequest::Finger { user_queries: _ }) => {
171                unimplemented!()
172            }
173        }
174    }
175}
176
177pub async fn varlink_client_server_task(
178    socket: zlink::unix::Listener,
179    whod_status_store: RwhodStatusStore,
180) -> anyhow::Result<()> {
181    let service = VarlinkRoowhoo2ClientServer::new(whod_status_store);
182
183    let server = zlink::Server::new(socket, service);
184
185    tracing::info!("Starting Rwhod client API server");
186
187    server
188        .run()
189        .await
190        .context("Rwhod client API server failed")?;
191
192    Ok(())
193}