#![allow(dead_code)]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Template {
pub template: TemplateMeta,
#[serde(default)]
pub tools: TemplateTools,
#[serde(default)]
pub hooks: HashMap<String, TemplateHook>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplateMeta {
pub name: String,
pub description: String,
#[serde(default)]
pub category: String,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub author: Option<String>,
#[serde(default)]
pub version: Option<String>,
#[serde(default)]
pub min_jarvy_version: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TemplateTools {
pub tools: HashMap<String, String>,
}
impl TemplateTools {
pub fn new() -> Self {
Self {
tools: HashMap::new(),
}
}
pub fn add(&mut self, name: &str, version: &str) {
self.tools.insert(name.to_string(), version.to_string());
}
pub fn len(&self) -> usize {
self.tools.len()
}
pub fn is_empty(&self) -> bool {
self.tools.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.tools.iter().map(|(k, v)| (k.as_str(), v.as_str()))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplateHook {
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub script: Option<String>,
}
impl Template {
pub fn from_toml(content: &str) -> Result<Self, toml::de::Error> {
toml::from_str(content)
}
pub fn name(&self) -> &str {
&self.template.name
}
pub fn description(&self) -> &str {
&self.template.description
}
pub fn category(&self) -> &str {
&self.template.category
}
pub fn tool_count(&self) -> usize {
self.tools.len()
}
pub fn to_jarvy_toml(&self) -> String {
let mut content = String::new();
content.push_str(&format!("# Generated from {} template\n", self.name()));
content.push_str(&format!("# {}\n\n", self.description()));
content.push_str("[provisioner]\n");
let mut tools: Vec<_> = self.tools.iter().collect();
tools.sort_by_key(|(name, _)| *name);
for (name, version) in tools {
content.push_str(&format!("{} = \"{}\"\n", name, version));
}
if !self.hooks.is_empty() {
content.push('\n');
for (tool, hook) in &self.hooks {
if let Some(ref desc) = hook.description {
content.push_str(&format!("# {}: {}\n", tool, desc));
}
if let Some(ref script) = hook.script {
content.push_str(&format!("[hooks.{}]\n", tool));
content.push_str(&format!("script = '''\n{}\n'''\n\n", script.trim()));
}
}
}
content
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_template_from_toml() {
let toml = r#"
[template]
name = "test"
description = "Test template"
category = "Testing"
tags = ["test", "example"]
[tools]
git = "latest"
node = "20"
"#;
let template = Template::from_toml(toml).unwrap();
assert_eq!(template.name(), "test");
assert_eq!(template.description(), "Test template");
assert_eq!(template.category(), "Testing");
assert_eq!(template.tool_count(), 2);
}
#[test]
fn test_template_to_jarvy_toml() {
let mut tools = TemplateTools::new();
tools.add("git", "latest");
tools.add("node", "20");
let template = Template {
template: TemplateMeta {
name: "test".to_string(),
description: "Test template".to_string(),
category: "Testing".to_string(),
tags: vec![],
author: None,
version: None,
min_jarvy_version: None,
},
tools,
hooks: HashMap::new(),
};
let content = template.to_jarvy_toml();
assert!(content.contains("[provisioner]"));
assert!(content.contains("git = \"latest\""));
assert!(content.contains("node = \"20\""));
}
#[test]
fn test_template_tools_operations() {
let mut tools = TemplateTools::new();
assert!(tools.is_empty());
tools.add("git", "latest");
assert_eq!(tools.len(), 1);
assert!(!tools.is_empty());
tools.add("node", "20");
assert_eq!(tools.len(), 2);
}
}