1use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt};
20use harmos_domain::Message;
22use harmos_sdk::operations::{CHILD_METHOD, OperationDeclaration, WIRE_METHOD, WireAnswer, WireChildCall, WireCommand};
23
24#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
33#[serde(deny_unknown_fields)]
34pub struct PluginCatalog {
35 pub registrations: Vec<crate::Registration>,
36 pub observers: Vec<crate::inventory::observers::Declaration>,
37 pub operations: Vec<crate::operations::Definition>,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct Publication {
42 pub stream: String,
43 pub schema: u32,
44 pub sequence: u64,
45 pub published_unix_micros: Option<i64>,
46 pub context: Option<String>,
47 pub payload: Vec<u8>,
48}
49
50#[derive(Clone, Debug, PartialEq, Eq)]
53pub enum Frame {
54 Call { id: u64, method: String, params: Vec<u8> },
55 Cancel { id: u64 },
56 Push { method: String, params: Vec<u8> },
57 Answer { id: u64, result: Vec<u8> },
58 Refusal { id: u64, refusal: harmos_domain::Refusal },
59}
60
61impl Frame {
66 pub const REGISTER: &'static str = "harmos.register";
67 pub const EXITING: &'static str = "harmos.exiting";
68 pub const LIMIT: usize = 16 * 1024 * 1024;
69 pub const OVERSIZED: i64 = -32_603;
70
71 pub fn request_encoded_len<O: OperationDeclaration>(request: &O) -> usize {
72 Self::request_payload_encoded_len(O::ID, harmos_domain::Message::encoded_len(request))
73 }
74
75 pub fn request_payload_encoded_len(operation: &str, payload: usize) -> usize {
78 if operation.len() > Self::LIMIT {
79 return usize::MAX;
80 }
81 let payload = binary_payload_growth(payload);
82 let prepare = WireCommand::Prepare { invocation: u64::MAX, operation: operation.to_owned(), payload: Vec::new() };
83 let child = WireChildCall {
84 context: [u8::MAX; 32],
85 generation_context: Some([u8::MAX; 32]),
86 values: Vec::new(),
87 operation: operation.to_owned(),
88 payload: Vec::new(),
89 submit: true,
90 };
91 call_encoded_len(u64::MAX, WIRE_METHOD, prepare.encode().map_or(usize::MAX, |bytes| bytes.len()).saturating_add(payload))
92 .max(call_encoded_len(u64::MAX, CHILD_METHOD, child.encode().map_or(usize::MAX, |bytes| bytes.len()).saturating_add(payload)))
93 }
94
95 pub fn request_fits<O: OperationDeclaration>(request: &O) -> bool {
96 Self::request_encoded_len::<O>(request) <= Self::LIMIT
97 }
98
99 pub fn reply_encoded_len<O: OperationDeclaration>(reply: &O::Response) -> usize {
100 let empty = WireAnswer::Ready { payload: Vec::new() };
101 let payload = empty.encode().map_or(usize::MAX, |bytes| bytes.len()).saturating_add(binary_payload_growth(reply.encoded_len()));
102 answer_encoded_len(u64::MAX, payload)
103 }
104
105 pub fn reply_fits<O: OperationDeclaration>(reply: &O::Response) -> bool {
106 Self::reply_encoded_len::<O>(reply) <= Self::LIMIT
107 }
108
109 pub fn encoded_len(&self) -> usize {
110 match self {
111 Self::Call { id, method, params } => call_encoded_len(*id, method, params.len()),
112 Self::Answer { id, result } => answer_encoded_len(*id, result.len()),
113 Self::Refusal { id, refusal } => refusal_encoded_len(*id, refusal),
114 Self::Cancel { id } => 2 + varint_len(*id) + varint_len(u64::MAX),
115 Self::Push { method, params } => {
116 2usize.saturating_add(binary_bytes_len(method.len())).saturating_add(binary_bytes_len(params.len()))
117 }
118 }
119 }
120
121 pub fn fits(&self) -> bool {
122 self.encoded_len() <= Self::LIMIT
123 }
124
125 #[cfg(feature = "kit")]
126 pub(crate) fn exiting(reason: String) -> Self {
127 Self::Push { method: Self::EXITING.to_owned(), params: reason.encode_to_vec() }
128 }
129
130 pub fn exiting_reason(params: &[u8]) -> Option<String> {
131 String::decode(params).ok()
132 }
133
134 pub async fn diagnostic<R>(input: &mut R) -> std::io::Result<Option<String>>
135 where
136 R: AsyncBufRead + Unpin + Send,
137 {
138 loop {
139 let mut bytes = Vec::new();
140 let read = input.take(Self::LIMIT as u64 + 1).read_until(b'\n', &mut bytes).await?;
141 if read == 0 {
142 return Ok(None);
143 }
144 if bytes.len() > Self::LIMIT {
145 let mut rest = Vec::new();
146 input.read_until(b'\n', &mut rest).await?;
147 continue;
148 }
149
150 let text = String::from_utf8_lossy(&bytes);
151 return Ok(Some(text.trim_end_matches(['\n', '\r']).to_owned()));
152 }
153 }
154}
155
156#[cfg(feature = "kit")]
160pub(crate) fn publication_encoded_len(
161 stream: &str, schema: u32, sequence: u64, payload: usize, published_unix_micros: Option<i64>, context: Option<&str>,
162) -> usize {
163 binary_bytes_len(stream.len())
164 .saturating_add(varint_len(u64::from(schema)))
165 .saturating_add(1 + varint_len(sequence))
166 .saturating_add(1 + published_unix_micros.map_or(0, zigzag_len))
167 .saturating_add(1 + context.map_or(0, |value| binary_bytes_len(value.len())))
168 .saturating_add(binary_bytes_len(payload))
169}
170
171#[cfg(feature = "kit")]
173pub(crate) fn following_encoded_len(payload: usize, published_delta_micros: Option<i64>) -> usize {
174 binary_bytes_len(payload).saturating_add(1 + published_delta_micros.map_or(0, zigzag_len))
175}
176
177pub(crate) fn varint_len(value: u64) -> usize {
178 ((64 - value.leading_zeros()).max(1) as usize).div_ceil(7)
179}
180
181fn binary_payload_growth(bytes: usize) -> usize {
184 bytes.saturating_add(varint_len(bytes as u64).saturating_sub(1))
185}
186
187fn call_encoded_len(id: u64, method: &str, payload_len: usize) -> usize {
188 2usize.saturating_add(varint_len(id)).saturating_add(binary_bytes_len(method.len())).saturating_add(binary_bytes_len(payload_len))
189}
190
191fn answer_encoded_len(id: u64, payload_len: usize) -> usize {
192 2usize.saturating_add(varint_len(id)).saturating_add(binary_bytes_len(payload_len))
193}
194
195fn refusal_encoded_len(id: u64, refusal: &harmos_domain::Refusal) -> usize {
196 let refusal = postcard::experimental::serialized_size(refusal).unwrap_or(usize::MAX);
197 2usize.saturating_add(varint_len(id)).saturating_add(refusal)
198}
199
200#[cfg(feature = "kit")]
201fn zigzag_len(value: i64) -> usize {
202 varint_len(((value as u64) << 1) ^ ((value >> 63) as u64))
203}
204
205fn binary_bytes_len(length: usize) -> usize {
206 varint_len(length as u64).saturating_add(length)
207}
208
209