Files
tauri-shadcn-vite-project/src-tauri/src/utils/code_formatter.rs
shenjianZ 55845d2b57 feat: 新增 CSS 和 Rust 专业代码格式化器
集成 lightningcss 和 syn 库,为代码格式化工具添加 CSS 和 Rust 语言的专业格式化能力。同时重构 XML
   格式化器,使用 roxmltree 和 quick-xml 替代纯函数实现,提升解析准确性和性能。
2026-03-17 19:49:13 +08:00

433 lines
13 KiB
Rust

//! 代码格式化工具函数
//!
//! 提供纯函数的代码处理算法
use crate::models::code_format::{CodeFormatConfig, CodeLanguage, FormatMode};
/// 格式化代码字符串
///
/// 对输入的代码字符串进行格式化,支持美化和压缩模式
///
/// # 参数
///
/// * `input` - 输入的代码字符串
/// * `config` - 格式化配置
///
/// # 返回
///
/// 返回格式化后的代码字符串
///
/// # 错误
///
/// 当代码解析失败时返回错误
pub fn format_code(input: &str, config: &CodeFormatConfig) -> Result<String, String> {
if input.trim().is_empty() {
return Err("输入内容不能为空".to_string());
}
match config.mode {
FormatMode::Pretty => prettify_code(input, config),
FormatMode::Compact => compact_code(input, config),
}
}
/// 美化代码字符串
fn prettify_code(input: &str, config: &CodeFormatConfig) -> Result<String, String> {
match config.language {
CodeLanguage::Json => {
// JSON 使用已有的格式化器
use crate::utils::json_formatter;
let json_config = crate::models::json_format::JsonFormatConfig {
indent: config.indent,
sort_keys: false,
mode: crate::models::json_format::FormatMode::Pretty,
};
json_formatter::format_json(input, &json_config)
}
CodeLanguage::Xml => {
// XML 使用已有的格式化器
use crate::utils::xml_formatter;
let xml_config = crate::models::xml_format::XmlFormatConfig {
indent: config.indent,
mode: crate::models::xml_format::FormatMode::Pretty,
};
xml_formatter::format_xml(input, &xml_config)
}
CodeLanguage::Html => {
// HTML 使用已有的格式化器
use crate::utils::html_formatter;
let html_config = crate::models::html_format::HtmlFormatConfig {
indent: config.indent,
mode: crate::models::html_format::FormatMode::Pretty,
};
html_formatter::format_html(input, &html_config)
}
CodeLanguage::Css => {
// CSS 使用专业格式化器
use crate::utils::css_formatter;
let css_config = crate::models::css_format::CssFormatConfig {
indent: config.indent,
mode: crate::models::code_format::FormatMode::Pretty,
};
css_formatter::format_css(input, &css_config)
}
CodeLanguage::Rust => {
// Rust 使用专业格式化器
use crate::utils::rust_formatter;
let rust_config = crate::models::rust_format::RustFormatConfig {
indent: config.indent,
mode: crate::models::code_format::FormatMode::Pretty,
};
rust_formatter::format_rust(input, &rust_config)
}
_ => {
// 其他语言使用通用格式化
generic_prettify(input, config)
}
}
}
/// 压缩代码字符串
fn compact_code(input: &str, config: &CodeFormatConfig) -> Result<String, String> {
match config.language {
CodeLanguage::Json => {
use crate::utils::json_formatter;
let json_config = crate::models::json_format::JsonFormatConfig {
indent: 2,
sort_keys: false,
mode: crate::models::json_format::FormatMode::Compact,
};
json_formatter::format_json(input, &json_config)
}
CodeLanguage::Xml => {
use crate::utils::xml_formatter;
let xml_config = crate::models::xml_format::XmlFormatConfig {
indent: 2,
mode: crate::models::xml_format::FormatMode::Compact,
};
xml_formatter::format_xml(input, &xml_config)
}
CodeLanguage::Html => {
use crate::utils::html_formatter;
let html_config = crate::models::html_format::HtmlFormatConfig {
indent: 2,
mode: crate::models::html_format::FormatMode::Compact,
};
html_formatter::format_html(input, &html_config)
}
CodeLanguage::Css => {
// CSS 使用专业格式化器
use crate::utils::css_formatter;
let css_config = crate::models::css_format::CssFormatConfig {
indent: 2,
mode: crate::models::code_format::FormatMode::Compact,
};
css_formatter::format_css(input, &css_config)
}
CodeLanguage::Rust => {
// Rust 使用专业格式化器
use crate::utils::rust_formatter;
let rust_config = crate::models::rust_format::RustFormatConfig {
indent: 2,
mode: crate::models::code_format::FormatMode::Compact,
};
rust_formatter::format_rust(input, &rust_config)
}
_ => {
// 其他语言使用通用压缩
generic_compact(input)
}
}
}
/// 通用代码美化
fn generic_prettify(input: &str, config: &CodeFormatConfig) -> Result<String, String> {
let indent_str = if config.use_tabs {
"\t".to_string()
} else {
" ".repeat(config.indent as usize)
};
let mut result = String::new();
let mut indent_level = 0;
let mut chars = input.chars().peekable();
let mut in_string = false;
let mut in_comment = false;
let mut in_multiline_comment = false;
let mut string_char = ' ';
let mut prev_char = ' ';
while let Some(c) = chars.next() {
// 处理字符串
if !in_comment && !in_multiline_comment && (c == '"' || c == '\'' || c == '`') {
if !in_string {
in_string = true;
string_char = c;
} else if c == string_char && prev_char != '\\' {
in_string = false;
}
result.push(c);
prev_char = c;
continue;
}
if in_string {
result.push(c);
prev_char = c;
continue;
}
// 处理单行注释
if c == '/' && chars.peek() == Some(&'/') && !in_multiline_comment {
chars.next();
in_comment = true;
result.push_str("//");
continue;
}
if in_comment {
result.push(c);
if c == '\n' {
in_comment = false;
}
prev_char = c;
continue;
}
// 处理多行注释
if c == '/' && chars.peek() == Some(&'*') && !in_comment {
chars.next();
in_multiline_comment = true;
result.push_str("/*");
continue;
}
if in_multiline_comment {
result.push(c);
if c == '*' && chars.peek() == Some(&'/') {
chars.next();
result.push('/');
in_multiline_comment = false;
}
prev_char = c;
continue;
}
// 处理括号和缩进
match c {
'{' | '(' => {
result.push(c);
if c == '{' {
indent_level += 1;
result.push('\n');
result.push_str(&indent_str.repeat(indent_level));
}
}
'}' | ')' => {
if c == '}' && indent_level > 0 {
indent_level -= 1;
if result.ends_with(&indent_str) {
result.truncate(result.len() - indent_str.len());
} else if result.ends_with('\n') {
result.push_str(&indent_str.repeat(indent_level));
} else {
result.push('\n');
result.push_str(&indent_str.repeat(indent_level));
}
}
result.push(c);
}
';' => {
result.push(c);
if !in_string {
result.push('\n');
result.push_str(&indent_str.repeat(indent_level));
}
}
'\n' | '\r' => {
// 跳过多余的换行
if !result.ends_with('\n') {
result.push('\n');
result.push_str(&indent_str.repeat(indent_level));
}
}
' ' | '\t' => {
// 只保留一个空格
if !result.ends_with(' ') && !result.ends_with('\n') && !result.ends_with('\t') {
result.push(' ');
}
}
_ => {
result.push(c);
}
}
prev_char = c;
}
Ok(result.trim().to_string())
}
/// 通用代码压缩
fn generic_compact(input: &str) -> Result<String, String> {
let mut result = String::new();
let mut chars = input.chars().peekable();
let mut in_string = false;
let mut string_char = ' ';
let mut prev_char = ' ';
while let Some(c) = chars.next() {
// 处理字符串
if c == '"' || c == '\'' || c == '`' {
if !in_string {
in_string = true;
string_char = c;
} else if c == string_char && prev_char != '\\' {
in_string = false;
}
result.push(c);
prev_char = c;
continue;
}
if in_string {
result.push(c);
prev_char = c;
continue;
}
// 处理单行注释
if c == '/' && chars.peek() == Some(&'/') {
while let Some(nc) = chars.next() {
if nc == '\n' {
break;
}
}
continue;
}
// 处理多行注释
if c == '/' && chars.peek() == Some(&'*') {
chars.next();
while let Some(nc) = chars.next() {
if nc == '*' && chars.peek() == Some(&'/') {
chars.next();
break;
}
}
continue;
}
// 压缩空格和换行
if c.is_whitespace() {
if !result.is_empty() && !result.ends_with(' ') &&
prev_char.is_ascii_alphanumeric() || prev_char == '_' {
result.push(' ');
}
prev_char = c;
continue;
}
result.push(c);
prev_char = c;
}
Ok(result.trim().to_string())
}
/// 验证代码字符串
pub fn validate_code(input: &str, language: CodeLanguage) -> CodeValidateResult {
if input.trim().is_empty() {
return CodeValidateResult {
is_valid: false,
error_message: Some("输入内容不能为空".to_string()),
error_line: Some(1),
};
}
match language {
CodeLanguage::Json => {
use crate::utils::json_formatter;
let result = json_formatter::validate_json(input);
CodeValidateResult {
is_valid: result.is_valid,
error_message: result.error_message,
error_line: result.error_line,
}
}
CodeLanguage::Xml => {
use crate::utils::xml_formatter;
let result = xml_formatter::validate_xml(input);
CodeValidateResult {
is_valid: result.is_valid,
error_message: result.error_message,
error_line: result.error_line,
}
}
CodeLanguage::Html => {
use crate::utils::html_formatter;
let result = html_formatter::validate_html(input);
CodeValidateResult {
is_valid: result.is_valid,
error_message: result.error_message,
error_line: result.error_line,
}
}
_ => {
// 其他语言的简单验证
CodeValidateResult {
is_valid: true,
error_message: None,
error_line: None,
}
}
}
}
/// 代码验证结果结构
#[derive(Debug, Clone)]
pub struct CodeValidateResult {
pub is_valid: bool,
pub error_message: Option<String>,
pub error_line: Option<usize>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_code_json() {
let input = "{\"name\":\"test\",\"value\":123}";
let config = CodeFormatConfig {
language: CodeLanguage::Json,
..Default::default()
};
let result = format_code(input, &config).unwrap();
assert!(result.contains('\n'));
}
#[test]
fn test_format_code_generic() {
let input = "function test(){let x=1;return x;}";
let config = CodeFormatConfig {
language: CodeLanguage::JavaScript,
..Default::default()
};
let result = format_code(input, &config).unwrap();
assert!(result.contains('\n'));
}
#[test]
fn test_compact_code() {
let input = "function test() {\n let x = 1;\n return x;\n}";
let config = CodeFormatConfig {
language: CodeLanguage::JavaScript,
mode: FormatMode::Compact,
..Default::default()
};
let result = format_code(input, &config).unwrap();
assert!(!result.contains('\n'));
}
}