use super::{RuleEmitter, ensure_parent_dir, format_bullet_list, format_heading};
use crate::{Editor, Result, parser::UnifiedRule};
use std::path::Path;
#[derive(Debug, Default)]
pub struct GenericEmitter;
impl GenericEmitter {
pub fn new() -> Self {
Self
}
}
impl RuleEmitter for GenericEmitter {
fn emit_file(&self, rules: &[UnifiedRule], path: &Path) -> Result<()> {
ensure_parent_dir(path)?;
let content = self.emit_string(rules)?;
std::fs::write(path, content)?;
Ok(())
}
fn emit_string(&self, rules: &[UnifiedRule]) -> Result<String> {
let mut output = String::new();
output.push_str(&format_heading(1, "AI Coding Instructions"));
output.push_str("Generated by Driven - Universal AI Rule Format\n\n");
for rule in rules {
match rule {
UnifiedRule::Persona {
name,
role,
identity,
style,
traits,
principles,
} => {
output.push_str(&format_heading(2, &format!("Persona: {}", name)));
output.push_str(&format!("**Role:** {}\n\n", role));
if let Some(id) = identity {
output.push_str(&format!("**Identity:** {}\n\n", id));
}
if let Some(s) = style {
output.push_str(&format!("**Style:** {}\n\n", s));
}
if !traits.is_empty() {
output.push_str("**Traits:**\n");
output.push_str(&format_bullet_list(traits));
output.push('\n');
}
if !principles.is_empty() {
output.push_str("**Principles:**\n");
output.push_str(&format_bullet_list(principles));
output.push('\n');
}
}
UnifiedRule::Standard {
category,
priority,
description,
pattern,
} => {
output.push_str(&format!(
"- **[{:?}:{}]** {}\n",
category, priority, description
));
if let Some(p) = pattern {
output.push_str(&format!(" - Pattern: `{}`\n", p));
}
}
UnifiedRule::Context {
includes,
excludes,
focus,
} => {
output.push_str(&format_heading(2, "Context"));
if !includes.is_empty() {
output.push_str("**Include:**\n");
output.push_str(&format_bullet_list(includes));
}
if !excludes.is_empty() {
output.push_str("**Exclude:**\n");
output.push_str(&format_bullet_list(excludes));
}
if !focus.is_empty() {
output.push_str("**Focus:**\n");
output.push_str(&format_bullet_list(focus));
}
output.push('\n');
}
UnifiedRule::Workflow { name, steps } => {
output.push_str(&format_heading(2, &format!("Workflow: {}", name)));
for (i, step) in steps.iter().enumerate() {
output.push_str(&format!("### Step {}: {}\n\n", i + 1, step.name));
output.push_str(&step.description);
output.push_str("\n\n");
if let Some(condition) = &step.condition {
output.push_str(&format!("*Condition: {}*\n\n", condition));
}
if !step.actions.is_empty() {
output.push_str("**Actions:**\n");
output.push_str(&format_bullet_list(&step.actions));
output.push('\n');
}
}
}
UnifiedRule::Raw { content } => {
output.push_str(content);
output.push_str("\n\n");
}
}
}
Ok(output.trim().to_string())
}
fn editor(&self) -> Editor {
Editor::Aider }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_emit_generic() {
let rules = vec![
UnifiedRule::persona("Test Agent", "Testing role"),
UnifiedRule::standard(crate::format::RuleCategory::Style, 0, "Test standard"),
];
let emitter = GenericEmitter::new();
let output = emitter.emit_string(&rules).unwrap();
assert!(output.contains("AI Coding Instructions"));
assert!(output.contains("Persona: Test Agent"));
assert!(output.contains("Test standard"));
}
}