opencode_ralph_loop_cli/templates/
mod.rs1use rust_embed::RustEmbed;
2
3pub const TEMPLATE_VERSION: &str = "1.0.0";
4
5#[derive(RustEmbed)]
6#[folder = "templates/"]
7pub struct Assets;
8
9pub const DEFAULT_PLUGIN_VERSION: &str = "1.4.7";
10
11pub struct RenderContext {
12 pub plugin_version: String,
13}
14
15impl Default for RenderContext {
16 fn default() -> Self {
17 Self {
18 plugin_version: DEFAULT_PLUGIN_VERSION.to_string(),
19 }
20 }
21}
22
23impl RenderContext {
24 pub fn with_version(version: impl Into<String>) -> Self {
25 Self {
26 plugin_version: version.into(),
27 }
28 }
29}
30
31pub fn render_template(path: &str, ctx: &RenderContext) -> Option<Vec<u8>> {
34 let file = Assets::get(path)?;
35 if path == "package.json.template" {
36 let content = std::str::from_utf8(&file.data).ok()?;
37 let rendered = content.replace("{{PLUGIN_VERSION}}", &ctx.plugin_version);
38 Some(rendered.into_bytes())
39 } else {
40 Some(file.data.into_owned())
41 }
42}
43
44pub fn template_to_output_path(template_path: &str) -> &'static str {
46 match template_path {
47 "plugins/ralph.ts" => "plugins/ralph.ts",
48 "commands/ralph-loop.md" => "commands/ralph-loop.md",
49 "commands/cancel-ralph.md" => "commands/cancel-ralph.md",
50 "commands/ralph-help.md" => "commands/ralph-help.md",
51 "package.json.template" => "package.json",
52 ".gitignore.template" => ".gitignore",
53 _ => "",
54 }
55}
56
57pub fn canonical_templates() -> &'static [&'static str] {
59 &[
60 "plugins/ralph.ts",
61 "commands/ralph-loop.md",
62 "commands/cancel-ralph.md",
63 "commands/ralph-help.md",
64 "package.json.template",
65 ".gitignore.template",
66 ]
67}
68
69pub fn template_canonical_hash(path: &str, ctx: &RenderContext) -> Option<String> {
71 let bytes = render_template(path, ctx)?;
72 Some(crate::hash::sha256_hex(&bytes))
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn all_canonical_templates_exist() {
81 let ctx = RenderContext::default();
82 for path in canonical_templates() {
83 let result = render_template(path, &ctx);
84 assert!(result.is_some(), "template not found: {path}");
85 }
86 }
87
88 #[test]
89 fn package_json_substitutes_version() {
90 let ctx = RenderContext::with_version("9.9.9");
91 let bytes = render_template("package.json.template", &ctx).unwrap();
92 let content = String::from_utf8(bytes).unwrap();
93 assert!(
94 content.contains("9.9.9"),
95 "version not substituted: {content}"
96 );
97 assert!(!content.contains("{{PLUGIN_VERSION}}"));
98 }
99
100 #[test]
101 fn default_version_is_correct() {
102 assert_eq!(DEFAULT_PLUGIN_VERSION, "1.4.7");
103 }
104
105 #[test]
106 fn template_to_output_path_mappings() {
107 assert_eq!(
108 template_to_output_path("plugins/ralph.ts"),
109 "plugins/ralph.ts"
110 );
111 assert_eq!(
112 template_to_output_path("package.json.template"),
113 "package.json"
114 );
115 assert_eq!(template_to_output_path(".gitignore.template"), ".gitignore");
116 }
117}