roam_codegen/targets/typescript/
encode.rs1use facet_core::{ScalarType, Shape, StructKind};
6use roam_schema::{
7 EnumInfo, ShapeKind, StructInfo, VariantKind, classify_shape, classify_variant, is_bytes,
8};
9
10use super::types::ts_field_access;
11
12pub fn generate_encode_expr(shape: &'static Shape, expr: &str) -> String {
15 if is_bytes(shape) {
17 return format!("encodeBytes({expr})");
18 }
19
20 match classify_shape(shape) {
21 ShapeKind::Scalar(scalar) => encode_scalar_expr(scalar, expr),
22 ShapeKind::List { element } => {
23 let item_encode = generate_encode_expr(element, "item");
24 format!("encodeVec({expr}, (item) => {item_encode})")
25 }
26 ShapeKind::Option { inner } => {
27 let inner_encode = generate_encode_expr(inner, "v");
28 format!("encodeOption({expr}, (v) => {inner_encode})")
29 }
30 ShapeKind::Array { element, .. } => {
31 let item_encode = generate_encode_expr(element, "item");
33 format!("encodeVec({expr}, (item) => {item_encode})")
34 }
35 ShapeKind::Slice { element } => {
36 let item_encode = generate_encode_expr(element, "item");
37 format!("encodeVec({expr}, (item) => {item_encode})")
38 }
39 ShapeKind::Map { key, value } => {
40 let k_enc = generate_encode_expr(key, "k");
42 let v_enc = generate_encode_expr(value, "v");
43 format!("encodeVec(Array.from({expr}.entries()), ([k, v]) => concat({k_enc}, {v_enc}))")
44 }
45 ShapeKind::Set { element } => {
46 let item_encode = generate_encode_expr(element, "item");
47 format!("encodeVec(Array.from({expr}), (item) => {item_encode})")
48 }
49 ShapeKind::Tuple { elements } => {
50 if elements.len() == 2 {
51 let a_enc = generate_encode_expr(elements[0].shape, &format!("{expr}[0]"));
52 let b_enc = generate_encode_expr(elements[1].shape, &format!("{expr}[1]"));
53 format!("concat({a_enc}, {b_enc})")
54 } else if elements.len() == 3 {
55 let a_enc = generate_encode_expr(elements[0].shape, &format!("{expr}[0]"));
56 let b_enc = generate_encode_expr(elements[1].shape, &format!("{expr}[1]"));
57 let c_enc = generate_encode_expr(elements[2].shape, &format!("{expr}[2]"));
58 format!("concat({a_enc}, {b_enc}, {c_enc})")
59 } else if elements.is_empty() {
60 "new Uint8Array(0)".into()
61 } else {
62 let parts: Vec<_> = elements
64 .iter()
65 .enumerate()
66 .map(|(i, p)| generate_encode_expr(p.shape, &format!("{expr}[{i}]")))
67 .collect();
68 format!("concat({})", parts.join(", "))
69 }
70 }
71 ShapeKind::Struct(StructInfo { fields, kind, .. }) => {
72 if fields.is_empty() || kind == StructKind::Unit {
73 "new Uint8Array(0)".into()
74 } else {
75 let parts: Vec<_> = fields
76 .iter()
77 .map(|f| generate_encode_expr(f.shape(), &ts_field_access(expr, f.name)))
78 .collect();
79 format!("concat({})", parts.join(", "))
80 }
81 }
82 ShapeKind::Enum(EnumInfo { variants, .. }) => {
83 let mut cases = String::new();
85 for (i, v) in variants.iter().enumerate() {
86 cases.push_str(&format!(" case '{}': ", v.name));
87 match classify_variant(v) {
88 VariantKind::Unit => {
89 cases.push_str(&format!("return encodeEnumVariant({i});\n"));
90 }
91 VariantKind::Newtype { inner } => {
92 let inner_enc = generate_encode_expr(inner, &format!("{expr}.value"));
93 cases.push_str(&format!(
94 "return concat(encodeEnumVariant({i}), {inner_enc});\n"
95 ));
96 }
97 VariantKind::Tuple { fields } | VariantKind::Struct { fields } => {
98 let field_encs: Vec<_> = fields
99 .iter()
100 .map(|f| {
101 generate_encode_expr(f.shape(), &ts_field_access(expr, f.name))
102 })
103 .collect();
104 cases.push_str(&format!(
105 "return concat(encodeEnumVariant({i}), {});\n",
106 field_encs.join(", ")
107 ));
108 }
109 }
110 }
111 format!(
112 "(() => {{ switch ({expr}.tag) {{\n{cases} default: throw new Error('unknown enum variant'); }} }})()"
113 )
114 }
115 ShapeKind::Tx { .. } | ShapeKind::Rx { .. } => {
116 format!("encodeU64({expr}.channelId)")
119 }
120 ShapeKind::Pointer { pointee } => generate_encode_expr(pointee, expr),
121 ShapeKind::Result { .. } => {
122 "/* Result type encoding not yet implemented */ new Uint8Array(0)".to_string()
123 }
124 ShapeKind::TupleStruct { fields } => {
125 let field_encodes: Vec<String> = fields
126 .iter()
127 .enumerate()
128 .map(|(i, f)| generate_encode_expr(f.shape(), &format!("{expr}[{i}]")))
129 .collect();
130 format!("concat({})", field_encodes.join(", "))
131 }
132 ShapeKind::Opaque => "/* unsupported type */ new Uint8Array(0)".to_string(),
133 }
134}
135
136fn encode_scalar_expr(scalar: ScalarType, expr: &str) -> String {
138 match scalar {
139 ScalarType::Bool => format!("encodeBool({expr})"),
140 ScalarType::U8 => format!("encodeU8({expr})"),
141 ScalarType::I8 => format!("encodeI8({expr})"),
142 ScalarType::U16 => format!("encodeU16({expr})"),
143 ScalarType::I16 => format!("encodeI16({expr})"),
144 ScalarType::U32 => format!("encodeU32({expr})"),
145 ScalarType::I32 => format!("encodeI32({expr})"),
146 ScalarType::U64 | ScalarType::USize => format!("encodeU64({expr})"),
147 ScalarType::I64 | ScalarType::ISize => format!("encodeI64({expr})"),
148 ScalarType::U128 => format!("encodeU64({expr})"), ScalarType::I128 => format!("encodeI64({expr})"), ScalarType::F32 => format!("encodeF32({expr})"),
151 ScalarType::F64 => format!("encodeF64({expr})"),
152 ScalarType::Char | ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
153 format!("encodeString({expr})")
154 }
155 ScalarType::Unit => "new Uint8Array(0)".into(),
156 _ => "/* unsupported scalar */ new Uint8Array(0)".to_string(),
157 }
158}
159
160pub fn generate_encode_fn_inline(shape: &'static Shape) -> String {
162 if is_bytes(shape) {
164 return "(v: Uint8Array) => v".into();
165 }
166
167 match classify_shape(shape) {
168 ShapeKind::Scalar(scalar) => encode_scalar_fn_inline(scalar),
169 ShapeKind::Struct(StructInfo { fields, .. }) => {
170 if fields.is_empty() {
171 return "(v: any) => new Uint8Array(0)".into();
172 }
173 let parts: Vec<_> = fields
174 .iter()
175 .map(|f| generate_encode_expr(f.shape(), &format!("v.{}", f.name)))
176 .collect();
177 if parts.len() == 1 {
178 format!("(v: any) => {}", parts[0])
179 } else {
180 format!("(v: any) => concat({})", parts.join(", "))
181 }
182 }
183 _ => {
184 let encode_expr = generate_encode_expr(shape, "v");
186 format!("(v: any) => {encode_expr}")
187 }
188 }
189}
190
191fn encode_scalar_fn_inline(scalar: ScalarType) -> String {
193 match scalar {
194 ScalarType::Bool => "(v: boolean) => encodeBool(v)".into(),
195 ScalarType::U8 => "(v: number) => encodeU8(v)".into(),
196 ScalarType::I8 => "(v: number) => encodeI8(v)".into(),
197 ScalarType::U16 => "(v: number) => encodeU16(v)".into(),
198 ScalarType::I16 => "(v: number) => encodeI16(v)".into(),
199 ScalarType::U32 => "(v: number) => encodeU32(v)".into(),
200 ScalarType::I32 => "(v: number) => encodeI32(v)".into(),
201 ScalarType::U64 | ScalarType::USize => "(v: bigint) => encodeU64(v)".into(),
202 ScalarType::I64 | ScalarType::ISize => "(v: bigint) => encodeI64(v)".into(),
203 ScalarType::F32 => "(v: number) => encodeF32(v)".into(),
204 ScalarType::F64 => "(v: number) => encodeF64(v)".into(),
205 ScalarType::Char | ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
206 "(v: string) => encodeString(v)".into()
207 }
208 ScalarType::Unit => "(v: void) => new Uint8Array(0)".into(),
209 _ => "(v: any) => new Uint8Array(0)".into(),
210 }
211}