1use std::path::PathBuf;
5
6use serde::{Deserialize, Deserializer, Serialize};
7use serde_json::{Map, Value};
8use subc_protocol::Principal;
9
10use crate::ops;
11
12pub const ACTIVITY_LEDGER_CAPABILITY: &str = "activity-ledger/v1";
14
15pub const ACTIVITY_KIND_FORBIDDEN: &str = "activity_kind_forbidden";
16pub const ACTIVITY_ACTOR_UNKNOWN: &str = "activity_actor_unknown";
17pub const ACTIVITY_RATE_LIMITED: &str = "activity_rate_limited";
18pub const ACTIVITY_READ_FORBIDDEN: &str = "activity_read_forbidden";
19pub const ACTIVITY_TOO_LARGE: &str = "activity_too_large";
20pub const ACTIVITY_MALFORMED: &str = "activity_malformed";
21pub const ACTIVITY_UNAVAILABLE: &str = "activity_unavailable";
22pub const ACTIVITY_CURSOR_INCARNATION_MISMATCH: &str = "activity_cursor_incarnation_mismatch";
23pub const ACTIVITY_CURSOR_TOO_OLD: &str = "activity_cursor_too_old";
24pub const ACTIVITY_SUBSCRIBER_LAGGED: &str = "activity_subscriber_lagged";
25
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
28pub struct ActivityCursor {
29 pub daemon_incarnation: String,
30 pub seq: u64,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
35#[serde(deny_unknown_fields)]
36pub struct ActivitySubject {
37 #[serde(rename = "type")]
38 pub subject_type: String,
39 pub id: String,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
44pub struct ActivityActor {
45 pub principal: Principal,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub scope_owner: Option<Principal>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub agent_id: Option<String>,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub run_id: Option<String>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub flow_id: Option<String>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub project_root: Option<PathBuf>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
62pub struct ActivityEntry {
63 pub seq: u64,
64 pub at_ms: u64,
65 pub daemon_incarnation: String,
66 pub module: String,
67 pub kind: String,
68 pub summary: String,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub subject: Option<ActivitySubject>,
71 pub details: Map<String, Value>,
72 pub actor: ActivityActor,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77#[serde(deny_unknown_fields)]
78pub struct ActivityRoute {
79 pub channel: u16,
80 pub epoch: u32,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
85#[serde(deny_unknown_fields)]
86pub struct ActivityScope {
87 #[serde(rename = "ref")]
88 pub scope_ref: String,
89 pub epoch: u64,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
94#[serde(rename_all = "snake_case")]
95pub enum ActivityOn {
96 Route(ActivityRoute),
97 Scope(ActivityScope),
98}
99
100macro_rules! activity_body {
104 ($(#[$doc:meta])* $name:ident, $op:path, $wire_name:literal,
105 $(#[$wire_meta:meta])* { $( $(#[$field_meta:meta])* pub $field:ident: $ty:ty, )* }) => {
106 $(#[$doc])*
107 #[derive(Debug, Clone, Serialize, PartialEq)]
108 #[serde(tag = "op", rename = $wire_name)]
109 pub struct $name {
110 $( $(#[$field_meta])* pub $field: $ty, )*
111 }
112
113 impl $name {
114 pub const OP: &'static str = $op;
115 }
116
117 impl<'de> Deserialize<'de> for $name {
118 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
119 where D: Deserializer<'de> {
120 #[derive(Deserialize)]
121 #[serde(tag = "op")]
122 $(#[$wire_meta])*
123 enum Wire {
124 #[serde(rename = $wire_name)]
125 Body { $( $(#[$field_meta])* $field: $ty, )* },
126 }
127 let Wire::Body { $( $field, )* } = Wire::deserialize(deserializer)?;
128 Ok(Self { $( $field, )* })
129 }
130 }
131 };
132}
133
134fn deserialize_details<'de, D>(deserializer: D) -> Result<Option<Map<String, Value>>, D::Error>
135where
136 D: Deserializer<'de>,
137{
138 Map::deserialize(deserializer).map(Some)
139}
140
141activity_body! {
142ActivityRecordRequest, ops::ACTIVITY_RECORD, "activity.record",
145#[serde(deny_unknown_fields)] {
146 pub kind: String,
147 pub summary: String,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
149 pub subject: Option<ActivitySubject>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
151 #[serde(deserialize_with = "deserialize_details")]
152 pub details: Option<Map<String, Value>>,
153 #[serde(default, skip_serializing_if = "Option::is_none")]
154 pub on: Option<ActivityOn>,
155}
156
157}
158
159activity_body! {
160ActivityRecordResponse, ops::ACTIVITY_RECORD, "activity.record", {
162 pub seq: u64,
163}
164
165}
166
167activity_body! {
168ActivityQueryRequest, ops::ACTIVITY_QUERY, "activity.query", {
170 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub agent: Option<String>,
172 #[serde(default, skip_serializing_if = "Option::is_none")]
173 pub project: Option<PathBuf>,
174 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub module: Option<String>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
177 pub kind: Option<String>,
178 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub since_ms: Option<u64>,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
181 pub until_ms: Option<u64>,
182 pub limit: u32,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub cursor: Option<u64>,
185}
186
187}
188
189activity_body! {
190ActivityQueryResponse, ops::ACTIVITY_QUERY, "activity.query", {
193 pub entries: Vec<ActivityEntry>,
194 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub cursor: Option<u64>,
196 pub head: ActivityCursor,
197}
198
199}
200
201activity_body! {
202ActivitySubscribeRequest, ops::ACTIVITY_SUBSCRIBE, "activity.subscribe", {
205 #[serde(default, skip_serializing_if = "Option::is_none")]
206 pub since: Option<ActivityCursor>,
207}
208
209}
210
211activity_body! {
212ActivitySubscribeResponse, ops::ACTIVITY_SUBSCRIBE, "activity.subscribe", {
215 pub entry: ActivityEntry,
216}
217
218}