1use crate::encode::{encode, hash_image};
110use crate::error::Result;
111use crate::interop::{project_node_with, redacted_key, secret_table, Projected};
112use crate::ir::{AnnotationKind, InfinityModule, ProvValue};
113
114const WIRE_VARINT: u8 = 0;
119const WIRE_DOUBLE: u8 = 1;
120const WIRE_LEN: u8 = 2;
121
122fn varint(out: &mut Vec<u8>, mut value: u64) {
123 while value >= 0x80 {
124 out.push((value as u8) | 0x80);
125 value >>= 7;
126 }
127 out.push(value as u8);
128}
129
130fn tag(out: &mut Vec<u8>, field: u32, wire: u8) {
131 varint(out, (u64::from(field) << 3) | u64::from(wire));
132}
133
134fn uint(out: &mut Vec<u8>, field: u32, value: u64) {
135 tag(out, field, WIRE_VARINT);
136 varint(out, value);
137}
138
139fn sint(out: &mut Vec<u8>, field: u32, value: i64) {
140 tag(out, field, WIRE_VARINT);
141 varint(out, ((value << 1) ^ (value >> 63)) as u64);
142}
143
144fn boolean(out: &mut Vec<u8>, field: u32, value: bool) {
145 tag(out, field, WIRE_VARINT);
146 varint(out, u64::from(value));
147}
148
149fn double(out: &mut Vec<u8>, field: u32, value: f64) {
150 tag(out, field, WIRE_DOUBLE);
151 out.extend_from_slice(&value.to_bits().to_le_bytes());
152}
153
154fn bytes(out: &mut Vec<u8>, field: u32, value: &[u8]) {
155 tag(out, field, WIRE_LEN);
156 varint(out, value.len() as u64);
157 out.extend_from_slice(value);
158}
159
160fn string(out: &mut Vec<u8>, field: u32, value: &str) {
161 bytes(out, field, value.as_bytes());
162}
163
164fn message(out: &mut Vec<u8>, field: u32, body: &[u8]) {
165 bytes(out, field, body);
166}
167
168fn encode_value(value: &Projected, out: &mut Vec<u8>) {
173 match value {
174 Projected::Null => message(out, 1, &[]),
175 Projected::Bool(flag) => boolean(out, 2, *flag),
176 Projected::Int(number) => sint(out, 3, *number),
177 Projected::Float(number) => double(out, 4, *number),
178 Projected::Text(text) => string(out, 5, text),
179 Projected::DurationMs(ms) => {
180 let mut body = Vec::new();
181 uint(&mut body, 1, *ms);
182 message(out, 6, &body);
183 }
184 Projected::Quantity(value, unit) => {
185 let mut body = Vec::new();
186 let mut inner = Vec::new();
187 encode_value(value, &mut inner);
188 message(&mut body, 1, &inner);
189 string(&mut body, 2, unit);
190 message(out, 7, &body);
191 }
192 Projected::Array(items) => {
193 let mut body = Vec::new();
194 for item in items {
195 let mut encoded = Vec::new();
196 encode_value(item, &mut encoded);
197 message(&mut body, 1, &encoded);
198 }
199 message(out, 8, &body);
200 }
201 Projected::Record(fields) => {
202 let mut body = Vec::new();
203 for (name, value) in fields {
204 let mut field = Vec::new();
205 string(&mut field, 1, name);
206 let mut encoded = Vec::new();
207 encode_value(value, &mut encoded);
208 message(&mut field, 2, &encoded);
209 message(&mut body, 1, &field);
210 }
211 message(out, 9, &body);
212 }
213 Projected::Blob {
214 hash,
215 size,
216 media_type,
217 tainted,
218 } => {
219 let mut body = Vec::new();
220 bytes(&mut body, 1, hash);
221 uint(&mut body, 2, *size);
222 string(&mut body, 3, media_type);
223 boolean(&mut body, 4, *tainted);
224 message(out, 10, &body);
225 }
226 Projected::Env(name) => {
227 let mut body = Vec::new();
228 string(&mut body, 1, name);
229 message(out, 11, &body);
230 }
231 Projected::Expr(kind) => {
232 let mut body = Vec::new();
233 string(&mut body, 1, kind);
234 message(out, 12, &body);
235 }
236 }
237}
238
239pub fn to_protobuf(module: &InfinityModule) -> Result<Vec<u8>> {
253 let image = encode(module)?;
254 let hash = hash_image(&image)?;
255 let secrets = secret_table(module);
256
257 let mut out = Vec::new();
258 bytes(&mut out, 1, &hash);
259 uint(&mut out, 2, image.len() as u64);
260
261 if let Some(root) = module.root() {
262 let value = project_node_with(module, root, &secrets)?;
263 let mut body = Vec::new();
264 encode_value(&value, &mut body);
265 message(&mut out, 3, &body);
266 }
267
268 for (name, id) in module.bindings() {
269 let value = project_node_with(module, *id, &secrets)?;
270 let mut binding = Vec::new();
271 string(&mut binding, 1, &redacted_key(name, &secrets));
272 let mut encoded = Vec::new();
273 encode_value(&value, &mut encoded);
274 message(&mut binding, 2, &encoded);
275 message(&mut out, 4, &binding);
276 }
277
278 if let Some(prov) = module.provenance() {
279 message(&mut out, 5, &encode_provenance(prov, &secrets));
280 }
281
282 for annotation in module.annotations() {
283 message(&mut out, 6, &encode_annotation(annotation, &secrets));
284 }
285
286 for (tag, payload) in module.extensions() {
287 let mut extension = Vec::new();
288 uint(&mut extension, 1, *tag);
289 bytes(&mut extension, 2, payload);
290 message(&mut out, 7, &extension);
291 }
292
293 Ok(out)
294}
295
296fn encode_annotation(annotation: &crate::ir::Annotation, secrets: &[String]) -> Vec<u8> {
297 let mut body = Vec::new();
298 uint(&mut body, 1, annotation.node.index() as u64);
299 match &annotation.kind {
300 AnnotationKind::Intent(text) => string(&mut body, 2, &redacted_key(text, secrets)),
301 AnnotationKind::Constraint { source, expr } => {
302 let mut constraint = Vec::new();
303 string(&mut constraint, 1, &redacted_key(source, secrets));
304 uint(&mut constraint, 2, expr.index() as u64);
305 message(&mut body, 3, &constraint);
306 }
307 AnnotationKind::AiImmutable(flag) => boolean(&mut body, 4, *flag),
308 }
309 body
310}
311
312fn encode_provenance(prov: &crate::ir::Provenance, secrets: &[String]) -> Vec<u8> {
313 let mut body = Vec::new();
314 string(
315 &mut body,
316 1,
317 &redacted_key(prov.producer.as_deref().unwrap_or(""), secrets),
318 );
319 uint(&mut body, 2, prov.timestamp_ms);
320 string(
321 &mut body,
322 3,
323 &redacted_key(prov.model.as_deref().unwrap_or(""), secrets),
324 );
325 string(
326 &mut body,
327 4,
328 &redacted_key(prov.tool.as_deref().unwrap_or(""), secrets),
329 );
330 if let Some(parent) = &prov.parent {
331 bytes(&mut body, 5, parent);
332 }
333 for entry in &prov.entries {
334 let mut encoded = Vec::new();
335 string(&mut encoded, 1, &redacted_key(&entry.key, secrets));
336 match &entry.value {
337 ProvValue::Text(text) => string(&mut encoded, 2, &redacted_key(text, secrets)),
338 ProvValue::Int(number) => sint(&mut encoded, 3, *number),
339 ProvValue::Flag(flag) => boolean(&mut encoded, 4, *flag),
340 ProvValue::Hash(hash) => bytes(&mut encoded, 5, hash),
341 }
342 message(&mut body, 6, &encoded);
343 }
344 body
345}