#functional #error #data #interface #programs #command #book

bin+lib sfw-tools

《软件工具》一书中的功能和程序

7 个版本 (4 个重大变更)

0.5.0 2024 年 7 月 9 日
0.4.0 2021 年 4 月 24 日
0.3.0 2021 年 4 月 1 日
0.2.1 2021 年 3 月 12 日
0.1.1 2021 年 1 月 9 日

#682 in 命令行工具

Download history 16/week @ 2024-06-29 106/week @ 2024-07-06 9/week @ 2024-07-13 78/week @ 2024-07-27 1/week @ 2024-08-03

每月下载量 79

MPL-2.0 许可证

1MB
1K SLoC

sfw-tools

设计

秉承《软件工具》的精神,目标是使组件以三种方式可重用

  1. 将核心功能实现为函数,以便在 Rust 中重用。这些函数通常返回一个 Result 类型,以便调用者可以决定如何处理错误。
  2. 具有简单接口的可执行命令,通常作为库函数的薄包装,或者以有趣的方式组合库函数。
  3. 以及设计良好的代码,在必要时可以复制和重用。

可以探索第四种途径,即采用 nushell 方法在命令之间传输表格数据。

有关另一个遵循《软件工具》在 Rust 中实施的项目,并且可能作为一个有趣的比较,请参阅 Sweater。一个功能更丰富的项目是 uutils coreutils,如名称所示,它是一个类似于 GNU Coreutils 的 Rust 实现。

功能设施

高阶函数(HOFs)经常被用来减少代码的复杂性、冗长性和错误的风险。主要的例子是 mapfor_each(类似于 map 但有副作用)和 fold。正如《软件工具》第 21 页所指出的,“最好的程序是以松散耦合的函数设计的,每个函数都执行一个简单的任务”。

一些反映函数式编程价值观的其他参考资料

  • 第 36 页,关于 break 的讨论:建议也与递归函数相一致。
  • 第 44-45 页讨论了通过安全检查保护控制变量来进行防御性编程。在函数式编程中,这样的控制变量通常不会出现,因此由于 HOFs 的设计是安全的,因此不需要安全检查。第 45 页还指出,非冗长的代码列表更容易调试(我同意这一点,函数式风格通常可以实现这一点),尽管我们也想警告人们不要使代码过于简洁。在这方面,经验是最好的指南。

当前实现工具

  • cp
  • wc
  • detab
  • entab
  • echo
  • compress
  • expand

依赖项

由于目标是使软件尽可能自我包含且具有说明性,我们尽量依赖很少的依赖项。以下存在一些例外

  • fp-core 这通常在函数式语言的常规库中找到,所以我们在这里包含它。虽然Rust在某种程度上是函数式的——它有lambda函数(即Rust闭包)并且标准库有许多高阶函数(HOFs)——但其标准库不包括在函数式语言中常见到的有助抽象的特质。我们将特别说明或合理的情况下使用其中的一些,但将坚持使用惯用的Rust,因为这显然更简单。一个有趣的现象是,过滤器是第2章和本书大部分内容的主旨,它们只是HOFs的一个特定类别。
  • peeking_take_while 一个提供 peeking_take_while 函数的 Peekable 迭代器的库。与标准 take_while 实现相比,这个库表现得更为符合预期,后者在 take_while 模式结束时“丢失”第一个元素。
  • tailcall 这是一个宏,它可以为尾递归函数启用尾调用消除。换句话说,我们有时可以只写一个调用自身的函数,而不是写循环。如果没有这个宏,这样的函数最终会导致堆栈爆炸。
  • seahorse Seahorse 是一个最小化参数解析器。根据一些Google搜索结果,clap 比较受欢迎,但它有额外的依赖项;我们力求尽可能的便携性,所以最小化似乎与这个目标一致。此外,Clap似乎不允许直接传递参数列表,这对于维护基于其他命令构建的独立命令很有用。无论如何,参数解析仅在应用逻辑的后期使用,并且大多数API可以在不担心它的前提下使用。

目前未使用

  • byteorder 用于读取/写入大端和小端数字的库。这是一个相对底层的库,但作为这个IO密集型工具库,依赖它可能是有意义的。
  • im 对于大型数据类型,实现结构共享的不可变数据结构甚至可能比 std 的可变结构表现更佳,而且虽然Rust使修改远比大多数语言更安全,但有时修改仍会导致混淆,所以在清晰度比性能更重要(或性能不是很重要,例如单操作)的情况下,可能更倾向于使用不可变数据结构。

构建

杂项笔记

使用 todo!() 的方法

使用来自 std::todotodo!() 是在开发功能的同时从编译器获取反馈的有用方式。[待办事项:显示示例]

一个 警告 是,目前您需要在 todo!() 之后的函数中包含代码,即使它不匹配类型。例如,我们可以使用这样的函数

pub fn some_num() -> i32 {
    todo!(); ();
}

最有益的是,如果代码中留下了todo!()rustc会警告你,因为如果执行到这个路径,就会导致程序崩溃。

Rust on nix

nix develop

优化大小

目前,要生成小型构建,需要以下命令。

  1. (每个环境只一次) 使标准库的源代码可用
rustup component add rust-src --toolchain nightly
cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu --release
  1. (可选) strip 二进制文件 - 请参阅注释中的链接

项目管理

Git 钩子

Cargo-Husky

我们使用cargo-husky来保持一致;它通过pre-push钩子强制执行多个检查。有时它可能有点限制性,所以如果我们需要将正在进行的工作推送到分支,我们可以使用git push --no-verify -u origin feature_branch。Cargo-husky期望某些文件位于存储库的根目录,因此使用了符号链接。

pre-commit

我们包括以下不那么严格的pre-commit检查。

#!/bin/sh

# Put in your Rust repository's .git/hooks/pre-commit to ensure you never
# breaks rustfmt.
#
# WARNING: rustfmt is a fast moving target so ensure you have the version that
#          all contributors have.

for FILE in `git diff --cached --name-only`; do
    if [[ -f "$FILE" ]] && [[ $FILE == *.rs ]] \
           && ! rustup run nightly rustfmt --unstable-features \
                --skip-children $FILE; then
        echo "Commit rejected due to invalid formatting of \"$FILE\" file."
        exit 1
    fi
done

cd Rust/sfw-tools && cargo readme > README.md && git add README.md

如你所见,这也从lib.rs中的文档注释生成了README。

许可证:MPL-2.0

依赖项

~2MB
~46K SLoC