diff --git a/.gitignore b/.gitignore index ea8c4bf7f35f6f77f75d92ad8ce8349f6e81ddba..3a8cabc9e988187922461e910f02cd5813d0d10c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +.idea diff --git a/README.md b/README.md index 12ce6eff2dfb2316cc3969dff37046a489a3c107..98e89bb05d775308e808aedb915fa7b5cc7d49c5 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ + *--user*,*-u* 用户名 + *--password*,*-p* 用户密码 + *--database*,*-d* 数据库 -+ *--output*,*-o* 输出文件名(默认为*table.docx*) ++ *--output*,*-o* 输出文件名(默认为*数据库名称*) ## 图形界面使用 -使用图形界面的时候,只要在运行了`sql2doc`后的界面中填写数据库对应的 *地址*,*端口号*,*用户名*,*用户密码*和*数据库*,然后点击*生成*按钮,结果就会自动保存在应用程序所有的目录下,文件名为*table.docx*. +使用图形界面的时候,只要在运行了`sql2doc`后的界面中填写数据库对应的 *地址*,*端口号*,*用户名*,*用户密码*和*数据库*,然后点击*生成*按钮,结果就会自动保存在应用程序所有的目录下,文件名为*数据库名称.docx*. diff --git a/sql2doc_core/src/lib.rs b/sql2doc_core/src/lib.rs index 44cbb9a80c39a25059721314a0dcfc5bb129cc8d..20132081a66ca74ea38eaaa558acf17e03346701 100644 --- a/sql2doc_core/src/lib.rs +++ b/sql2doc_core/src/lib.rs @@ -1,7 +1,11 @@ pub use config::SqlConfig; use serde::Deserialize; -use std::borrow::Cow; -use std::collections::HashMap; +use std::{ + collections::BTreeMap, + fs::File, + io::{Seek, Write}, + path::Path, +}; use thiserror::Error; use to_word::write; @@ -36,6 +40,15 @@ pub enum SqlType { Mssql, } +/// 根据不同的SQL类型,异步执行数据库查询并生成结果文件。 +/// +/// 参数: +/// - `sql_type`:查询使用的SQL类型,支持MySQL和SQL Server。 +/// - `config`:数据库配置信息,用于连接和查询数据库。 +/// +/// 返回值: +/// - 成功时返回输出文件的路径作为字符串。 +/// - 失败时返回一个包含错误信息的`RunError`。 pub async fn run(sql_type: SqlType, config: &SqlConfig) -> Result { let row = match sql_type { SqlType::Mysql => mysql::fetch_data(config).await?, @@ -44,13 +57,49 @@ pub async fn run(sql_type: SqlType, config: &SqlConfig) -> Result>(); let groups = group_table(&row); let output = &get_output(config); - write(&groups, output)?; + let path = Path::new(output); + let mut file = File::create(path).unwrap(); + write(&groups, &mut file)?; Ok(output.to_string()) } + +/// 异步执行数据库写入任务。 +/// +/// # 参数 +/// - `sql_type`: 数据库类型,决定使用哪种数据库协议来执行数据抓取。 +/// - `config`: 一个引用,指向包含数据库连接配置信息的结构体。 +/// - `writer`: 一个可变引用,指向实现了`Write`和`Seek`特性的写入目标,用于输出处理后的数据。 +/// +/// ## 泛型约束 +/// - `T`: 必须实现`Write`和`Seek`特质。 +/// +/// # 返回值 +/// - `Result<(), RunError>`:操作成功返回`Ok(())`,失败则返回具体的错误信息。 +/// +/// # 注意 +/// 目前仅支持MySQL和MSSQL两种数据库类型。 +pub async fn run_writer( + sql_type: SqlType, + config: &SqlConfig, + writer: &mut T, +) -> Result<(), RunError> +where + T: Write + Seek, +{ + let row = match sql_type { + SqlType::Mysql => mysql::fetch_data(config).await?, + SqlType::Mssql => mssql::fetch_data(config).await?, + }; + let row_vec = row.iter().collect::>(); + let grouped_data = group_table(&row_vec); + write(&grouped_data, writer)?; + Ok(()) +} + /// 按表名分组 -fn group_table<'a>(row: &'a [&'a Info]) -> HashMap<&'a String, Vec<&'a Info>> { - row.iter().fold(HashMap::new(), |mut map, cur| { +fn group_table<'a>(row: &'a [&'a Info]) -> BTreeMap<&'a String, Vec<&'a Info>> { + row.iter().fold(BTreeMap::new(), |mut map, cur| { let key = &cur.table_name; let entry = map.entry(key).or_default(); entry.push(cur); @@ -58,12 +107,8 @@ fn group_table<'a>(row: &'a [&'a Info]) -> HashMap<&'a String, Vec<&'a Info>> { }) } -const OUTPUT: &str = "./table.docx"; /// 获取输出路径 -fn get_output(config: &SqlConfig) -> Cow<'static, str> { - config - .output - .as_ref() - .map(|v| Cow::Owned(v.clone())) - .unwrap_or(Cow::Borrowed(OUTPUT)) +fn get_output(config: &SqlConfig) -> String { + let name = format!("{}.docx", config.database); + config.output.as_ref().cloned().unwrap_or(name) } diff --git a/sql2doc_core/src/to_word.rs b/sql2doc_core/src/to_word.rs index 4f1851a591322e723373c19c8e1bad0b7fcef524..545bf004ff0d87bcdce7f1fe6b8906afc759804e 100644 --- a/sql2doc_core/src/to_word.rs +++ b/sql2doc_core/src/to_word.rs @@ -10,20 +10,23 @@ use docx_rs::TableCell; use docx_rs::TableLayoutType; use docx_rs::TableRow; use docx_rs::WidthType; -use std::collections::HashMap; -use std::fs::File; -use std::path::Path; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::Write; const HEADER_FILL: &str = "#C1D6F0"; const HEADER_COLOR: &str = "#000"; -pub(crate) fn write(groups: &HashMap<&String, Vec<&Info>>, output: &str) -> Result<(), DocxError> { - let path = Path::new(output); - let file = File::create(path).unwrap(); +pub(crate) fn write( + groups: &BTreeMap<&String, Vec<&Info>>, + writer: &mut T, +) -> Result<(), DocxError> +where + T: Write + Seek, +{ let mut docx = Docx::new(); - let mut list = groups.iter().collect::>(); - list.sort_by(|a, b| a.0.cmp(b.0)); - list.iter() + groups + .iter() .enumerate() .map(|(index, (name, list))| { let name = format!("表{}", name); @@ -48,7 +51,7 @@ pub(crate) fn write(groups: &HashMap<&String, Vec<&Info>>, output: &str) -> Resu .add_paragraph(empty_line()) .add_paragraph(empty_line()); }); - docx.build().pack(file)?; + docx.build().pack(writer)?; Ok(()) }