Skip to main content

datum_agent/dcp/
proto.rs

1use std::collections::HashMap;
2
3use prost::Message as ProstMessage;
4
5/// DCP protocol version implemented by this crate.
6///
7/// Minor-version bumps are additive within one DCP major: new request variants
8/// and payload fields may be added without changing the major. Servers keep
9/// rejecting unknown major versions during `Hello` negotiation.
10pub const DCP_PROTOCOL_VERSION: &str = "0.10.0";
11
12/// DCP major version accepted by this crate.
13pub const DCP_PROTOCOL_MAJOR: u32 = 0;
14
15/// Client role advertised in the initial DCP hello.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)]
17#[repr(i32)]
18pub enum ClientKind {
19    Unspecified = 0,
20    Cli = 1,
21    Tui = 2,
22    Daemon = 3,
23    ClusterNode = 4,
24}
25
26/// Response status for unary DCP requests and hello negotiation.
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)]
28#[repr(i32)]
29pub enum ResponseStatus {
30    Ok = 0,
31    BadRequest = 1,
32    Unauthorized = 2,
33    NotFound = 3,
34    Conflict = 4,
35    Failed = 5,
36    ProtocolMismatch = 6,
37    DeadlineExceeded = 7,
38}
39
40/// Authentication data carried in the hello. Remote deployments are expected to
41/// rely on QUIC mTLS first; this is an optional operator token hook.
42#[derive(Clone, PartialEq, ProstMessage)]
43pub struct Auth {
44    #[prost(string, tag = "1")]
45    pub bearer_token: String,
46}
47
48/// First client-to-server frame on every DCP stream.
49#[derive(Clone, PartialEq, ProstMessage)]
50pub struct Hello {
51    #[prost(string, tag = "1")]
52    pub protocol_version: String,
53    #[prost(string, tag = "2")]
54    pub node_id: String,
55    #[prost(enumeration = "ClientKind", tag = "3")]
56    pub client_kind: i32,
57    #[prost(message, optional, tag = "4")]
58    pub auth: Option<Auth>,
59    #[prost(string, repeated, tag = "5")]
60    pub capabilities: Vec<String>,
61}
62
63impl Hello {
64    #[must_use]
65    pub fn new(node_id: impl Into<String>, client_kind: ClientKind) -> Self {
66        Self {
67            protocol_version: DCP_PROTOCOL_VERSION.to_owned(),
68            node_id: node_id.into(),
69            client_kind: client_kind as i32,
70            auth: None,
71            capabilities: Vec::new(),
72        }
73    }
74}
75
76/// Unary or subscription request.
77#[derive(Clone, PartialEq, ProstMessage)]
78pub struct Request {
79    #[prost(uint64, tag = "1")]
80    pub request_id: u64,
81    #[prost(uint64, tag = "2")]
82    pub deadline_ms: u64,
83    #[prost(
84        oneof = "request::Command",
85        tags = "10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30"
86    )]
87    pub command: Option<request::Command>,
88}
89
90pub mod request {
91    #[derive(Clone, PartialEq, prost::Oneof)]
92    pub enum Command {
93        #[prost(message, tag = "10")]
94        ListJobs(super::ListJobs),
95        #[prost(message, tag = "11")]
96        StartJob(super::StartJob),
97        #[prost(message, tag = "12")]
98        DrainJob(super::DrainJob),
99        #[prost(message, tag = "13")]
100        StopJob(super::StopJob),
101        #[prost(message, tag = "14")]
102        RestartJob(super::RestartJob),
103        #[prost(message, tag = "15")]
104        JobStatus(super::JobStatusRequest),
105        #[prost(message, tag = "16")]
106        SubscribeEvents(super::SubscribeEvents),
107        #[prost(message, tag = "17")]
108        SubscribeMetrics(super::SubscribeMetrics),
109        #[prost(message, tag = "18")]
110        GetConfig(super::GetConfig),
111        #[prost(message, tag = "19")]
112        PutConfig(super::PutConfig),
113        #[prost(message, tag = "20")]
114        ListClusterJobs(super::ListClusterJobs),
115        #[prost(message, tag = "21")]
116        ClusterNodeInfo(super::ClusterNodeInfo),
117        #[prost(message, tag = "22")]
118        SubmitClusterJob(super::SubmitClusterJob),
119        #[prost(message, tag = "23")]
120        RememberClusterAssignment(super::RememberClusterAssignment),
121        #[prost(message, tag = "24")]
122        AllocateShard(super::ShardAllocationRequest),
123        #[prost(message, tag = "25")]
124        RememberShardAllocations(super::RememberShardAllocations),
125        #[prost(message, tag = "26")]
126        GetShardAllocations(super::GetShardAllocations),
127        #[prost(message, tag = "27")]
128        ForwardShardEnvelopes(super::ForwardShardEnvelopes),
129        #[prost(message, tag = "28")]
130        CompleteShardingAsk(super::CompleteShardingAsk),
131        #[prost(message, tag = "29")]
132        OpenShardPipe(super::OpenShardPipe),
133        #[prost(message, tag = "30")]
134        UnsubscribeMetrics(super::UnsubscribeMetrics),
135    }
136}
137
138#[derive(Clone, PartialEq, ProstMessage)]
139pub struct ListJobs {}
140
141/// StartJob uses the v0.9.1 registered-factory model.
142///
143/// Dynamic blueprint upload is intentionally out of scope until cluster
144/// placement work in v0.10.
145#[derive(Clone, PartialEq, ProstMessage)]
146pub struct StartJob {
147    #[prost(string, tag = "1")]
148    pub factory_name: String,
149    #[prost(string, tag = "2")]
150    pub instance_name: String,
151    #[prost(map = "string, string", tag = "3")]
152    pub params: HashMap<String, String>,
153    #[prost(message, optional, tag = "4")]
154    pub cluster: Option<ClusterJobStart>,
155}
156
157#[derive(Clone, PartialEq, ProstMessage)]
158pub struct DrainJob {
159    #[prost(string, tag = "1")]
160    pub name: String,
161    #[prost(bool, tag = "2")]
162    pub cluster: bool,
163}
164
165#[derive(Clone, PartialEq, ProstMessage)]
166pub struct StopJob {
167    #[prost(string, tag = "1")]
168    pub name: String,
169    #[prost(bool, tag = "2")]
170    pub cluster: bool,
171}
172
173#[derive(Clone, PartialEq, ProstMessage)]
174pub struct RestartJob {
175    #[prost(string, tag = "1")]
176    pub name: String,
177    #[prost(bool, tag = "2")]
178    pub cluster: bool,
179}
180
181#[derive(Clone, PartialEq, ProstMessage)]
182pub struct JobStatusRequest {
183    #[prost(string, tag = "1")]
184    pub name: String,
185    #[prost(bool, tag = "2")]
186    pub cluster: bool,
187}
188
189#[derive(Clone, PartialEq, ProstMessage)]
190pub struct SubscribeEvents {
191    #[prost(uint64, tag = "1")]
192    pub buffer: u64,
193}
194
195#[derive(Clone, PartialEq, ProstMessage)]
196pub struct SubscribeMetrics {
197    #[prost(uint64, tag = "1")]
198    pub interval_ms: u64,
199    /// Restrict samples to instrumentation names owned by these jobs. Empty
200    /// means every local stream.
201    #[prost(string, repeated, tag = "2")]
202    pub job_names: Vec<String>,
203    /// Sample only this server's instrumentation registry. Cluster node
204    /// sessions set this to prevent recursive proxying.
205    #[prost(bool, tag = "3")]
206    pub local_only: bool,
207}
208
209#[derive(Clone, PartialEq, ProstMessage)]
210pub struct UnsubscribeMetrics {
211    #[prost(uint64, tag = "1")]
212    pub subscription_id: u64,
213}
214
215#[derive(Clone, PartialEq, ProstMessage)]
216pub struct GetConfig {
217    #[prost(string, tag = "1")]
218    pub key: String,
219}
220
221#[derive(Clone, PartialEq, ProstMessage)]
222pub struct PutConfig {
223    #[prost(string, tag = "1")]
224    pub key: String,
225    #[prost(string, tag = "2")]
226    pub value: String,
227}
228
229/// Aggregate job snapshots across the cluster.
230///
231/// The server fans out to currently known peer node sessions and returns
232/// partial results with per-node errors when a peer is unreachable or the
233/// bounded wait expires.
234#[derive(Clone, PartialEq, ProstMessage)]
235pub struct ListClusterJobs {
236    /// Per-peer fan-out timeout in milliseconds. A zero value uses the server
237    /// default.
238    #[prost(uint64, tag = "1")]
239    pub timeout_ms: u64,
240}
241
242/// Return the local node's cluster membership and DCP session view.
243#[derive(Clone, PartialEq, ProstMessage)]
244pub struct ClusterNodeInfo {
245    /// Snapshot collection timeout in milliseconds. A zero value uses the
246    /// server default.
247    #[prost(uint64, tag = "1")]
248    pub timeout_ms: u64,
249}
250
251/// Submit a registered-factory job to the placement coordinator.
252#[derive(Clone, PartialEq, ProstMessage)]
253pub struct SubmitClusterJob {
254    #[prost(string, tag = "1")]
255    pub factory_name: String,
256    #[prost(string, tag = "2")]
257    pub instance_name: String,
258    #[prost(map = "string, string", tag = "3")]
259    pub params: HashMap<String, String>,
260    #[prost(message, optional, tag = "4")]
261    pub placement: Option<PlacementSpec>,
262    /// Coordinator/placement timeout in milliseconds. A zero value uses the
263    /// server default.
264    #[prost(uint64, tag = "5")]
265    pub timeout_ms: u64,
266}
267
268/// Internal best-effort replication of coordinator assignment state to peers.
269/// A present assignment upserts intent; an absent assignment removes it.
270#[derive(Clone, PartialEq, ProstMessage)]
271pub struct RememberClusterAssignment {
272    #[prost(string, tag = "1")]
273    pub instance_name: String,
274    #[prost(message, optional, tag = "2")]
275    pub assignment: Option<ClusterJobStart>,
276}
277
278/// Request the coordinator allocation for one shard.
279#[derive(Clone, PartialEq, ProstMessage)]
280pub struct ShardAllocationRequest {
281    #[prost(string, tag = "1")]
282    pub type_name: String,
283    #[prost(string, tag = "2")]
284    pub shard_id: String,
285    /// Coordinator timeout in milliseconds. A zero value uses the server
286    /// default.
287    #[prost(uint64, tag = "3")]
288    pub timeout_ms: u64,
289}
290
291/// Coordinator answer for one shard home.
292#[derive(Clone, PartialEq, ProstMessage)]
293pub struct ShardAllocation {
294    #[prost(string, tag = "1")]
295    pub type_name: String,
296    #[prost(string, tag = "2")]
297    pub shard_id: String,
298    #[prost(string, tag = "3")]
299    pub node_id: String,
300    #[prost(uint64, tag = "4")]
301    pub generation: u64,
302}
303
304/// Allocation-table row replicated to peer regions.
305#[derive(Clone, PartialEq, ProstMessage)]
306pub struct ShardAllocationEntry {
307    #[prost(string, tag = "1")]
308    pub type_name: String,
309    #[prost(string, tag = "2")]
310    pub shard_id: String,
311    #[prost(string, tag = "3")]
312    pub node_id: String,
313    #[prost(uint64, tag = "4")]
314    pub generation: u64,
315}
316
317/// Allocation-table snapshot. v0.10 keeps this in memory; persistence and
318/// remember-entities belong to later sharding work packages.
319#[derive(Clone, PartialEq, ProstMessage)]
320pub struct ShardAllocationTable {
321    #[prost(message, repeated, tag = "1")]
322    pub entries: Vec<ShardAllocationEntry>,
323}
324
325/// Best-effort allocation-table replication from the active coordinator.
326#[derive(Clone, PartialEq, ProstMessage)]
327pub struct RememberShardAllocations {
328    #[prost(message, optional, tag = "1")]
329    pub table: Option<ShardAllocationTable>,
330}
331
332/// Ask a peer region for its cached allocation table.
333#[derive(Clone, PartialEq, ProstMessage)]
334pub struct GetShardAllocations {
335    #[prost(string, tag = "1")]
336    pub type_name: String,
337}
338
339/// One serialized entity envelope carried between peer regions.
340#[derive(Clone, PartialEq, ProstMessage)]
341pub struct ShardEnvelopeWire {
342    #[prost(string, tag = "1")]
343    pub entity_id: String,
344    #[prost(string, tag = "2")]
345    pub shard_id: String,
346    #[prost(bytes = "vec", tag = "3")]
347    pub payload: Vec<u8>,
348}
349
350/// Batched peer-region forwarding on the existing C2 session.
351#[derive(Clone, PartialEq, ProstMessage)]
352pub struct ForwardShardEnvelopes {
353    #[prost(string, tag = "1")]
354    pub type_name: String,
355    #[prost(message, repeated, tag = "2")]
356    pub envelopes: Vec<ShardEnvelopeWire>,
357}
358
359/// Per-envelope enqueue result for a forwarded batch.
360#[derive(Clone, PartialEq, ProstMessage)]
361pub struct ShardEnvelopeAck {
362    #[prost(string, tag = "1")]
363    pub entity_id: String,
364    #[prost(string, tag = "2")]
365    pub shard_id: String,
366    #[prost(bool, tag = "3")]
367    pub ok: bool,
368    #[prost(string, tag = "4")]
369    pub message: String,
370}
371
372/// Result for a peer-region forwarding batch.
373#[derive(Clone, PartialEq, ProstMessage)]
374pub struct ShardEnvelopeBatchResult {
375    #[prost(message, repeated, tag = "1")]
376    pub acks: Vec<ShardEnvelopeAck>,
377}
378
379/// Complete an ask whose reply port originated on another region.
380#[derive(Clone, PartialEq, ProstMessage)]
381pub struct CompleteShardingAsk {
382    #[prost(uint64, tag = "1")]
383    pub request_id: u64,
384    #[prost(bool, tag = "2")]
385    pub ok: bool,
386    #[prost(bytes = "vec", tag = "3")]
387    pub payload: Vec<u8>,
388    #[prost(string, tag = "4")]
389    pub message: String,
390}
391
392/// Switch this DCP connection to the sharding hot-path pipe after hello.
393#[derive(Clone, PartialEq, ProstMessage)]
394pub struct OpenShardPipe {}
395
396/// One sharding hot-path pipe frame.
397///
398/// Frames are one-way and ordered per peer connection. The sender batches only
399/// when its local pipe queue already has backlog; idle sends are flushed
400/// immediately so p50 does not pay a linger tax.
401#[derive(Clone, PartialEq, ProstMessage)]
402pub struct ShardPipeFrame {
403    #[prost(message, repeated, tag = "1")]
404    pub forwards: Vec<ForwardShardEnvelopes>,
405    #[prost(message, repeated, tag = "2")]
406    pub replies: Vec<CompleteShardingAsk>,
407}
408
409#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)]
410#[repr(i32)]
411pub enum PlacementStrategy {
412    LeastJobs = 0,
413    Pinned = 1,
414}
415
416#[derive(Clone, PartialEq, ProstMessage)]
417pub struct PlacementSpec {
418    #[prost(string, tag = "1")]
419    pub role_constraint: String,
420    #[prost(enumeration = "PlacementStrategy", tag = "2")]
421    pub strategy: i32,
422    #[prost(string, tag = "3")]
423    pub pinned_node_id: String,
424}
425
426#[derive(Clone, PartialEq, ProstMessage)]
427pub struct ClusterPlacementHistory {
428    #[prost(uint64, tag = "1")]
429    pub generation: u64,
430    #[prost(string, tag = "2")]
431    pub from_node_id: String,
432    #[prost(string, tag = "3")]
433    pub to_node_id: String,
434    #[prost(string, tag = "4")]
435    pub reason: String,
436    #[prost(uint64, tag = "5")]
437    pub timestamp_ms: u64,
438}
439
440/// Cluster metadata carried on coordinator-issued StartJob.
441#[derive(Clone, PartialEq, ProstMessage)]
442pub struct ClusterJobStart {
443    #[prost(string, tag = "1")]
444    pub factory_name: String,
445    #[prost(map = "string, string", tag = "2")]
446    pub params: HashMap<String, String>,
447    #[prost(message, optional, tag = "3")]
448    pub placement: Option<PlacementSpec>,
449    #[prost(string, tag = "4")]
450    pub coordinator_node_id: String,
451    #[prost(string, tag = "5")]
452    pub assigned_node_id: String,
453    #[prost(uint64, tag = "6")]
454    pub placement_generation: u64,
455    #[prost(message, repeated, tag = "7")]
456    pub history: Vec<ClusterPlacementHistory>,
457}
458
459#[derive(Clone, PartialEq, ProstMessage)]
460pub struct Response {
461    #[prost(uint64, tag = "1")]
462    pub request_id: u64,
463    #[prost(enumeration = "ResponseStatus", tag = "2")]
464    pub status: i32,
465    #[prost(string, tag = "3")]
466    pub message: String,
467    #[prost(bytes = "vec", tag = "4")]
468    pub payload: Vec<u8>,
469}
470
471impl Response {
472    #[must_use]
473    pub fn ok(request_id: u64, payload: Vec<u8>) -> Self {
474        Self {
475            request_id,
476            status: ResponseStatus::Ok as i32,
477            message: String::new(),
478            payload,
479        }
480    }
481
482    #[must_use]
483    pub fn error(request_id: u64, status: ResponseStatus, message: impl Into<String>) -> Self {
484        Self {
485            request_id,
486            status: status as i32,
487            message: message.into(),
488            payload: Vec::new(),
489        }
490    }
491
492    #[must_use]
493    pub fn response_status(&self) -> ResponseStatus {
494        ResponseStatus::try_from(self.status).unwrap_or(ResponseStatus::Failed)
495    }
496}
497
498#[derive(Clone, PartialEq, ProstMessage)]
499pub struct JobList {
500    #[prost(message, repeated, tag = "1")]
501    pub jobs: Vec<JobStatus>,
502}
503
504/// Cluster-wide job list response.
505#[derive(Clone, PartialEq, ProstMessage)]
506pub struct ClusterJobList {
507    #[prost(message, repeated, tag = "1")]
508    pub nodes: Vec<ClusterJobNode>,
509    #[prost(message, repeated, tag = "2")]
510    pub errors: Vec<ClusterNodeError>,
511    #[prost(bool, tag = "3")]
512    pub partial: bool,
513}
514
515/// Jobs reported by one cluster node.
516#[derive(Clone, PartialEq, ProstMessage)]
517pub struct ClusterJobNode {
518    #[prost(string, tag = "1")]
519    pub node_id: String,
520    #[prost(string, tag = "2")]
521    pub address: String,
522    #[prost(bool, tag = "3")]
523    pub local: bool,
524    #[prost(message, repeated, tag = "4")]
525    pub jobs: Vec<JobStatus>,
526}
527
528/// Cluster node information response.
529#[derive(Clone, PartialEq, ProstMessage)]
530pub struct ClusterNodeList {
531    #[prost(message, repeated, tag = "1")]
532    pub nodes: Vec<ClusterNodeStatus>,
533    #[prost(message, repeated, tag = "2")]
534    pub errors: Vec<ClusterNodeError>,
535    #[prost(bool, tag = "3")]
536    pub partial: bool,
537}
538
539/// Membership and DCP session state for one node.
540#[derive(Clone, PartialEq, ProstMessage)]
541pub struct ClusterNodeStatus {
542    #[prost(string, tag = "1")]
543    pub node_id: String,
544    #[prost(string, tag = "2")]
545    pub member_state: String,
546    #[prost(string, tag = "3")]
547    pub address: String,
548    #[prost(string, tag = "4")]
549    pub agent_addr: String,
550    #[prost(string, repeated, tag = "5")]
551    pub roles: Vec<String>,
552    #[prost(bool, tag = "6")]
553    pub unreachable: bool,
554    #[prost(bool, tag = "7")]
555    pub local: bool,
556    #[prost(string, tag = "8")]
557    pub session_state: String,
558}
559
560/// Per-node partial-result error for cluster fan-out requests.
561#[derive(Clone, PartialEq, ProstMessage)]
562pub struct ClusterNodeError {
563    #[prost(string, tag = "1")]
564    pub node_id: String,
565    #[prost(string, tag = "2")]
566    pub message: String,
567}
568
569#[derive(Clone, PartialEq, ProstMessage)]
570pub struct ConfigValue {
571    #[prost(string, tag = "1")]
572    pub key: String,
573    #[prost(string, tag = "2")]
574    pub value: String,
575    #[prost(bool, tag = "3")]
576    pub existed: bool,
577}
578
579#[derive(Clone, PartialEq, ProstMessage)]
580pub struct JobStatus {
581    #[prost(string, tag = "1")]
582    pub name: String,
583    #[prost(uint64, tag = "2")]
584    pub job_id: u64,
585    #[prost(string, tag = "3")]
586    pub state: String,
587    #[prost(string, tag = "4")]
588    pub desired_state: String,
589    #[prost(uint64, tag = "5")]
590    pub generation: u64,
591    #[prost(uint64, tag = "6")]
592    pub starts_total: u64,
593    #[prost(uint64, tag = "7")]
594    pub restarts_total: u64,
595    #[prost(uint64, optional, tag = "8")]
596    pub last_start_at_ms: Option<u64>,
597    #[prost(uint64, optional, tag = "9")]
598    pub last_exit_at_ms: Option<u64>,
599    #[prost(string, tag = "10")]
600    pub last_exit_reason: String,
601    #[prost(uint64, optional, tag = "11")]
602    pub backoff_remaining_ms: Option<u64>,
603    #[prost(uint64, optional, tag = "12")]
604    pub drain_remaining_ms: Option<u64>,
605    #[prost(bool, tag = "13")]
606    pub drain_supported: bool,
607    #[prost(uint64, optional, tag = "14")]
608    pub active_streams: Option<u64>,
609    #[prost(bool, tag = "15")]
610    pub cluster_job: bool,
611    #[prost(string, tag = "16")]
612    pub factory_name: String,
613    #[prost(message, optional, tag = "17")]
614    pub placement: Option<PlacementSpec>,
615    #[prost(string, tag = "18")]
616    pub coordinator_node_id: String,
617    #[prost(string, tag = "19")]
618    pub placement_node_id: String,
619    #[prost(uint64, tag = "20")]
620    pub placement_generation: u64,
621    #[prost(message, repeated, tag = "21")]
622    pub placement_history: Vec<ClusterPlacementHistory>,
623    #[prost(map = "string, string", tag = "22")]
624    pub params: HashMap<String, String>,
625}
626
627#[derive(Clone, PartialEq, ProstMessage)]
628pub struct EventFrame {
629    #[prost(uint64, tag = "1")]
630    pub subscription_id: u64,
631    #[prost(message, optional, tag = "2")]
632    pub event: Option<Event>,
633}
634
635#[derive(Clone, PartialEq, ProstMessage)]
636pub struct Event {
637    #[prost(uint64, tag = "1")]
638    pub sequence: u64,
639    #[prost(uint64, tag = "2")]
640    pub timestamp_ms: u64,
641    #[prost(string, tag = "3")]
642    pub name: String,
643    #[prost(uint64, tag = "4")]
644    pub job_id: u64,
645    #[prost(uint64, tag = "5")]
646    pub generation: u64,
647    #[prost(string, tag = "6")]
648    pub kind: String,
649    #[prost(string, tag = "7")]
650    pub detail: String,
651}
652
653#[derive(Clone, PartialEq, ProstMessage)]
654pub struct MetricFrame {
655    #[prost(uint64, tag = "1")]
656    pub subscription_id: u64,
657    #[prost(message, optional, tag = "2")]
658    pub sample: Option<MetricSample>,
659}
660
661#[derive(Clone, PartialEq, ProstMessage)]
662pub struct MetricSample {
663    #[prost(uint64, tag = "1")]
664    pub timestamp_ms: u64,
665    #[prost(message, repeated, tag = "2")]
666    pub streams: Vec<StreamMetric>,
667}
668
669#[derive(Clone, PartialEq, ProstMessage)]
670pub struct StreamMetric {
671    #[prost(uint64, tag = "1")]
672    pub id: u64,
673    #[prost(string, tag = "2")]
674    pub name: String,
675    #[prost(uint64, tag = "3")]
676    pub elements_through: u64,
677    #[prost(uint64, tag = "4")]
678    pub restarts: u64,
679    #[prost(string, tag = "5")]
680    pub state: String,
681    #[prost(uint64, tag = "6")]
682    pub started_at_ms: u64,
683    #[prost(uint64, tag = "7")]
684    pub state_changed_at_ms: u64,
685    #[prost(uint64, optional, tag = "8")]
686    pub finished_at_ms: Option<u64>,
687    #[prost(uint64, tag = "9")]
688    pub uptime_ms: u64,
689}
690
691#[derive(Clone, PartialEq, ProstMessage)]
692pub struct DcpFrame {
693    #[prost(oneof = "dcp_frame::Frame", tags = "1, 2, 3, 4, 5, 6")]
694    pub frame: Option<dcp_frame::Frame>,
695}
696
697impl DcpFrame {
698    #[must_use]
699    pub fn hello(hello: Hello) -> Self {
700        Self {
701            frame: Some(dcp_frame::Frame::Hello(hello)),
702        }
703    }
704
705    #[must_use]
706    pub fn request(request: Request) -> Self {
707        Self {
708            frame: Some(dcp_frame::Frame::Request(request)),
709        }
710    }
711
712    #[must_use]
713    pub fn response(response: Response) -> Self {
714        Self {
715            frame: Some(dcp_frame::Frame::Response(response)),
716        }
717    }
718
719    #[must_use]
720    pub fn event(subscription_id: u64, event: Event) -> Self {
721        Self {
722            frame: Some(dcp_frame::Frame::Event(EventFrame {
723                subscription_id,
724                event: Some(event),
725            })),
726        }
727    }
728
729    #[must_use]
730    pub fn metric(subscription_id: u64, sample: MetricSample) -> Self {
731        Self {
732            frame: Some(dcp_frame::Frame::Metric(MetricFrame {
733                subscription_id,
734                sample: Some(sample),
735            })),
736        }
737    }
738
739    #[must_use]
740    pub fn shard_pipe(pipe: ShardPipeFrame) -> Self {
741        Self {
742            frame: Some(dcp_frame::Frame::ShardPipe(pipe)),
743        }
744    }
745}
746
747pub mod dcp_frame {
748    #[allow(clippy::large_enum_variant)]
749    #[derive(Clone, PartialEq, prost::Oneof)]
750    pub enum Frame {
751        #[prost(message, tag = "1")]
752        Hello(super::Hello),
753        #[prost(message, tag = "2")]
754        Request(super::Request),
755        #[prost(message, tag = "3")]
756        Response(super::Response),
757        #[prost(message, tag = "4")]
758        Event(super::EventFrame),
759        #[prost(message, tag = "5")]
760        Metric(super::MetricFrame),
761        #[prost(message, tag = "6")]
762        ShardPipe(super::ShardPipeFrame),
763    }
764}