use crate::error::Result;
use crate::template_ref::TronRef;
#[derive(Debug)]
pub struct TronAssembler {
templates: Vec<TronRef>,
separator: String,
}
impl TronAssembler {
pub fn new() -> Self {
Self {
templates: Vec::new(),
separator: "\n".to_string(),
}
}
pub fn with_separator(separator: &str) -> Self {
Self {
templates: Vec::new(),
separator: separator.to_string(),
}
}
pub fn separator(&self) -> &str {
&self.separator
}
pub fn set_separator(&mut self, separator: &str) {
self.separator = separator.to_string();
}
pub fn len(&self) -> usize {
self.templates.len()
}
pub fn is_empty(&self) -> bool {
self.templates.is_empty()
}
pub fn add_template(&mut self, template: TronRef) {
self.templates.push(template);
}
pub fn add_templates<I>(&mut self, templates: I)
where
I: IntoIterator<Item = TronRef>,
{
for template in templates {
self.templates.push(template);
}
}
pub fn insert_template(&mut self, index: usize, template: TronRef) {
self.templates.insert(index, template);
}
pub fn remove_template(&mut self, index: usize) -> Option<TronRef> {
if index < self.templates.len() {
Some(self.templates.remove(index))
} else {
None
}
}
pub fn clear(&mut self) {
self.templates.clear();
}
pub fn get(&self, index: usize) -> Option<&TronRef> {
self.templates.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut TronRef> {
self.templates.get_mut(index)
}
pub fn set_global(&mut self, placeholder: &str, value: &str) -> Result<()> {
for template in &mut self.templates {
if template.inner().has_placeholder(placeholder) {
template.set(placeholder, value)?;
}
}
Ok(())
}
pub fn set_ref_global(&mut self, placeholder: &str, template_ref: TronRef) -> Result<()> {
for template in &mut self.templates {
if template.inner().has_placeholder(placeholder) {
template.set_ref(placeholder, template_ref.clone())?;
}
}
Ok(())
}
pub fn all_dependencies(&self) -> Vec<String> {
let mut deps = std::collections::HashSet::new();
for template in &self.templates {
for dep in template.dependencies() {
deps.insert(dep.clone());
}
}
deps.into_iter().collect()
}
pub fn render_all(&self) -> Result<String> {
if self.templates.is_empty() {
return Ok(String::new());
}
let mut results = Vec::new();
for template in &self.templates {
results.push(template.render()?);
}
Ok(results.join(&self.separator))
}
pub fn render_all_partial(&self) -> Result<String> {
if self.templates.is_empty() {
return Ok(String::new());
}
let mut results = Vec::new();
for template in &self.templates {
results.push(template.render_partial()?);
}
Ok(results.join(&self.separator))
}
pub fn iter(&self) -> std::slice::Iter<'_, TronRef> {
self.templates.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, TronRef> {
self.templates.iter_mut()
}
}
impl Default for TronAssembler {
fn default() -> Self {
Self::new()
}
}
impl IntoIterator for TronAssembler {
type Item = TronRef;
type IntoIter = std::vec::IntoIter<TronRef>;
fn into_iter(self) -> Self::IntoIter {
self.templates.into_iter()
}
}
impl<'a> IntoIterator for &'a TronAssembler {
type Item = &'a TronRef;
type IntoIter = std::slice::Iter<'a, TronRef>;
fn into_iter(self) -> Self::IntoIter {
self.templates.iter()
}
}
impl<'a> IntoIterator for &'a mut TronAssembler {
type Item = &'a mut TronRef;
type IntoIter = std::slice::IterMut<'a, TronRef>;
fn into_iter(self) -> Self::IntoIter {
self.templates.iter_mut()
}
}
impl FromIterator<TronRef> for TronAssembler {
fn from_iter<I: IntoIterator<Item = TronRef>>(iter: I) -> Self {
let mut assembler = TronAssembler::new();
assembler.add_templates(iter);
assembler
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::template::TronTemplate;
#[test]
fn test_new_assembler() {
let assembler = TronAssembler::new();
assert_eq!(assembler.len(), 0);
assert!(assembler.is_empty());
assert_eq!(assembler.separator(), "\n");
}
#[test]
fn test_with_separator() {
let assembler = TronAssembler::with_separator("---");
assert_eq!(assembler.separator(), "---");
}
#[test]
fn test_add_template() -> Result<()> {
let mut assembler = TronAssembler::new();
let template = TronTemplate::new("Hello")?;
assembler.add_template(TronRef::new(template));
assert_eq!(assembler.len(), 1);
assert!(!assembler.is_empty());
Ok(())
}
#[test]
fn test_add_templates() -> Result<()> {
let mut assembler = TronAssembler::new();
let templates = vec![
TronRef::new(TronTemplate::new("First")?),
TronRef::new(TronTemplate::new("Second")?),
];
assembler.add_templates(templates);
assert_eq!(assembler.len(), 2);
Ok(())
}
#[test]
fn test_render_all() -> Result<()> {
let mut assembler = TronAssembler::new();
let mut template1 = TronRef::new(TronTemplate::new("Hello @[name]@!")?);
template1.set("name", "World")?;
let mut template2 = TronRef::new(TronTemplate::new("Goodbye @[name]@!")?);
template2.set("name", "World")?;
assembler.add_template(template1);
assembler.add_template(template2);
let result = assembler.render_all()?;
assert_eq!(result, "Hello World!\nGoodbye World!");
Ok(())
}
#[test]
fn test_set_global() -> Result<()> {
let mut assembler = TronAssembler::new();
assembler.add_template(TronRef::new(TronTemplate::new("Hello @[name]@!")?));
assembler.add_template(TronRef::new(TronTemplate::new("Goodbye @[name]@!")?));
assembler.set_global("name", "World")?;
let result = assembler.render_all()?;
assert!(result.contains("Hello World!"));
assert!(result.contains("Goodbye World!"));
Ok(())
}
#[test]
fn test_template_composition() -> Result<()> {
let mut assembler = TronAssembler::new();
let content = TronTemplate::new("Generated at @[time]@")?;
let mut content_ref = TronRef::new(content);
content_ref.set("time", "2024-01-01")?;
assembler.add_template(TronRef::new(TronTemplate::new("Header: @[content]@")?));
assembler.add_template(TronRef::new(TronTemplate::new("Body: @[content]@")?));
assembler.set_ref_global("content", content_ref)?;
let result = assembler.render_all()?;
assert!(result.contains("Header: Generated at 2024-01-01"));
assert!(result.contains("Body: Generated at 2024-01-01"));
Ok(())
}
#[test]
fn test_all_dependencies() -> Result<()> {
let mut assembler = TronAssembler::new();
let template1 = TronRef::new(TronTemplate::new("fn main() {}")?)
.with_dependency("serde = \"1.0\"");
let template2 = TronRef::new(TronTemplate::new("fn other() {}")?)
.with_dependency("tokio = \"1.0\"")
.with_dependency("serde = \"1.0\"");
assembler.add_template(template1);
assembler.add_template(template2);
let deps = assembler.all_dependencies();
assert_eq!(deps.len(), 2); assert!(deps.contains(&"serde = \"1.0\"".to_string()));
assert!(deps.contains(&"tokio = \"1.0\"".to_string()));
Ok(())
}
#[test]
fn test_custom_separator() -> Result<()> {
let mut assembler = TronAssembler::with_separator(" | ");
assembler.add_template(TronRef::new(TronTemplate::new("First")?));
assembler.add_template(TronRef::new(TronTemplate::new("Second")?));
let result = assembler.render_all()?;
assert_eq!(result, "First | Second");
Ok(())
}
#[test]
fn test_insert_and_remove() -> Result<()> {
let mut assembler = TronAssembler::new();
assembler.add_template(TronRef::new(TronTemplate::new("Second")?));
assembler.insert_template(0, TronRef::new(TronTemplate::new("First")?));
let result = assembler.render_all()?;
assert_eq!(result, "First\nSecond");
let removed = assembler.remove_template(0);
assert!(removed.is_some());
assert_eq!(assembler.len(), 1);
Ok(())
}
#[test]
fn test_empty_render() {
let assembler = TronAssembler::new();
let result = assembler.render_all().unwrap();
assert_eq!(result, "");
}
#[test]
fn test_render_partial() -> Result<()> {
let mut assembler = TronAssembler::new();
let mut template = TronRef::new(TronTemplate::new("Hello @[greeting]@ @[name]@!")?);
template.set("greeting", "Hi")?;
assembler.add_template(template);
let result = assembler.render_all_partial()?;
assert_eq!(result, "Hello Hi @[name]@!");
Ok(())
}
}