shellcanvas_adapter_sdk/
wire.rs1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::io;
6use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
7
8pub const MAX_FRAME: usize = 4 * 1024 * 1024;
10#[derive(Debug, Serialize, Deserialize)]
11#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
12pub enum Envelope {
13 Request {
14 v: u8,
15 id: u64,
16 method: String,
17 params: Value,
18 },
19 Cancel {
20 v: u8,
21 id: u64,
22 },
23 Result {
24 v: u8,
25 id: u64,
26 value: Value,
27 },
28 Error {
29 v: u8,
30 id: u64,
31 code: String,
32 message: String,
33 },
34}
35pub fn encode(message: &Envelope) -> io::Result<Vec<u8>> {
36 struct Capped(Vec<u8>);
37 impl io::Write for Capped {
38 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
39 if bytes.len() > MAX_FRAME - self.0.len() {
40 return Err(io::Error::other(
41 "Adapter frame exceeds the stream envelope limit",
42 ));
43 }
44 self.0.extend_from_slice(bytes);
45 Ok(bytes.len())
46 }
47 fn flush(&mut self) -> io::Result<()> {
48 Ok(())
49 }
50 }
51 let mut output = Capped(Vec::new());
52 serde_json::to_writer(&mut output, message).map_err(io::Error::other)?;
53 Ok(output.0)
54}
55pub async fn read_frame(reader: &mut (impl AsyncRead + Unpin)) -> io::Result<Envelope> {
56 read_frame_or_eof(reader)
57 .await?
58 .ok_or_else(|| io::Error::from(io::ErrorKind::UnexpectedEof))
59}
60pub async fn read_frame_or_eof(
62 reader: &mut (impl AsyncRead + Unpin),
63) -> io::Result<Option<Envelope>> {
64 let first = match reader.read_u8().await {
65 Ok(first) => first,
66 Err(error) if error.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
67 Err(error) => return Err(error),
68 };
69 let mut prefix = [first, 0, 0, 0];
70 reader.read_exact(&mut prefix[1..]).await?;
71 let length = u32::from_be_bytes(prefix) as usize;
72 if length == 0 || length > MAX_FRAME {
73 return Err(io::Error::other("Invalid adapter frame length"));
74 }
75 let mut data = vec![0; length];
76 reader.read_exact(&mut data).await?;
77 serde_json::from_slice(&data)
78 .map(Some)
79 .map_err(io::Error::other)
80}
81pub async fn write_frame(writer: &mut (impl AsyncWrite + Unpin), data: &[u8]) -> io::Result<()> {
82 if data.is_empty() || data.len() > MAX_FRAME {
83 return Err(io::Error::other("Invalid adapter frame length"));
84 }
85 writer.write_u32(data.len() as u32).await?;
86 writer.write_all(data).await?;
87 writer.flush().await
88}
89
90#[derive(Clone, Debug, Deserialize, Serialize)]
91#[serde(rename_all = "camelCase", deny_unknown_fields)]
92pub struct ServiceDescriptor {
93 pub id: String,
94 pub version: u32,
95 pub methods: Vec<String>,
96}
97#[derive(Clone, Debug, Deserialize, Serialize)]
98#[serde(deny_unknown_fields)]
99pub struct Initialized {
100 pub protocol: u8,
101 pub services: Vec<ServiceDescriptor>,
102}
103pub fn name(value: &str) -> bool {
105 !value.is_empty()
106 && value.len() <= 200
107 && value.split('.').all(|part| {
108 part.as_bytes().first().is_some_and(u8::is_ascii_alphabetic)
109 && part.bytes().all(|c| c.is_ascii_alphanumeric() || c == b'-')
110 })
111}
112pub fn validate(info: &Initialized) -> bool {
113 let mut ids = std::collections::HashSet::new();
114 let mut methods = std::collections::HashSet::new();
115 info.protocol == 1
116 && info.services.iter().all(|service| {
117 name(&service.id)
118 && service.id != "system"
119 && !service.id.starts_with("system.")
120 && service.version > 0
121 && ids.insert(&service.id)
122 && !service.methods.is_empty()
123 && service.methods.iter().all(|method| {
124 name(method)
125 && method.starts_with(&format!("{}.", service.id))
126 && methods.insert(method)
127 })
128 })
129}