kotoba_build/
lib.rs

1//! Kotoba Build Tool
2//!
3//! Denoのビルドシステムに似た使い勝手で、Kotobaプロジェクトの
4//! ビルド、依存関係解決、タスク実行を統合的に管理します。
5
6use std::path::PathBuf;
7use serde::{Deserialize, Serialize};
8use colored::Colorize;
9
10/// ビルドツールのエラー型
11#[derive(Debug, thiserror::Error)]
12pub enum BuildError {
13    #[error("Configuration error: {0}")]
14    Config(String),
15
16    #[error("Task execution error: {0}")]
17    Task(String),
18
19    #[error("Build error: {0}")]
20    Build(String),
21
22    #[error("IO error: {0}")]
23    Io(#[from] std::io::Error),
24
25    #[error("JSON error: {0}")]
26    Json(#[from] serde_json::Error),
27
28    #[error("TOML error: {0}")]
29    Toml(#[from] toml::de::Error),
30
31    #[error("Anyhow error: {0}")]
32    Anyhow(#[from] anyhow::Error),
33}
34
35pub type Result<T> = std::result::Result<T, BuildError>;
36
37/// ビルド設定
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct BuildConfig {
40    pub name: String,
41    pub version: String,
42    pub description: Option<String>,
43    pub tasks: std::collections::HashMap<String, TaskConfig>,
44    pub dependencies: std::collections::HashMap<String, String>,
45}
46
47impl Default for BuildConfig {
48    fn default() -> Self {
49        Self {
50            name: "kotoba-project".to_string(),
51            version: "0.1.0".to_string(),
52            description: None,
53            tasks: std::collections::HashMap::new(),
54            dependencies: std::collections::HashMap::new(),
55        }
56    }
57}
58
59/// タスク設定
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct TaskConfig {
62    pub command: String,
63    pub args: Vec<String>,
64    pub description: Option<String>,
65    pub depends_on: Vec<String>,
66    pub cwd: Option<String>,
67    pub env: Option<std::collections::HashMap<String, String>>,
68}
69
70/// ビルドエンジン
71#[derive(Debug)]
72pub struct BuildEngine {
73    config: BuildConfig,
74    project_root: PathBuf,
75}
76
77impl BuildEngine {
78    pub async fn new(project_root: PathBuf) -> Result<Self> {
79        let config = Self::load_config(&project_root).await?;
80        Ok(Self { config, project_root })
81    }
82
83    pub async fn default() -> Result<Self> {
84        let project_root = std::env::current_dir()?;
85        Self::new(project_root).await
86    }
87
88    async fn load_config(project_root: &std::path::Path) -> Result<BuildConfig> {
89        let config_path = project_root.join("kotoba-build.toml");
90        if config_path.exists() {
91            let content = tokio::fs::read_to_string(config_path).await?;
92            let config: BuildConfig = toml::from_str(&content)?;
93            Ok(config)
94        } else {
95            Ok(BuildConfig::default())
96        }
97    }
98
99    pub async fn run_task(&self, task_name: &str) -> Result<()> {
100        match self.config.tasks.get(task_name) {
101            Some(task) => {
102                println!("Running task: {}", task_name.green());
103                self.execute_task(task).await
104            }
105            None => Err(BuildError::Task(format!("Task '{}' not found", task_name))),
106        }
107    }
108
109    async fn execute_task(&self, task: &TaskConfig) -> Result<()> {
110        use tokio::process::Command;
111
112        let mut cmd = Command::new(&task.command);
113        cmd.args(&task.args);
114        cmd.current_dir(&self.project_root);
115
116        let output = cmd.output().await?;
117        if output.status.success() {
118            println!("Task completed successfully!");
119            Ok(())
120        } else {
121            let stderr = String::from_utf8_lossy(&output.stderr);
122            Err(BuildError::Task(format!("Command failed: {}", stderr)))
123        }
124    }
125
126    pub async fn build(&self) -> Result<()> {
127        println!("Building project...");
128        // 簡易的なビルド処理
129        let output_dir = self.project_root.join("dist");
130        tokio::fs::create_dir_all(&output_dir).await?;
131        println!("Build completed successfully!");
132        Ok(())
133    }
134
135    pub async fn list_tasks(&self) -> Vec<(String, String)> {
136        self.config.tasks.iter()
137            .map(|(name, task)| {
138                let desc = task.description.clone()
139                    .unwrap_or_else(|| format!("Run {}", name));
140                (name.clone(), desc)
141            })
142            .collect()
143    }
144}
145
146// 各モジュールの再エクスポート
147pub mod config;
148pub mod tasks;
149pub mod watcher;
150pub mod utils;