Skip to main content

ggen_cli_lib/cmds/
template.rs

1//! Template Commands - clap-noun-verb v3.4.0 Migration
2//!
3//! This module implements template commands using the v3.4.0 #[verb] pattern.
4
5use clap_noun_verb::Result as NounVerbResult;
6use clap_noun_verb_macros::verb;
7use serde::Serialize;
8use std::path::PathBuf;
9
10// ============================================================================
11// Output Types
12// ============================================================================
13
14#[derive(Serialize)]
15struct ShowOutput {
16    name: String,
17    path: String,
18    description: Option<String>,
19    output_path: Option<String>,
20    variables: Vec<String>,
21    rdf_sources: Vec<String>,
22    sparql_queries_count: usize,
23    determinism_seed: Option<u64>,
24}
25
26#[derive(Serialize)]
27struct NewOutput {
28    template_name: String,
29    template_type: String,
30    path: String,
31}
32
33#[derive(Serialize)]
34struct ListOutput {
35    templates: Vec<TemplateInfo>,
36    total: usize,
37    directory: String,
38}
39
40#[derive(Serialize)]
41struct TemplateInfo {
42    name: String,
43    source: String,
44    description: Option<String>,
45    path: String,
46}
47
48#[derive(Serialize)]
49struct LintOutput {
50    has_errors: bool,
51    has_warnings: bool,
52    errors: Vec<LintMessage>,
53    warnings: Vec<LintMessage>,
54}
55
56#[derive(Serialize)]
57struct LintMessage {
58    line: Option<usize>,
59    message: String,
60}
61
62// ============================================================================
63// Verb Functions
64// ============================================================================
65
66/// Show template metadata
67#[verb]
68fn show(template: String) -> NounVerbResult<ShowOutput> {
69    use ggen_core::domain::template::show;
70
71    let metadata = show::show_template_metadata(&template).map_err(|e| {
72        clap_noun_verb::NounVerbError::execution_error(format!("Failed to show template: {}", e))
73    })?;
74
75    Ok(ShowOutput {
76        name: metadata.name,
77        path: metadata.path,
78        description: metadata.description,
79        output_path: metadata.output_path,
80        variables: metadata.variables,
81        rdf_sources: metadata.rdf_sources,
82        sparql_queries_count: metadata.sparql_queries.len(),
83        determinism_seed: metadata.determinism_seed,
84    })
85}
86
87/// Create new template
88#[verb]
89fn new(name: String, template_type: Option<String>) -> NounVerbResult<NewOutput> {
90    use ggen_core::domain::template::new as template_new;
91    use ggen_core::domain::template::TemplateService;
92
93    let template_type_str = template_type.as_deref().unwrap_or("generic");
94
95    let content =
96        template_new::generate_template_content(&name, template_type_str).map_err(|e| {
97            clap_noun_verb::NounVerbError::execution_error(format!(
98                "Failed to generate template: {}",
99                e
100            ))
101        })?;
102
103    let service = TemplateService::default_instance();
104    let path = service.write_template(&name, &content).map_err(|e| {
105        clap_noun_verb::NounVerbError::execution_error(format!("Failed to write template: {}", e))
106    })?;
107
108    Ok(NewOutput {
109        template_name: name,
110        template_type: template_type_str.to_string(),
111        path: path.display().to_string(),
112    })
113}
114
115/// List templates
116#[verb]
117fn list(directory: Option<PathBuf>) -> NounVerbResult<ListOutput> {
118    use ggen_core::domain::template::list;
119
120    let default_dir = PathBuf::from("templates");
121    let templates_dir = directory.as_ref().unwrap_or(&default_dir);
122
123    let filters = list::ListFilters {
124        pattern: None,
125        local_only: false,
126        gpack_only: false,
127    };
128
129    let templates = list::list_templates(templates_dir, &filters).map_err(|e| {
130        clap_noun_verb::NounVerbError::execution_error(format!("Failed to list templates: {}", e))
131    })?;
132
133    let template_infos = templates
134        .into_iter()
135        .map(|t| TemplateInfo {
136            name: t.name,
137            source: match t.source {
138                list::TemplateSource::Local => "local".to_string(),
139                list::TemplateSource::Gpack(name) => name,
140            },
141            description: t.description,
142            path: t.path,
143        })
144        .collect::<Vec<_>>();
145
146    let total = template_infos.len();
147
148    Ok(ListOutput {
149        templates: template_infos,
150        total,
151        directory: templates_dir.display().to_string(),
152    })
153}
154
155/// Lint a template
156#[verb]
157fn lint(template: String) -> NounVerbResult<LintOutput> {
158    use ggen_core::domain::template::lint;
159
160    let options = lint::LintOptions {
161        check_sparql: false,
162        check_schema: false,
163    };
164
165    let report = lint::lint_template(&template, &options).map_err(|e| {
166        clap_noun_verb::NounVerbError::execution_error(format!("Failed to lint template: {}", e))
167    })?;
168
169    let has_errors = report.has_errors();
170    let has_warnings = report.has_warnings();
171
172    let errors = report
173        .errors
174        .into_iter()
175        .map(|e| LintMessage {
176            line: e.line,
177            message: e.message,
178        })
179        .collect();
180
181    let warnings = report
182        .warnings
183        .into_iter()
184        .map(|w| LintMessage {
185            line: w.line,
186            message: w.message,
187        })
188        .collect();
189
190    Ok(LintOutput {
191        has_errors,
192        has_warnings,
193        errors,
194        warnings,
195    })
196}
197
198// ============================================================================
199// Helper Functions
200// ============================================================================
201
202// Use utility function from ggen_core::utils::cli instead of duplicating here