kotoba_formatter/
formatter.rs

1//! フォーマッター実装モジュール
2
3use super::{FormatterConfig, FormatResult, FormatRules};
4use std::path::PathBuf;
5use tokio::fs;
6
7/// メインのフォーマッター実装
8#[derive(Debug)]
9pub struct CodeFormatter {
10    config: FormatterConfig,
11    rules: FormatRules,
12}
13
14impl CodeFormatter {
15    /// 新しいフォーマッターを作成
16    pub fn new(config: FormatterConfig) -> Self {
17        let rules = FormatRules::new(&config);
18        Self { config, rules }
19    }
20
21    /// 設定を取得
22    pub fn config(&self) -> &FormatterConfig {
23        &self.config
24    }
25
26    /// 設定を更新
27    pub fn set_config(&mut self, config: FormatterConfig) {
28        self.config = config.clone();
29        self.rules = FormatRules::new(&config);
30    }
31
32    /// 単一のファイルをフォーマット
33    pub async fn format_file(&self, file_path: &PathBuf) -> Result<FormatResult, Box<dyn std::error::Error>> {
34        let content = fs::read_to_string(file_path).await?;
35        let mut result = FormatResult::new(file_path.clone(), content);
36
37        match self.format_content(&result.original_content).await {
38            Ok(formatted) => {
39                result.set_formatted_content(formatted);
40                Ok(result)
41            }
42            Err(e) => {
43                result.set_error(e.to_string());
44                Ok(result)
45            }
46        }
47    }
48
49    /// コンテンツをフォーマット
50    pub async fn format_content(&self, content: &str) -> Result<String, Box<dyn std::error::Error>> {
51        // ルールを適用
52        let formatted = self.rules.apply(content)?;
53
54        // 最終的なクリーンアップ
55        let cleaned = self.final_cleanup(&formatted);
56
57        Ok(cleaned)
58    }
59
60    /// 最終的なクリーンアップ処理
61    fn final_cleanup(&self, content: &str) -> String {
62        let mut lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
63
64        // 末尾の空行を削除
65        while let Some(line) = lines.last() {
66            if line.trim().is_empty() {
67                lines.pop();
68            } else {
69                break;
70            }
71        }
72
73        // 各行の末尾の空白を削除
74        for line in &mut lines {
75            *line = line.trim_end().to_string();
76        }
77
78        lines.join(&self.get_line_ending())
79    }
80
81    /// 改行文字を取得
82    fn get_line_ending(&self) -> String {
83        match self.config.line_ending {
84            super::LineEnding::Lf => "\n".to_string(),
85            super::LineEnding::Crlf => "\r\n".to_string(),
86            super::LineEnding::Auto => "\n".to_string(),
87        }
88    }
89}
90
91/// ユーティリティ関数
92pub async fn format_file_with_config(
93    file_path: &PathBuf,
94    config: &FormatterConfig,
95) -> Result<FormatResult, Box<dyn std::error::Error>> {
96    let formatter = CodeFormatter::new(config.clone());
97    formatter.format_file(file_path).await
98}
99
100pub async fn format_content_with_config(
101    content: &str,
102    config: &FormatterConfig,
103) -> Result<String, Box<dyn std::error::Error>> {
104    let formatter = CodeFormatter::new(config.clone());
105    formatter.format_content(content).await
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_formatter_creation() {
114        let config = FormatterConfig::default();
115        let formatter = CodeFormatter::new(config);
116        assert_eq!(formatter.config().indent_width, 4);
117    }
118
119    #[tokio::test]
120    async fn test_format_simple_content() {
121        let config = FormatterConfig::default();
122        let formatter = CodeFormatter::new(config);
123
124        let input = "graph test{node a}";
125        let result = formatter.format_content(input).await.unwrap();
126
127        // フォーマット後の結果を検証
128        assert!(!result.is_empty());
129    }
130}