From 1e83228392e8602663499eac79c7b55ff397ae64 Mon Sep 17 00:00:00 2001 From: hummel mao Date: Sat, 30 Aug 2025 19:37:14 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(build=5Ferrors):=20=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=20build=5Ferrors=20=E5=8C=85=EF=BC=8C=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=8A=A5=E9=94=99=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModelVis/Cargo.lock | 40 ++++++++- .../ModelVis/app/src-tauri/Cargo.toml | 9 +- .../ModelVis/app/src-tauri/build.rs | 13 +++ .../ModelVis/app/src-tauri/errors/file.yaml | 56 +++++++++++++ .../ModelVis/app/src-tauri/errors/fsg.yaml | 20 +++++ .../ModelVis/app/src-tauri/src/commands.rs | 30 +++++-- .../app/src-tauri/src/errors/app_error.rs | 41 +++++++++ .../app/src-tauri/src/errors/convert.rs | 20 +++++ .../ModelVis/app/src-tauri/src/errors/mod.rs | 6 ++ .../ModelVis/app/src-tauri/src/lib.rs | 1 + .../ModelVis/rust/build_errors/Cargo.toml | 13 +++ .../rust/build_errors/src/generator.rs | 84 +++++++++++++++++++ .../ModelVis/rust/build_errors/src/lib.rs | 69 +++++++++++++++ .../ModelVis/rust/build_errors/src/loader.rs | 34 ++++++++ .../rust/build_errors/src/validator.rs | 21 +++++ .../ModelVis/rust/path-validator/src/lib.rs | 9 +- 16 files changed, 450 insertions(+), 16 deletions(-) create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/file.yaml create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/fsg.yaml create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/app_error.rs create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/convert.rs create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/Cargo.toml create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/loader.rs create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/validator.rs diff --git a/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock b/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock index 5956be65d..1a8c7cb58 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock +++ b/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock @@ -247,6 +247,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "build_errors" +version = "0.1.0" +dependencies = [ + "anyhow", + "serde", + "serde_yaml", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -2519,6 +2528,14 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "path_validator" +version = "0.2.0" +dependencies = [ + "libc", + "windows", +] + [[package]] name = "pathdiff" version = "0.2.3" @@ -3427,6 +3444,19 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.10.0", + "itoa 1.0.15", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serialize-to-javascript" version = "0.1.1" @@ -4435,6 +4465,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "url" version = "2.5.4" @@ -4499,17 +4535,19 @@ version = "0.1.0" dependencies = [ "ahash", "anyhow", + "build_errors", "csv_parser", "fsg", "layout", "parser", + "path_validator", "serde", - "serde_json", "smartstring", "tauri", "tauri-build", "tauri-plugin-dialog", "tauri-plugin-shell", + "thiserror 2.0.12", "tokio", ] diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml index 2f20e6147..035746344 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml @@ -9,18 +9,21 @@ crate-type = ["lib", "cdylib", "staticlib"] [build-dependencies] tauri-build = { version = "2.0.5", features = [] } +build_errors = { path = "../../rust/build_errors" } [dependencies] tauri = { version = "2.5.1", features = ["unstable", "devtools"] } tauri-plugin-dialog = "2.2.1" tauri-plugin-shell = "2.2.1" +tokio = { version = "1.45.0", features = ["sync", "macros"] } smartstring = { workspace = true } ahash = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true +anyhow = { workspace = true } +thiserror = { workspace = true } +path_validator = { path = "../../rust/path-validator" } csv_parser = { path = "../../rust/csv_parser" } parser = { path = "../../rust/parser" } layout = { path = "../../rust/layout" } fsg = { path = "../../rust/fsg" } -anyhow = { workspace = true } -tokio = "1.45.0" +build_errors = { path = "../../rust/build_errors" } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/build.rs b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/build.rs index d860e1e6a..4175dbdec 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/build.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/build.rs @@ -1,3 +1,16 @@ +use std::path::Path; +use build_errors::generate_error_modules; + +fn build_error_files() { + println!("cargo:rerun-if-changed=errors/"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=build_errors"); + + let error_dir = Path::new("errors"); + generate_error_modules(error_dir).expect("generate error modules failed!"); +} + fn main() { + build_error_files(); tauri_build::build() } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/file.yaml b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/file.yaml new file mode 100644 index 000000000..71ecc9108 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/file.yaml @@ -0,0 +1,56 @@ +# 文件模块错误码 +meta: + source_type: FileErrorEnum + target_variant: File + start: 1000 + end: 1999 + +# 错误定义 +errors: + InvalidFileName: + code: 1101 + en: "Invalid file name: {}" + zh: "文件名无效: {}" + fields: [path] + + NoSuchFile: + code: 1102 + en: "File not found: {}" + zh: "文件未找到: {}" + fields: [path] + + DirNotSupported: + code: 1103 + en: "Directory not supported: {}" + zh: "目录不被支持: {}" + fields: [path] + + UnknownError: + code: 1104 + en: "Unknown error: {}" + zh: "未知错误: {}" + fields: [error] + + UnsupportedFIleType: + code: 1105 + en: "Unsupported file type: {}" + zh: "不支持的文件类型: {}" + fields: [path] + + BadPermission: + code: 1106 + en: "Bad permission: {}" + zh: "文件权限错误: {}" + fields: [path] + + InconsistentOwner: + code: 1107 + en: "Inconsistent owner: {}" + zh: "所有者不一致: {}" + fields: [path] + + BadFile: + code: 1108 + en: "Bad file: {}" + zh: "坏文件: {}" + fields: [path] \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/fsg.yaml b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/fsg.yaml new file mode 100644 index 000000000..b4b747130 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/errors/fsg.yaml @@ -0,0 +1,20 @@ +# FSG模块错误码 +meta: + source_type: FSGErrorEnum + target_variant: FSG + start: 2000 + end: 2999 + +# 错误定义 +errors: + OverDepth10: + code: 2101 + en: "Over depth 10!" + zh: "图深度超过 10" + fields: [] + + NoSuchSubgraph: + code: 2102 + en: "Subgraph with name '{}' not found" + zh: "子图 '{}' 未找到" + fields: [name] \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/commands.rs b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/commands.rs index 9f5bfbc2b..4a80e78a4 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/commands.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/commands.rs @@ -1,17 +1,19 @@ use std::{env::home_dir, fs::{create_dir, File}, io, io::{BufReader, BufWriter}, path::Path, time::Instant}; use std::path::PathBuf; use ahash::{HashMap, HashMapExt}; -use anyhow::{anyhow, Result}; +use anyhow::Result; use layout::{layout, Graph, GraphEdge, GraphNode, Key, KeyCodecExt}; use parser::{parse_bin, Model, StdString}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use smartstring::alias::String; use fsg::{result::JSONResult, fsgs_model}; -use tauri::{path::BaseDirectory, AppHandle, Emitter, Manager, Result as InvokeResult}; +use tauri::{path::BaseDirectory, AppHandle, Emitter, Manager}; use tauri::async_runtime::spawn_blocking; use tauri_plugin_shell::ShellExt; use csv_parser::operator::OperatorGroup; use csv_parser::parse_operator_csv; +use path_validator::{FileErrorKind, FileExt}; +use crate::errors::{app_error::AppResult, convert::convert_error, fsg::FSGErrorEnum}; #[allow(non_snake_case)] #[derive(Debug, Clone, Serialize, Deserialize)] @@ -199,10 +201,11 @@ pub struct GraphLayer { } #[tauri::command] -pub fn layout_bin(handle: AppHandle, path: String) -> InvokeResult { +pub fn layout_bin(handle: AppHandle, path: String) -> AppResult { let script_path = handle.path().resolve("resources/bin.ts", BaseDirectory::Resource)?; let s1 = Instant::now(); + check_path(&path)?; let model = parse_bin(&path)?; println!("parse costs {:?}", s1.elapsed()); @@ -390,7 +393,8 @@ fn read_from<'a, T: DeserializeOwned, P: AsRef>(path: P) -> T { } #[tauri::command] -pub async fn mine_fsg(path: &str, name: &str, min_sup: usize, min: usize, max: usize) -> InvokeResult> { +pub async fn mine_fsg(path: &str, name: &str, min_sup: usize, min: usize, max: usize) -> AppResult> { + check_path(&path)?; let mut model = parse_bin(&path)?; let single_graph = if model.name == name { Model { @@ -405,9 +409,9 @@ pub async fn mine_fsg(path: &str, name: &str, min_sup: usize, min: usize, max: u }; wrap(fsgs_model(&single_graph, min_sup, min, max)) } -fn recursive_get_single_graph(model: &mut Model, name: &str, depth: usize) -> Result { +fn recursive_get_single_graph(model: &mut Model, name: &str, depth: usize) -> AppResult { if depth >= 10 { - return Err(anyhow!("Over depth 10!")); + return Err(FSGErrorEnum::OverDepth10.into()); } if let Some(target) = model.subgraphes.get_mut(name) { return Ok(Model { @@ -424,15 +428,16 @@ fn recursive_get_single_graph(model: &mut Model, name: &str, depth: usize) -> Re } } - Err(anyhow!("Subgraph with name '{}' not found", name)) + Err(FSGErrorEnum::NoSuchSubgraph(name.to_string()).into()) } #[tauri::command] -pub async fn analyze_duration(path: &str) -> InvokeResult> { +pub async fn analyze_duration(path: &str) -> AppResult> { + check_path(path)?; wrap(parse_operator_csv(path)) } -fn wrap(src: Result) -> InvokeResult { +fn wrap(src: Result) -> AppResult { src.map_err(|e| e.into()) } @@ -479,3 +484,10 @@ fn create_dir_750>(path: P) -> io::Result<()> { Ok(()) } +fn check_path(path: &str) -> AppResult<()> { + let result = FileExt::validate(path); + match result { + FileErrorKind::ErrorNone => Ok(()), + other => Err(convert_error(other)), + } +} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/app_error.rs b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/app_error.rs new file mode 100644 index 000000000..b26499b90 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/app_error.rs @@ -0,0 +1,41 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ +use serde::Serialize; +use thiserror::Error; + +#[derive(Error, Debug, Serialize)] +pub enum AppError { + #[error("{message_en}")] + File { + code: u32, + message_en: String, + message_cn: String, + }, + #[error("{message_en}")] + FSG { + code: u32, + message_en: String, + message_cn: String, + }, + #[error("{message_en}")] + Anyhow { + code: u32, + message_en: String, + message_cn: String, + backtrace: String, + } +} + +pub type AppResult = Result; + +impl From for AppError { + fn from(error: anyhow::Error) -> Self { + AppError::Anyhow { + code: 9999, // 通用内部错误码 + message_en: "An internal error occurred".to_string(), + message_cn: "发生内部错误".to_string(), + backtrace: error.to_string(), + } + } +} \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/convert.rs b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/convert.rs new file mode 100644 index 000000000..bc24d5707 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/convert.rs @@ -0,0 +1,20 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ +use path_validator::FileErrorKind; +use crate::errors::app_error::AppError; +use crate::errors::file::FileErrorEnum; + +pub fn convert_error(result: FileErrorKind<'_>) -> AppError { + match result { + FileErrorKind::ErrorNone => unreachable!(), + FileErrorKind::InvalidFileName(p) => FileErrorEnum::InvalidFileName(p.to_string()).into(), + FileErrorKind::NoSuchFile(p) => FileErrorEnum::NoSuchFile(p.to_string()).into(), + FileErrorKind::DirNotSupported(p) => FileErrorEnum::DirNotSupported(p.to_string()).into(), + FileErrorKind::UnknownError(e, _) => FileErrorEnum::UnknownError(e).into(), + FileErrorKind::UnsupportedFIleType(p) => FileErrorEnum::UnsupportedFIleType(p.to_string()).into(), + FileErrorKind::BadPermission(p) => FileErrorEnum::BadPermission(p.to_string()).into(), + FileErrorKind::InconsistentOwner(p) => FileErrorEnum::InconsistentOwner(p.to_string()).into(), + FileErrorKind::BadFile(p) => FileErrorEnum::BadFile(p.to_string()).into(), + } +} \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs new file mode 100644 index 000000000..8ee2bcc36 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs @@ -0,0 +1,6 @@ +use build_errors::include_error_module; + +include_error_module!(file); +include_error_module!(fsg); +pub mod app_error; +pub mod convert; \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/lib.rs index e37f2d4a1..289953e88 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/lib.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/lib.rs @@ -1,4 +1,5 @@ mod commands; +mod errors; use commands::{layout_bin, mine_fsg, analyze_duration}; diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/Cargo.toml b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/Cargo.toml new file mode 100644 index 000000000..a7b323b76 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "build_errors" +version = "0.1.0" +edition = "2024" +description = "Codegen for error enums from YAML" + +[lib] +proc-macro = false # 我们不是 proc-macro,是 build-dep + +[dependencies] +serde_yaml = "0.9.34" +serde = { workspace = true, features = ["derive"] } +anyhow = { workspace = true } \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs new file mode 100644 index 000000000..440719b5c --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs @@ -0,0 +1,84 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ +use std::{env, fs}; +use std::path::Path; +use super::{ModuleMap, ERROR_FILE}; + +pub fn generate(modules: &ModuleMap) -> Result<(), std::io::Error> { + let prefix = env::var("OUT_DIR").unwrap(); + let file_name = &format!("{}/{}", &prefix, ERROR_FILE); + let out_dir = Path::new(file_name); + fs::create_dir_all(out_dir)?; + + let mut mod_lines = vec![]; + + for (module_name, module) in modules { + let file_path = out_dir.join(format!("{}.rs", module_name)); + let mut content = String::new(); + + content.push_str("// === AUTO-GENERATED ===\n"); + content.push_str(&format!("// Source: errors/{}.yaml\n", module_name)); + content.push_str("// Tool: build.rs\n"); + content.push_str("// DO NOT EDIT MANUALLY\n"); + content.push_str("// To update: modify errors/*.yaml and run `cargo build`\n//\n"); + content.push_str("// @generated\n"); + + content.push_str("use crate::errors::app_error::AppError;\n"); + + // 生成 enum + content.push_str("#[derive(Debug)]\n"); + content.push_str(&format!("pub enum {} {{\n", module.meta.source_enum)); + for (name, item) in &module.errors { + if item.fields.is_empty() { + content.push_str(&format!(" {},\n", name)); + } else { + let fields = item.fields.iter().map(|_| "String".to_string()).collect::>().join(", "); + content.push_str(&format!(" {}({}),\n", name, fields)); + } + } + content.push_str("}\n\n"); + + // 生成 From for AppError + content.push_str(&format!("impl From<{}> for AppError {{\n", module.meta.source_enum)); + content.push_str(&format!(" fn from(kind: {}) -> Self {{\n", module.meta.source_enum)); + content.push_str(" match kind {\n"); + for (name, item) in &module.errors { + let fields: Vec<&str> = item.fields.iter().map(|s| s.as_str()).collect(); + let fields_str = fields.join(", "); + content.push_str(&format!(" {}::{}", module.meta.source_enum, name)); + if !fields.is_empty() { + content.push_str("("); + content.push_str(&fields_str); + content.push_str(")"); + } + content.push_str(&format!(" => AppError::{} {{\n", module.meta.target_variant)); + content.push_str(&format!(" code: {},\n", item.code)); + if !fields.is_empty() { + content.push_str(&format!(" message_en: format!(\"{}\", {}),\n", item.en, fields_str)); + content.push_str(&format!(" message_cn: format!(\"{}\", {}),\n", item.zh, fields_str)); + } else { + content.push_str(&format!(" message_en: \"{}\".to_string(),\n", item.en)); + content.push_str(&format!(" message_cn: \"{}\".to_string(),\n", item.zh)); + } + content.push_str(" },\n"); + } + content.push_str(" }\n"); + content.push_str(" }\n"); + content.push_str("}\n"); + + fs::write(&file_path, content)?; + println!("✅ 生成: {}", file_path.display()); + mod_lines.push(module_name.as_str()); + } + + let mut include_code = String::new(); + include_code += "// Auto-generated - DO NOT EDIT\n"; + include_code += "use build_errors::include_error_module;\n\n"; + for name in mod_lines { + include_code += &format!("include_error_module!({});\n", name); + } + fs::write(out_dir.join("include_all_generated.rs"), include_code)?; + println!("✅ 生成: {}", out_dir.join("include_all_generated.rs").display()); + Ok(()) +} \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs new file mode 100644 index 000000000..3e6c53c2e --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs @@ -0,0 +1,69 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ +use std::collections::HashMap; +use std::path::Path; +use serde::Deserialize; +use anyhow::Result; + +pub mod loader; +pub mod validator; +pub mod generator; + +// 公共类型定义(供所有模块使用) +#[derive(Debug, Deserialize)] +pub struct ModuleMeta { + #[serde(rename = "source_type")] + pub source_enum: String, + #[serde(rename = "target_variant")] + pub target_variant: String, + pub start: u32, + pub end: u32, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ErrorItem { + pub code: u32, + pub en: String, + pub zh: String, + pub fields: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct ErrorModule { + pub meta: ModuleMeta, + pub errors: HashMap, +} + +pub type ModuleMap = HashMap; + +static ERROR_FILE: &str = "/generated_errors/"; + +pub fn generate_error_modules(input_dir: &Path) -> Result<()> { + // 1. 加载所有错误配置 + let config = loader::load_all_errors(input_dir)?; + // 2. 校验错误码范围 + validator::validate(&config)?; + // 3. 生成 Rust 代码 + generator::generate(&config)?; + Ok(()) +} + +// 可选:提供一个宏,用于引入所有 +#[macro_export] +macro_rules! include_all_errors { + () => { + // 包含 build.rs 生成的“宏调用文件” + include!(concat!(env!("OUT_DIR"), "/generated_errors/", "/include_all_generated.rs")); + }; +} + +// 通用宏:引入单个模块 +#[macro_export] +macro_rules! include_error_module { + ($name:ident) => { + pub mod $name { + include!(concat!(env!("OUT_DIR"), "/generated_errors/", stringify!($name), ".rs")); + } + }; +} \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/loader.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/loader.rs new file mode 100644 index 000000000..7e4e425d1 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/loader.rs @@ -0,0 +1,34 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ +use std::collections::HashMap; +use anyhow::Result; +use std::fs; +use std::path::Path; +use super::{ModuleMap}; + +pub fn load_all_errors(errors_dir: &Path) -> Result { + let mut modules = HashMap::new(); + + for entry in fs::read_dir(errors_dir)? { + let entry = entry?; + let path = entry.path(); + + if is_yaml_file(&path) { + let module_name = match path.file_stem().and_then(|s| s.to_str()) { + Some(name) => name.to_string(), + _ => continue, + }; + let yaml_content = fs::read_to_string(&path)?; + modules.insert(module_name, serde_yaml::from_str(&yaml_content) + .map_err(|e| anyhow::anyhow!("Invalid format in {}: {}", &path.display(), e))?); + } + } + + Ok(modules) +} + +fn is_yaml_file(path: &Path) -> bool { + matches!(path.extension().and_then(|s| s.to_str()), + Some("yaml") | Some("yml")) +} \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/validator.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/validator.rs new file mode 100644 index 000000000..910d58805 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/validator.rs @@ -0,0 +1,21 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ +use super::{ModuleMap}; +use anyhow::{format_err, Result}; + +pub fn validate(modules: &ModuleMap) -> Result<()> { + for (module_name, module) in modules { + let range = &module.meta; + for (name, item) in &module.errors { + if item.code < range.start || item.code >= range.end { + return Err(format_err!( + "❌ [{}/{}] code={} 超出范围 [{}, {})", + module_name, name, item.code, range.start, range.end + ).into()); + } + } + } + + Ok(()) +} \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs index 107c6c5e5..8ce599a32 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs @@ -1,3 +1,6 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ //! ### Logging Unimplemented //! Many error handling in the current module should be logged, //! and since the log module has not yet been completed, @@ -23,7 +26,7 @@ use windows::{ use self::FileErrorKind::*; -enum FileErrorKind<'a> { +pub enum FileErrorKind<'a> { ErrorNone, InvalidFileName(&'a str), NoSuchFile(&'a str), @@ -35,10 +38,10 @@ enum FileErrorKind<'a> { BadFile(&'a str), } -struct FileExt; +pub struct FileExt; impl FileExt { - fn validate(path: &str) -> FileErrorKind { + pub fn validate(path: &str) -> FileErrorKind { let mut metadata = fs::metadata(path); match metadata { -- Gitee From 995d4f6d51145a8915483a5244f1b55560d31ba0 Mon Sep 17 00:00:00 2001 From: hummel mao Date: Mon, 1 Sep 2025 10:12:54 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(path=5Fvalidate):=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=20windows=20=E7=9A=84=E5=B1=9E=E4=B8=BB=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModelVis/rust/path-validator/src/lib.rs | 78 +------------------ 1 file changed, 2 insertions(+), 76 deletions(-) diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs index 8ce599a32..e5778ebef 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/path-validator/src/lib.rs @@ -8,20 +8,13 @@ #![feature(io_error_more)] -use std::{fs, fs::Metadata, io::ErrorKind}; +use std::{fs, io::ErrorKind}; #[cfg(windows)] use windows::{ Win32::{ - Foundation::{CloseHandle, ERROR_SUCCESS, HANDLE, HLOCAL, LocalFree}, - Security::{ - Authorization::{GetNamedSecurityInfoA, SE_FILE_OBJECT}, - EqualSid, GetTokenInformation, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, PSID, - TOKEN_QUERY, TOKEN_USER, TokenUser, - }, - System::Threading::{GetCurrentProcess, OpenProcessToken}, + Foundation::{CloseHandle, HANDLE, HLOCAL, LocalFree}, }, - core::PCSTR, }; use self::FileErrorKind::*; @@ -65,11 +58,6 @@ impl FileExt { } } - #[cfg(windows)] - if !check_owner(path) { - return InconsistentOwner(path); - } - #[cfg(unix)] if !check_owner(meta) { return InconsistentOwner(path); @@ -97,68 +85,6 @@ fn check_owner(meta: &Metadata) -> bool { uid == euid } -#[cfg(windows)] -fn check_owner(path: &str) -> bool { - use std::ptr::null_mut; - - unsafe { - let psd: *mut PSECURITY_DESCRIPTOR = null_mut(); - let psid_owner: *mut PSID = null_mut(); - - if GetNamedSecurityInfoA( - PCSTR(path.as_ptr()), - SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION, - Some(psid_owner), - None, - None, - None, - psd, - ) != ERROR_SUCCESS - { - return false; - } - - let mut token_handle = HANDLE::default(); - if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle).is_err() { - return false; - } - - let mut token_info_len = 0; - if GetTokenInformation(token_handle, TokenUser, None, 0, &mut token_info_len).is_err() { - return false; - } - - let token_user = libc::malloc(token_info_len as usize); - - if GetTokenInformation( - token_handle, - TokenUser, - Some(token_user), - token_info_len, - &mut token_info_len, - ) - .is_err() - { - libc::free(token_user); - local_free_rs(psd); - close_handle_rs(token_handle); - - return false; - } - - let process_sid = (*(token_user as *mut TOKEN_USER)).User.Sid; - - let res = EqualSid(*psd.cast(), process_sid).is_ok(); - - libc::free(token_user); - local_free_rs(psd); - close_handle_rs(token_handle); - - res - } -} - #[cfg(windows)] fn local_free_rs(ptr: *mut T) { if !ptr.is_null() { -- Gitee From c38eb5519769367dfee49b6eae0bb21ed3c4c86f Mon Sep 17 00:00:00 2001 From: hummel mao Date: Mon, 1 Sep 2025 14:35:04 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat(command):=20=E6=B7=BB=E5=8A=A0=20serde?= =?UTF-8?q?=5Fjson?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModelVis/Cargo.lock | 15 ++++++++++++++- .../ModelVis/app/src-tauri/Cargo.toml | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock b/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock index 1a8c7cb58..b31253b3c 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock +++ b/plugins/mindstudio-insight-plugins/ModelVis/Cargo.lock @@ -3450,7 +3450,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.9.0", "itoa 1.0.15", "ryu", "serde", @@ -4217,10 +4217,22 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2", + "tokio-macros", "tracing", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "tokio-util" version = "0.7.15" @@ -4542,6 +4554,7 @@ dependencies = [ "parser", "path_validator", "serde", + "serde_json", "smartstring", "tauri", "tauri-build", diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml index 035746344..81cb7ca79 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml @@ -19,6 +19,7 @@ tokio = { version = "1.45.0", features = ["sync", "macros"] } smartstring = { workspace = true } ahash = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true anyhow = { workspace = true } thiserror = { workspace = true } path_validator = { path = "../../rust/path-validator" } -- Gitee From 6e673331a9dc05d0261db27c07d1414b22cadedb Mon Sep 17 00:00:00 2001 From: hummel mao Date: Mon, 1 Sep 2025 15:33:00 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat(build=5Ferrors):=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=97=A0=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModelVis/app/src-tauri/src/errors/mod.rs | 3 +++ .../ModelVis/rust/build_errors/src/generator.rs | 12 ------------ .../ModelVis/rust/build_errors/src/lib.rs | 9 --------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs index 8ee2bcc36..ce8f9da2b 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/src/errors/mod.rs @@ -1,3 +1,6 @@ +/* + * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ use build_errors::include_error_module; include_error_module!(file); diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs index 440719b5c..166962260 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/generator.rs @@ -10,9 +10,6 @@ pub fn generate(modules: &ModuleMap) -> Result<(), std::io::Error> { let file_name = &format!("{}/{}", &prefix, ERROR_FILE); let out_dir = Path::new(file_name); fs::create_dir_all(out_dir)?; - - let mut mod_lines = vec![]; - for (module_name, module) in modules { let file_path = out_dir.join(format!("{}.rs", module_name)); let mut content = String::new(); @@ -69,16 +66,7 @@ pub fn generate(modules: &ModuleMap) -> Result<(), std::io::Error> { fs::write(&file_path, content)?; println!("✅ 生成: {}", file_path.display()); - mod_lines.push(module_name.as_str()); } - let mut include_code = String::new(); - include_code += "// Auto-generated - DO NOT EDIT\n"; - include_code += "use build_errors::include_error_module;\n\n"; - for name in mod_lines { - include_code += &format!("include_error_module!({});\n", name); - } - fs::write(out_dir.join("include_all_generated.rs"), include_code)?; - println!("✅ 生成: {}", out_dir.join("include_all_generated.rs").display()); Ok(()) } \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs index 3e6c53c2e..802064aa0 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/build_errors/src/lib.rs @@ -49,15 +49,6 @@ pub fn generate_error_modules(input_dir: &Path) -> Result<()> { Ok(()) } -// 可选:提供一个宏,用于引入所有 -#[macro_export] -macro_rules! include_all_errors { - () => { - // 包含 build.rs 生成的“宏调用文件” - include!(concat!(env!("OUT_DIR"), "/generated_errors/", "/include_all_generated.rs")); - }; -} - // 通用宏:引入单个模块 #[macro_export] macro_rules! include_error_module { -- Gitee