Skip to main content

espforge_lib/examples/
template.rs

1use super::config::ConfigFile;
2use super::fs::OutputDirectory;
3use crate::cli::model::{ExampleConfig, ExportResult};
4use anyhow::{Context, Result, anyhow};
5use espforge_examples::EXAMPLES_DIR;
6use std::fs;
7use std::path::Path;
8
9pub struct ExampleExporter;
10
11impl Default for ExampleExporter {
12    fn default() -> Self {
13        Self::new()
14    }
15}
16
17impl ExampleExporter {
18    pub fn new() -> Self {
19        Self
20    }
21
22    pub fn export(&self, config: &ExampleConfig, output: &OutputDirectory) -> Result<ExportResult> {
23        let template = ExampleTemplate::find(&config.template_name)?;
24        template.extract_to(output.path())?;
25
26        let app_rust_dir = output.path().join("app").join("rust");
27        let src_dir = output.path().join("src");
28
29        if app_rust_dir.exists() {
30            if !src_dir.exists() {
31                fs::create_dir_all(&src_dir).context("Failed to create src directory")?;
32            }
33
34            for entry in fs::read_dir(&app_rust_dir).context("Failed to read app/rust directory")? {
35                let entry = entry?;
36                let path = entry.path();
37                if path.is_file()
38                    && let Some(file_name) = path.file_name() {
39                        let dest = src_dir.join(file_name);
40                        fs::rename(&path, &dest).context("Failed to move file to src")?;
41                    }
42            }
43
44            let app_dir = output.path().join("app");
45            if app_dir.exists() {
46                let _ = fs::remove_dir_all(&app_dir);
47            }
48        }
49
50        let config_file = ConfigFile::locate(output.path())?;
51        config_file.update(config)?;
52
53        let final_name = config_file.rename_to(&config.project_name)?;
54
55        Ok(ExportResult {
56            project_name: config.project_name.clone(),
57            output_file: format!("{}.yaml", final_name),
58        })
59    }
60}
61
62pub(crate) struct ExampleTemplate {
63    dir: &'static include_dir::Dir<'static>,
64}
65
66impl ExampleTemplate {
67    pub(crate) fn find(name: &str) -> Result<Self> {
68        let dir = Self::search_in_catalog(name)
69            .ok_or_else(|| anyhow!("Example template '{}' not found", name))?;
70
71        Ok(Self { dir })
72    }
73
74    fn search_in_catalog(name: &str) -> Option<&'static include_dir::Dir<'static>> {
75        EXAMPLES_DIR
76            .dirs()
77            .flat_map(|category| category.dirs())
78            .find(|example| example.path().file_name().and_then(|n| n.to_str()) == Some(name))
79    }
80
81    fn extract_to(&self, target: &Path) -> Result<()> {
82        extract_recursive(self.dir, target, self.dir.path())
83            .context("Failed to extract example files to disk")
84    }
85}
86
87fn extract_recursive(
88    dir: &include_dir::Dir,
89    base_path: &Path,
90    root_prefix: &Path,
91) -> std::io::Result<()> {
92    // Logic to strip prefix and write files/directories recursively
93    let dir_path = dir.path();
94    let relative_dir_path = dir_path
95        .strip_prefix(root_prefix)
96        .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
97
98    let dest_dir = base_path.join(relative_dir_path);
99    if !dest_dir.exists() {
100        fs::create_dir_all(&dest_dir)?;
101    }
102
103    for file in dir.files() {
104        let path = file.path();
105        let relative_path = path
106            .strip_prefix(root_prefix)
107            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
108
109        let dest_path = base_path.join(relative_path);
110
111        if let Some(parent) = dest_path.parent() {
112            fs::create_dir_all(parent)?;
113        }
114
115        fs::write(dest_path, file.contents())?;
116    }
117
118    for subdir in dir.dirs() {
119        extract_recursive(subdir, base_path, root_prefix)?;
120    }
121
122    Ok(())
123}