bundle_standard_core/
build.rs1use crate::errors::PackError;
4use crate::types::{PackInputs, PackOutput};
5use crate::workspace::synthesize_workspace;
6use crate::zip_writer::zip_entries;
7use sha2::{Digest, Sha256};
8
9pub fn build_pack(inputs: &PackInputs<'_>) -> Result<PackOutput, PackError> {
10 let entries = synthesize_workspace(inputs)?;
11 let bytes = zip_entries(&entries)?;
12 let sha256 = hex_sha256(&bytes);
13 let filename = format!(
14 "{}-{}.gtpack",
15 inputs.config.metadata.name, inputs.config.metadata.version
16 );
17 Ok(PackOutput {
18 filename,
19 bytes,
20 sha256,
21 })
22}
23
24fn hex_sha256(bytes: &[u8]) -> String {
25 let mut h = Sha256::new();
26 h.update(bytes);
27 let out = h.finalize();
28 const HEX: &[u8; 16] = b"0123456789abcdef";
29 let mut s = String::with_capacity(64);
30 for b in out {
31 s.push(HEX[(b >> 4) as usize] as char);
32 s.push(HEX[(b & 0x0f) as usize] as char);
33 }
34 s
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::types::{CardContentEntry, FlowEntry, I18nConfig, StandardConfig, StandardMetadata};
41 use serde_json::json;
42
43 fn min_inputs<'a>(
44 cfg: &'a StandardConfig,
45 flows: &'a [FlowEntry],
46 cards: &'a [CardContentEntry],
47 ) -> PackInputs<'a> {
48 PackInputs {
49 config: cfg,
50 flows,
51 cards,
52 assets: &[],
53 capabilities: &[],
54 }
55 }
56
57 #[test]
58 fn happy_path() {
59 let cfg = StandardConfig {
60 metadata: StandardMetadata {
61 name: "demo".into(),
62 version: "0.1.0".into(),
63 author: None,
64 },
65 channels: vec!["webchat".into()],
66 embed_ui: "webchat".into(),
67 i18n: I18nConfig::default(),
68 format: "gtpack-legacy".into(),
69 };
70 let flows = vec![FlowEntry {
71 name: "main".into(),
72 yaml: "schema_version: 2".into(),
73 }];
74 let cards = vec![CardContentEntry {
75 id: "welcome".into(),
76 json: json!({"type":"AdaptiveCard"}),
77 }];
78 let out = build_pack(&min_inputs(&cfg, &flows, &cards)).unwrap();
79 assert_eq!(out.filename, "demo-0.1.0.gtpack");
80 assert_eq!(out.sha256.len(), 64);
81 assert!(!out.bytes.is_empty());
82 }
83
84 #[test]
85 fn deterministic_sha() {
86 let cfg = StandardConfig {
87 metadata: StandardMetadata {
88 name: "x".into(),
89 version: "1".into(),
90 author: None,
91 },
92 channels: vec![],
93 embed_ui: "none".into(),
94 i18n: I18nConfig::default(),
95 format: "gtpack-legacy".into(),
96 };
97 let a = build_pack(&min_inputs(&cfg, &[], &[])).unwrap();
98 let b = build_pack(&min_inputs(&cfg, &[], &[])).unwrap();
99 assert_eq!(a.sha256, b.sha256);
100 }
101}