测试Baidu Comate编写rust工具的能力

126 阅读6分钟

背景

测试目的:

  • 测试Baidu Comate在代码生成方面的能力。
  • 学习rust语言。

测试方式:

  • 编写一个rust工具,用于快速解析二进制bitFlag字段所代表的含义。
  • 由Comate生成的代码或文字会有特殊的标识。

需求分析

在数据库字段中,总会存在使用二进制来判断一系列状态的情况。 但通常是显示为十进制数字,每次解读都需要将其转换为二进制,再逐位比较,才能搞明白各个状态。 因此本工具的功能十分简单,就是将二进制转换为十进制,并输出其含义。

process.png

本工具的主要功能为:

  1. 根据输入的字段和字段值,解析出其含义。
  2. 设置字段和字段每个bit的含义。
  3. 更新字段和字段每个bit的含义。
  4. 美观的TUI界面。

工具设计

存储方案

本质是一个实现存储的Map,其key为字段名,value为字段值。 value值很复杂,通常是对每个bit的含义进行描述。 例如第几位是什么含义。

对此数据的读取和存储是工具的核心内容。 而说到数据存储,自然而然会有以下方案**【Comate生成】**:

方式优点缺点
文件存储1. 易于实现和理解。
2. 不需要额外的数据库软件或服务。
3. 灵活性高,可以存储任意格式的数据。
1. 访问速度慢,尤其是处理大量数据时。
2. 数据结构不易查询,除非通过编写复杂的解析程序。
3. 数据安全性和完整性难以保证,容易出现数据丢失或损坏。
4. 难以支持并发访问和事务处理。
数据库存储1. 数据结构化和规范化,便于查询和管理。
2. 支持复杂的数据操作,如索引、排序、聚合等。
3. 数据安全性和完整性高,支持事务处理。
4. 易于实现数据的并发访问和共享。
1. 需要安装和维护数据库软件或服务。
2. 数据库设计需要一定的专业技能。
3. 对于非结构化数据的存储和查询可能不够灵活。
4. 在高并发场景下,性能可能受到限制。
内存存储1. 访问速度极快,适合需要高性能的场景。
2. 易于实现并发访问和共享。
3. 无需磁盘I/O操作,减少系统延迟。
1. 数据易失性高,一旦程序或系统崩溃,数据可能会丢失。
2. 容量受限,无法存储大量数据。
3. 需要定期将数据持久化到磁盘,增加系统复杂性。
4. 难以保证数据的安全性和完整性,除非配合其他机制。

对于一个小工具,数据库肯定是不需要的,内存存储不能保存数据,对于命令行工具,文件存储是最佳选择。

文件内容

在数据格式上设计上,也有几种方式可以选择:

  • csv文件:类似数据库,可读性良好,但字段值是大段的文字,不易阅读。
  • json文件:可读性也可以,字段值可以通过数组进行配置,也还行。

选择json文件最重要的原因时,对于大量字段配置文件,json格式可以很好的进行配置,直接复制粘贴。(csv也不是不行)

{
    "key": [
        "第0位含义",
        "第1位含义",
        ...
    ]
}

工具实现

第三方库

cargo add serde_json

创建项目【Comate生成】

  1. 打开终端:打开你的命令行界面。

运行cargo new命令:使用cargo new命令加上你的项目名称来创建一个新的项目。例如,要创建一个名为hello_world的项目,你可以运行:

cargo new hello_world

这个命令会创建一个新的目录hello_world,并在其中设置一个新的Rust项目。这个目录包含以下文件和目录:

  • Cargo.toml:项目的配置文件,用于描述项目的依赖和元信息。
  • src/:包含Rust源代码的目录。
  • src/main.rs:Rust程序的入口点。对于库项目,这个文件将被命名为lib.rs。

进入项目目录:创建项目后,使用cd命令进入项目目录:

cd hello_world

构建和运行项目:在项目目录中,你可以使用cargo build命令来构建项目,使用cargo run命令来运行项目。对于我们的hello_world项目,运行cargo run将打印出默认的"Hello, world!"消息。

cargo run

这将自动编译你的Rust代码(如果它是第一次运行或自上次编译以来有所更改),并运行编译后的程序。

文件读取

【Comate生成】

// 异步函数,用于读取目录中的所有JSON文件并解析它们
async fn read_json_in_dir(path: &Path) -> Result<Data, Box<dyn std::error::Error>> {
    let mut datas = HashMap::new();
    // 使用tokio::fs::read_directory_entry来异步读取目录项
    let mut stream = tokio::fs::read_dir(path).await?;

    while let Some(entry) = stream.next_entry().await? {
        let path = entry.path();

        if path.extension() == Some("json".as_ref()) {
            // 异步读取并解析JSON文件
            let data = read_and_parse_json_file(&path).await?;
            for (k, v) in data {
                datas.insert(k, v);
            }
        }
    }
    Ok(Data { data: datas })
}

async fn read_and_parse_json_file(path: &Path,) -> Result<HashMap<String, Vec<String>>, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(path).await;
    match content {
        Ok(content) => {
            let data: HashMap<String, Vec<String>> = serde_json::from_str(&content)?;
            Ok(data)
        }
        Err(e) => {
            println!("{}", e);
            Err(Box::new(e))
        }
    }
}

解析命令行

【Comate生成】

    let args: Vec<String> = env::args().collect();
    if args.len() != 3 {
        println!("Usage: {} <filed> <value>", args[0]);
        return Ok(());
    }

    let key = args[1].clone();
    if !data.contains_key(&key) {
        println!("feild {} not found", key);
        return Ok(());
    }
    let value = args[2].clone();
    let number = value.parse::<u32>();
    if number.is_err() {
        println!("value {} is not a number", value);
        return Ok(());
    }
    let number = number.unwrap();
    let binary = int_to_binary(number);
    let comment = data.get(&key).unwrap().clone();
    for i in 0..comment.len() {
        println!(
            "{}: {}",
            comment.get(i).unwrap(),
            binary.chars().nth(i).unwrap_or_else(|| '0')
        );
    }
    Ok(())

十进制转二进制

【Comate生成】

// 将i32整数转换为二进制字符串
fn int_to_binary(num: u32) -> String {
    if num == 0 {
        return "0".to_string();
    }

    let mut binary_str = String::new();
    let mut n = num;

    // 当n不为0时,循环继续
    while n > 0 {
        // 将n的最后一位(二进制)转换为字符并添加到字符串
        let remainder = n % 2;
        binary_str.push_str(&remainder.to_string());
        // 移除n的最后一位(二进制)
        n /= 2;
    }

    // 注意是从最低位开始构建的
    binary_str
}

效果

result.png

总结

  1. 目前只是简略实现了最主要的功能,后续还想加入TUI
  2. 作为练手项目,代码没有过多的Rust语法特性,鉴定为玩具中的玩具
  3. Comate生成的代码没有完备的错误处理,有很多地方都是自己完善的
  4. Comate使用还算顺手,代码提示风格和Copilot一致