Skip to main content

cvkg_cli/
scaffold.rs

1use anyhow::{Context, Result};
2use std::fs;
3use std::path::Path;
4use std::process::Command;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub enum Template {
8    Minimal,
9    Dashboard,
10    AiCopilot,
11}
12
13impl std::str::FromStr for Template {
14    type Err = ();
15
16    fn from_str(s: &str) -> Result<Self, Self::Err> {
17        match s {
18            "dashboard" => Ok(Template::Dashboard),
19            "ai" | "ai-copilot" => Ok(Template::AiCopilot),
20            _ => Ok(Template::Minimal),
21        }
22    }
23}
24
25/// Project scaffold generator for CVKG applications.
26pub struct Scaffolder {
27    pub name: String,
28    pub template: Template,
29    pub init_git: bool,
30}
31
32impl Scaffolder {
33    /// Create a new Scaffolder for a project name.
34    pub fn new(name: String, template: Template, init_git: bool) -> Self {
35        Self {
36            name,
37            template,
38            init_git,
39        }
40    }
41
42    /// Execute the scaffolding process.
43    pub fn run(&self) -> Result<()> {
44        let root = Path::new(&self.name);
45        if root.exists() {
46            return Err(anyhow::anyhow!("Directory already exists: {}", self.name));
47        }
48
49        println!("🛠️ Scaffolding CVKG application: {}...", self.name);
50
51        fs::create_dir_all(root.join("src"))?;
52        fs::create_dir_all(root.join("assets"))?;
53        fs::create_dir_all(root.join("themes"))?;
54        fs::create_dir_all(root.join(".github").join("workflows"))?;
55
56        self.gen_cargo_toml(root)?;
57        self.gen_main_rs(root)?;
58        self.gen_theme_rs(root)?;
59        self.gen_gitignore(root)?;
60        self.gen_ci_yml(root)?;
61
62        if self.init_git {
63            println!("📦 Initializing git repository...");
64            Command::new("git").arg("init").current_dir(root).output()?;
65        }
66
67        println!("✅ Successfully scaffolded CVKG project: {}", self.name);
68        println!("\nNext steps:");
69        println!("  cd {}", self.name);
70        println!("  cvkg dev");
71
72        Ok(())
73    }
74
75    fn gen_cargo_toml(&self, root: &Path) -> Result<()> {
76        let version = env!("CARGO_PKG_VERSION");
77        let content = format!(
78            r#"[package]
79name = "{}"
80version = "0.1.0"
81edition = "2024"
82
83[dependencies]
84cvkg = {{ version = "{}" }}
85cvkg-core = {{ version = "{}" }}
86tokio = {{ version = "1.0", features = ["full"] }}
87log = "0.4"
88"#,
89            self.name, version, version
90        );
91
92        fs::write(root.join("Cargo.toml"), content).context("Failed to write Cargo.toml")
93    }
94
95    fn gen_main_rs(&self, root: &Path) -> Result<()> {
96        let content = match self.template {
97            Template::Minimal => {
98                r#"use cvkg_core::View;
99
100/// Application entry point for the minimal CVKG template.
101///
102/// For a full application, implement the `View` trait and
103/// register it with your platform renderer.
104fn main() {
105    println!("CVKG Minimal Template — Replace with your app.");
106    println!("See https://github.com/sydonayrex/cvkg for documentation.");
107}
108"#
109            }
110            Template::Dashboard => {
111                r#"use cvkg_core::View;
112
113/// Application entry point for the dashboard CVKG template.
114///
115/// For a full application, implement the `View` trait and
116/// register it with your platform renderer.
117fn main() {
118    println!("CVKG Dashboard Template — Replace with your app.");
119    println!("See https://github.com/sydonayrex/cvkg for documentation.");
120}
121"#
122            }
123            Template::AiCopilot => {
124                r#"use cvkg_core::View;
125
126/// Application entry point for the AI Copilot CVKG template.
127///
128/// For a full application, implement the `View` trait and
129/// register it with your platform renderer.
130fn main() {
131    println!("CVKG AI Copilot Template — Replace with your app.");
132    println!("See https://github.com/sydonayrex/cvkg for documentation.");
133}
134"#
135            }
136        };
137        fs::write(root.join("src/main.rs"), content).context("Failed to write src/main.rs")
138    }
139
140    fn gen_theme_rs(&self, root: &Path) -> Result<()> {
141        let content = r#"/// Default Niflheim Theme for CVKG
142pub struct Theme {
143    pub primary: [f32; 4],
144    pub background: [f32; 4],
145}
146
147pub const DEFAULT_THEME: Theme = Theme {
148    primary: [0.0, 1.0, 1.0, 1.0],
149    background: [0.05, 0.05, 0.1, 1.0],
150};
151"#;
152        fs::write(root.join("themes/default.rs"), content)
153            .context("Failed to write themes/default.rs")
154    }
155
156    fn gen_gitignore(&self, root: &Path) -> Result<()> {
157        let content = r#"/target
158**/*.rs.bk
159Cargo.lock
160"#;
161        fs::write(root.join(".gitignore"), content).context("Failed to write .gitignore")
162    }
163
164    fn gen_ci_yml(&self, root: &Path) -> Result<()> {
165        let content = r#"name: CVKG CI
166on:
167  push:
168    branches: [ main ]
169  pull_request:
170    branches: [ main ]
171
172jobs:
173  build:
174    runs-on: ubuntu-latest
175    steps:
176    - uses: actions/checkout@v3
177    - name: Build
178      run: cargo build --verbose
179    - name: Run tests
180      run: cargo test --verbose
181"#;
182        fs::write(root.join(".github/workflows/ci.yml"), content).context("Failed to write ci.yml")
183    }
184}