Rust文件系统构建库build-fs-tree的使用:快速创建目录结构和文件的Rust工具库

Rust文件系统构建库build-fs-tree的使用:快速创建目录结构和文件的Rust工具库

描述

当我在编写集成测试时,经常需要创建临时文件和目录。因此我创建了这个crate,它既提供了一个可以在Rust代码中使用的库,也提供了一个根据YAML结构生成文件系统树的CLI程序。

使用示例

库的使用

FileSystemTree

FileSystemTree::buildMergeableFileSystemTree::build更快,但它不会覆盖现有目录,也不会在父目录不存在时创建父目录。

use build_fs_tree::{FileSystemTree, Build, dir, file};
let tree: FileSystemTree<&str, &str> = dir! {
    "index.html" => file!(r#"
        <!DOCTYPE html>
        <link rel="stylesheet" href="styles/style.css" />
        <script src="scripts/main.js"></script>
    "#)
    "scripts" => dir! {
        "main.js" => file!(r#"document.write('Hello World')"#)
    }
    "styles" => dir! {
        "style.css" => file!(r#":root { color: red; }"#)
    }
};
tree.build("public").unwrap();

MergeableFileSystemTree

FileSystemTree::build不同,MergeableFileSystemTree::build可以覆盖现有目录并创建以前不存在的父目录,但性能会有所下降。

你可以通过From::from/Into::intoFileSystemTree转换为MergeableFileSystemTree,反之亦然。

use build_fs_tree::{MergeableFileSystemTree, Build, dir, file};
let tree = MergeableFileSystemTree::<&str, &str>::from(dir! {
    "public" => dir! {
        "index.html" => file!(r#"
            <!DOCTYPE html>
            <link rel="stylesheet href="styles/style.css" />
            <script src="scripts/main.js"></script>
        "#)
        "scripts/main.js" => file!(r#"document.write('Hello World')"#)
        "scripts/style.css" => file!(r#":root { color: red; }"#)
    }
});
tree.build(".").unwrap();

序列化和反序列化

FileSystemTreeMergeableFileSystemTree都实现了serde::Deserializeserde::Serialize

CLI程序

命令名为build-fs-tree,有2个子命令:createpopulate

create

该命令从stdin读取YAML并创建一个新的文件系统树。它是FileSystemTree的CLI等效命令。

创建两个文本文件在一个新目录中:

echo '{ foo.txt: HELLO, bar.txt: WORLD }' | build-fs-tree create foo-and-bar

创建一个文本文件及其父目录:

echo '{ text-files: { foo.txt: HELLO } }' | build-fs-tree create files

从YAML文件创建一个新的文件系统树:

build-fs-tree create root < fs-tree.yaml

populate

该命令从stdin读取YAML,要么创建一个新的文件系统树,要么向已存在的目录添加文件和目录。它是MergeableFileSystemTree的CLI等效命令。

在当前目录创建两个文本文件:

echo '{ foo.txt: HELLO, bar.txt: WORLD }' | build-fs-tree populate .

创建一个文本文件及其父目录:

echo '{ files/text-files/foo.txt: HELLO }' | build-fs-tree populate .

用YAML文件中描述的文件系统树填充当前目录:

build-fs-tree populate . < fs-tree.yaml

完整示例代码

// 示例1: 使用FileSystemTree创建文件系统结构
use build_fs_tree::{FileSystemTree, Build, dir, file};

fn create_file_structure() -> std::io::Result<()> {
    // 创建一个包含文档、源代码和.gitignore的项目结构
    let tree: FileSystemTree<&str, &str> = dir! {
        "docs" => dir! {
            "README.md" => file!("# Project Documentation")
        },
        "src" => dir! {
            "main.rs" => file!("fn main() {\n    println!(\"Hello, world!\");\n}")
        },
        ".gitignore" => file!("target/\nCargo.lock")
    };
    
    // 在"my_project"目录下构建这个文件结构
    tree.build("my_project")?;
    Ok(())
}

// 示例2: 使用MergeableFileSystemTree修改现有结构
use build_fs_tree::{MergeableFileSystemTree, dir, file};

fn update_file_structure() -> std::io::Result<()> {
    // 创建一个可合并的树结构,添加库文件和Cargo.toml
    let tree = MergeableFileSystemTree::<&str, &str>::from(dir! {
        "my_project" => dir! {
            "src" => dir! {
                "lib.rs" => file!("pub fn hello() {\n    println!(\"Hello from library!\");\n}")
            },
            "Cargo.toml" => file!("[package]\nname = \"my_project\"\nversion = \"0.1.0\"")
        }
    });
    
    // 在当前目录构建/更新文件结构
    tree.build(".")?;
    Ok(())
}

fn main() {
    // 创建初始项目结构
    create_file_structure().unwrap();
    // 更新项目结构
    update_file_structure().unwrap();
}

这个库提供了非常方便的方式来快速创建和管理文件系统结构,特别适合测试场景和项目初始化。

常见问题

为什么选择YAML?

因为它具有我想要的特性:易于读写,正确处理多行字符串。

关于其他配置格式如何?

根据UNIX哲学,你可以将其他配置格式通过管道传输到转换为JSON的程序(YAML是JSON的超集),然后将JSON输出通过管道传输到build-fs-tree


1 回复

build-fs-tree:快速创建目录结构和文件的Rust工具库

build-fs-tree 是一个用于快速构建文件系统目录结构的Rust库,它允许开发者通过简单的Rust数据结构描述目录树,并自动创建相应的文件和目录。

功能特点

  • 通过Rust数据结构声明式创建目录结构
  • 支持创建文件和目录
  • 支持文件内容写入
  • 跨平台兼容(Windows/Unix)
  • 轻量级且无额外依赖

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
build-fs-tree = "0.4"

基本示例

use build_fs_tree::{dir, file, FileSystemTree};
use std::fs;

fn main() -> std::io::Result<()> {
    // 定义目录结构
    let tree = dir! {
        "project_root" => dir! {
            "src" => dir! {
                "main.rs" => file!("fn main() {\n    println!(\"Hello, world!\");\n}")
            },
            "Cargo.toml" => file!(r#"
                [package]
                name = "my-project"
                version = "0.1.0"
                edition = "2021"
            "#)
        }
    };

    // 创建实际文件和目录
    tree.build(".")?;

    Ok(())
}

高级用法

从现有结构创建

use build_fs_tree::FileSystemTreeBuilder;

let mut builder = FileSystemTreeBuilder::new();
builder
    .add_dir("parent_dir")?
    .add_dir("parent_dir/child_dir")?
    .add_file("parent_dir/file.txt", "File contents")?;

let tree = builder.build();
tree.build("target_directory")?;

使用Buildable trait

use build_fs_tree::{dir, file, Buildable};

let tree = dir! {
    "config" => dir! {
        "settings.json" => file!(r#"{"debug": true}"#)
    }
};

// 构建到特定路径
tree.build("my_app")?;

处理已存在文件

默认情况下,如果目标文件已存在,build-fs-tree会返回错误。可以通过force方法覆盖:

tree.force().build("path")?;

实际应用示例

创建项目脚手架

use build_fs_tree::{dir, file};

fn create_project_scaffold(project_name: &str) -> std::io::Result<()> {
    let tree = dir! {
        project_name => dir! {
            "src" => dir! {
                "main.rs" => file!("fn main() {\n    println!(\"Hello from {}!\");\n}", project_name),
                "lib.rs" => file!("pub fn greet() {\n    println!(\"Hello!\");\n}")
            },
            "tests" => dir! {
                "integration_test.rs" => file!(r#"
                    use my_project::greet;

                    #[test]
                    fn test_greet() {
                        greet();
                    }
                "#)
            },
            ".gitignore" => file!("/target\nCargo.lock"),
            "Cargo.toml" => file!(format!(r#"
                [package]
                name = "{}"
                version = "0.1.0"
                edition = "2021"

                [dependencies]
            "#, project_name))
        }
    };

    tree.build(".")?;
    Ok(())
}

创建配置文件结构

use build_fs_tree::{dir, file};

fn create_config_structure() -> std::io::Result<()> {
    let tree = dir! {
        "config" => dir! {
            "database" => dir! {
                "postgresql.conf" => file!(r#"
                    host = "localhost"
                    port = 5432
                    username = "admin"
                "#),
                "redis.conf" => file!(r#"
                    host = "127.0.0.1"
                    port = 6379
                "#)
            },
            "application" => dir! {
                "server.toml" => file!(r#"
                    [server]
                    port = 8080
                    workers = 4
                "#),
                "logging.yaml" => file!(r#"
                    level: info
                    format: json
                "#)
            }
        }
    };

    tree.build(".")?;
    Ok(())
}

完整示例demo

下面是一个结合了多种功能的完整示例:

use build_fs_tree::{dir, file, FileSystemTreeBuilder, Buildable};
use std::path::Path;

fn setup_test_environment() -> std::io::Result<()> {
    // 使用dir!宏创建基本结构
    let mut tree = dir! {
        "test_project" => dir! {
            "src" => dir! {
                "utils.rs" => file!(r#"
                    pub fn add(a: i32, b: i32) -> i32 {
                        a + b
                    }
                "#),
                "tests" => dir! {
                    "test_utils.rs" => file!(r#"
                        use super::*;

                        #[test]
                        fn test_add() {
                            assert_eq!(add(2, 3), 5);
                        }
                    "#)
                }
            },
            "config" => dir! {
                "app.toml" => file!(r#"
                    [settings]
                    debug = true
                    port = 8080
                "#)
            }
        }
    };

    // 使用FileSystemTreeBuilder添加更多内容
    let mut builder = FileSystemTreeBuilder::new();
    builder
        .add_dir("test_project/assets")?
        .add_file("test_project/assets/logo.png", "")? // 空文件,实际使用时可填充真实数据
        .add_file("test_project/README.md", "# Test Project\n\nThis is a sample project.")?;

    let additional_tree = builder.build();
    
    // 合并两个目录结构
    tree.merge(additional_tree);
    
    // 强制构建(覆盖已存在文件)
    tree.force().build(Path::new("test_dir"))?;
    
    Ok(())
}

fn main() -> std::io::Result<()> {
    // 创建测试环境
    setup_test_environment()?;
    
    // 创建项目脚手架
    create_project_scaffold("my_app")?;
    
    // 创建配置文件结构
    create_config_structure()?;
    
    Ok(())
}

注意事项

  1. 路径分隔符会自动根据操作系统转换
  2. 默认情况下不会覆盖已存在的文件(除非使用force()
  3. 创建的文件权限遵循操作系统默认设置
  4. 对于大型目录结构,考虑使用FileSystemTreeBuilder逐步构建

build-fs-tree特别适合测试场景(需要临时目录结构)、项目生成器和需要快速创建复杂目录结构的应用场景。

回到顶部