Skip to main content

appcore_ui/
protocol.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: protocol.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/10/13 00:00:00 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/10/13 00:00:00 by dnettoRaw
8//      ###########      S: 0.1.0-beta.1
9// =============================================================================
10
11//! UI wire V1 composes existing command/query DTOs without changing their contracts.
12use crate::{CommandRequest, QueryRequest};
13use serde::{Deserialize, Serialize};
14use std::fmt;
15/// Explicit version of this UI wire family, independent of Peer RPC.
16pub const UI_PROTOCOL_V1: u16 = 1;
17/// Transport-link state, distinct from application or peer health.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum UiConnectionState {
21    /// The bridge can serve authorized operations.
22    Connected,
23    /// The selected transport is not reachable.
24    Offline,
25    /// The bridge is reachable but its owning infrastructure is degraded.
26    Degraded,
27    /// The surface has been closed and must be recreated explicitly.
28    Closed,
29}
30/// One versioned frame; host identity and credentials are never client fields.
31#[derive(Clone, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct UiRequestV1 {
34    /// Explicit UI wire version.
35    pub version: u16,
36    /// Surface identity bound to the native/transport caller by the adapter.
37    pub surface_id: String,
38    /// Unique in-flight correlation identity.
39    pub request_id: String,
40    /// Monotonic execution budget starting at host admission.
41    pub timeout_ms: u32,
42    /// Requested operation with application-owned opaque content.
43    pub action: UiActionV1,
44}
45/// Versioned actions; the page cannot publish events or grant itself capabilities.
46#[derive(Clone, Serialize, Deserialize)]
47#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
48pub enum UiActionV1 {
49    /// Dispatch the existing V1 side-effect-free query contract.
50    Query {
51        /// Existing Runtime query.
52        query: QueryRequest,
53    },
54    /// Dispatch the existing V1 command contract through its owning handler.
55    Command {
56        /// Existing Runtime command.
57        command: CommandRequest,
58    },
59    /// Subscribe to a declared capability's permitted event topic.
60    Subscribe {
61        /// Capability used to authorize the subscription.
62        capability: String,
63        /// Bounded event topic.
64        topic: String,
65    },
66    /// Pull a bounded event page, acknowledging removal from this UI queue.
67    Poll {
68        /// Subscription identity within this surface generation.
69        subscription_id: String,
70    },
71    /// Release one subscription and all its retained notifications.
72    Unsubscribe {
73        /// Subscription identity.
74        subscription_id: String,
75    },
76    /// Cooperatively cancel one operation; completed effects are not rolled back.
77    Cancel {
78        /// Original in-flight request identity.
79        target_request_id: String,
80    },
81    /// Close this surface, cancelling work and releasing subscription queues.
82    Close,
83}
84impl fmt::Debug for UiRequestV1 {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        f.debug_struct("UiRequestV1")
87            .field("version", &self.version)
88            .field("surface_id", &self.surface_id)
89            .field("request_id", &self.request_id)
90            .field("timeout_ms", &self.timeout_ms)
91            .field("action", &self.action)
92            .finish()
93    }
94}
95impl fmt::Debug for UiActionV1 {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            Self::Query { query } => f
99                .debug_struct("Query")
100                .field("name", &query.query_name)
101                .field("payload", &"REDACTED")
102                .finish(),
103            Self::Command { command } => f
104                .debug_struct("Command")
105                .field("name", &command.command_name)
106                .field("payload", &"REDACTED")
107                .field("idempotency_key", &"REDACTED")
108                .finish(),
109            Self::Subscribe { capability, topic } => f
110                .debug_struct("Subscribe")
111                .field("capability", capability)
112                .field("topic", topic)
113                .finish(),
114            Self::Poll { subscription_id } => f.debug_tuple("Poll").field(subscription_id).finish(),
115            Self::Unsubscribe { subscription_id } => {
116                f.debug_tuple("Unsubscribe").field(subscription_id).finish()
117            }
118            Self::Cancel { target_request_id } => {
119                f.debug_tuple("Cancel").field(target_request_id).finish()
120            }
121            Self::Close => f.write_str("Close"),
122        }
123    }
124}