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