Skip to main content

myko/entities/
server.rs

1use std::sync::Arc;
2
3use hyphae::{JoinExt, MapExt, flat};
4
5use crate::{
6    entities::client::GetAllClients,
7    prelude::*,
8    report::{ReportContext, ReportHandler},
9};
10
11#[myko_item]
12pub struct Server {
13    pub version: String,
14    #[searchable]
15    pub address: String,
16    pub port: u16,
17    pub started_at: String, // ISO DateTime
18}
19
20#[myko_query(Server)]
21pub struct GetConnectedServer {}
22
23impl QueryHandler for GetConnectedServer {
24    fn test_entity(ctx: QueryTestCtx<Self>) -> bool {
25        let item_id = ctx.item.id.to_string();
26        let host_id = ctx.query_context.req.host_id.to_string();
27        item_id == host_id
28    }
29}
30
31#[myko_query(Server)]
32pub struct GetPeerServers {}
33
34impl QueryHandler for GetPeerServers {
35    fn test_entity(ctx: QueryTestCtx<Self>) -> bool {
36        let item_id = ctx.item.id.to_string();
37        let host_id = ctx.query_context.req.host_id.to_string();
38        item_id != host_id
39    }
40}
41
42// ─────────────────────────────────────────────────────────────────────────────
43// Manual Report Example: ServerStats
44// ─────────────────────────────────────────────────────────────────────────────
45
46/// Server statistics including connected client count.
47/// This is a manually implemented report demonstrating reactive query subscriptions.
48#[myko_macros::myko_report_output]
49pub struct ServerStatsOutput {
50    /// The server entity (if found)
51    pub server: Option<Arc<Server>>,
52    /// Number of clients connected to this server
53    pub client_count: usize,
54    /// Server uptime in seconds (computed from started_at)
55    pub uptime_seconds: Option<i64>,
56}
57
58/// Report that returns current server statistics.
59///
60/// This report demonstrates:
61/// - Subscribing to multiple queries (`GetConnectedServer`, `GetAllClients`)
62/// - Combining query results reactively
63/// - Computing derived values (uptime)
64///
65/// # Example
66///
67/// ```text
68/// // Client-side usage:
69/// let cell = client.watch_report::<ServerStats, ServerStatsOutput>(ServerStats {});
70///
71/// // `cell` updates whenever server/client state changes.
72/// // Read current value:
73/// let latest = cell.get();
74///
75/// // Or subscribe reactively:
76/// let _guard = cell.subscribe(|signal| {
77///   // handle Signal::Value(Some(ServerStatsOutput { ... }))
78/// });
79/// ```
80#[myko_macros::myko_report(ServerStatsOutput)]
81pub struct ServerStats {}
82
83impl ReportHandler for ServerStats {
84    type Output = ServerStatsOutput;
85
86    fn compute(&self, ctx: ReportContext) -> impl MaterializeDefinite<Arc<Self::Output>> {
87        // Combine server and client cells - emit whenever either changes
88        ctx.query_map(GetConnectedServer {})
89            .entries()
90            .join(&ctx.query_map(GetAllClients {}).entries())
91            .map(flat!(|servers, clients| {
92                let server = servers.first().map(|(_, server)| server.clone());
93
94                // Compute uptime if we have server info
95                let uptime_seconds = server.as_ref().and_then(|s| {
96                    chrono::DateTime::parse_from_rfc3339(&s.started_at)
97                        .ok()
98                        .map(|started| {
99                            let now = chrono::Utc::now();
100                            (now - started.with_timezone(&chrono::Utc)).num_seconds()
101                        })
102                });
103
104                Arc::new(ServerStatsOutput {
105                    server,
106                    client_count: clients.len(),
107                    uptime_seconds,
108                })
109            }))
110    }
111}