1
use std::collections::HashMap;
2

            
3
use serde::{Deserialize, Serialize};
4
use zlink::ReplyError;
5

            
6
use crate::{
7
    proto::{WhodStatusUpdate, WhodUserEntry},
8
    server::rwhod::RwhodStatusStore,
9
};
10

            
11
#[zlink::proxy("no.ntnu.pvv.roowho2.rwhod")]
12
pub trait VarlinkRwhodClientProxy {
13
    /// `max_idle_seconds` is the maximum idle time (in seconds) for a user
14
    /// to be included in the response. `None` means no limit (i.e. the old
15
    /// `all: true`). The client is expected to always send an explicit
16
    /// value, defaulting to whatever it considers reasonable if the user
17
    /// didn't ask for a specific limit.
18
    async fn rwho(
19
        &mut self,
20
        max_idle_seconds: Option<i64>,
21
    ) -> zlink::Result<Result<VarlinkRwhoResponse, VarlinkRwhodClientError>>;
22

            
23
    /// See [`VarlinkRwhodClientProxy::rwho`] for the meaning of `max_idle_seconds`.
24
    async fn ruptime(
25
        &mut self,
26
        max_idle_seconds: Option<i64>,
27
    ) -> zlink::Result<Result<VarlinkRuptimeResponse, VarlinkRwhodClientError>>;
28
}
29

            
30
#[derive(Debug, Deserialize)]
31
#[serde(tag = "method", content = "parameters")]
32
pub enum VarlinkRwhodClientRequest {
33
    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Rwho")]
34
    Rwho {
35
        /// Maximum idle time (in seconds) for a user to be included.
36
        /// `None` means no limit (i.e. return all users).
37
        max_idle_seconds: Option<i64>,
38
    },
39

            
40
    #[serde(rename = "no.ntnu.pvv.roowho2.rwhod.Ruptime")]
41
    Ruptime {
42
        /// Maximum idle time (in seconds) for a user to be counted.
43
        /// `None` means no limit (i.e. return all users).
44
        max_idle_seconds: Option<i64>,
45
    },
46
}
47

            
48
#[derive(Debug, Clone, PartialEq, Serialize)]
49
#[serde(untagged)]
50
pub enum VarlinkRwhodClientResponse {
51
    Rwho(VarlinkRwhoResponse),
52
    Ruptime(VarlinkRuptimeResponse),
53
}
54

            
55
pub type VarlinkRwhoResponse = HashMap<String, Vec<WhodUserEntry>>;
56
pub type VarlinkRuptimeResponse = Vec<WhodStatusUpdate>;
57

            
58
#[derive(Debug, Clone, PartialEq, ReplyError)]
59
#[zlink(interface = "no.ntnu.pvv.roowho2.rwhod")]
60
pub enum VarlinkRwhodClientError {
61
    InvalidRequest,
62
    TimedOut,
63
    Disabled,
64
}
65

            
66
pub async fn handle_rwho_request(
67
    whod_status_store: &RwhodStatusStore,
68
    max_idle_time: Option<chrono::TimeDelta>,
69
) -> VarlinkRwhoResponse {
70
    tracing::debug!(?max_idle_time, "Handling Rwho request");
71
    let store = whod_status_store.read().await;
72

            
73
    store
74
        .values()
75
        .filter_map(|status_update| {
76
            let users: Vec<WhodUserEntry> = status_update
77
                .users
78
                .iter()
79
                .filter(|user| max_idle_time.is_none_or(|max| user.idle_time < max))
80
                .cloned()
81
                .collect();
82

            
83
            (!users.is_empty()).then(|| (status_update.hostname.clone(), users))
84
        })
85
        .collect()
86
}
87

            
88
pub async fn handle_ruptime_request(
89
    whod_status_store: &RwhodStatusStore,
90
    max_idle_time: Option<chrono::TimeDelta>,
91
) -> VarlinkRuptimeResponse {
92
    tracing::debug!(?max_idle_time, "Handling Ruptime request");
93
    let store = whod_status_store.read().await;
94

            
95
    store
96
        .values()
97
        .cloned()
98
        .map(|mut status_update| {
99
            if let Some(max_idle_time) = max_idle_time {
100
                status_update
101
                    .users
102
                    .retain(|user| user.idle_time < max_idle_time);
103
            }
104
            status_update
105
        })
106
        .collect()
107
}