今天即将是这个系列的最后一次内容,我们正在cat
Rust 中从 GNU 核心实用程序进行重建。cat
用于将文件内容打印到STDOUT
.听起来很容易构建,所以让我们开始吧。
GitHub 存储库:GitHub - shafinmurani/gnu-core-utils-rust
伪代码
function read(path)->result{
read contents of path
propogate any errors to the caller
}
args = command_line_arguments
remove the first element of the args vector
if args.length == 0 {
print error_message
}
else if args.contains("--help") {
print help_message
} else {
for path in args {
result = remove(path);
error handling
}
}
设置任务
正如我们往常所做的那样,让我们impl
在库箱中创建一个结构体和一个块。
pub struct Config<'a> {
file_names: &'a Vec<String>,
}
impl Config<'_> {
pub fn new(args: &Vec<String>){
Config{ file_names: args }
}
fn not_enough_arguments(){
eprintln!("cat: missing operand");
eprintln!("for help: cat --help");
}
fn help(){
eprintln!("Welcome to cat in Rust, This is a a little copy of the cat utility present in the GNU core utilities. I made this to practice my rust skills. Check out my articles at https://shafinmurani.medium.com");
eprintln!("To use: cat file_name1 file_name2 file_name3");
}
fn read_documents(){}
pub fn run(&self){
if self.file_names.len() == 0 {
Self::not_enough_arguments();
} else if self.file_names.contain(&String::from("--help")){
Self::help();
} else {
//We call the read_documents function here
}
}
}
- 该代码定义了一个
Config
带有生命周期参数的结构体'a
,其中包含一个file_names
对字符串向量的引用的字段。 - 该
Config
结构有一个关联的实现块 (impl
),用于与 相关的方法Config
。 - 该
new
方法是 的构造函数Config
,将对字符串向量的引用args
作为输入,并返回一个设置为 的Config
实例。file_names
args
- 该
not_enough_arguments
方法向 stderr 打印一条错误消息,指示该cat
命令缺少操作数,建议使用cat --help
. - 该
help
方法打印欢迎消息和cat
命令的使用说明。 - 该
read_documents
方法当前留空,可能会被实现来读取和打印指定文件的内容。 - 该
run
方法是执行命令的主要函数cat
。 - 它首先检查是否没有提供文件名,在这种情况下它调用
not_enough_arguments
. - 然后它检查
--help
文件名中是否存在该标志,如果存在,则调用该help
方法。 - 否则,假设提供了文件名,并且它将继续读取和打印这些文件的内容,当前将其作为注释保留。
read_documents() 方法
我们将向此函数传递一个文件路径,它将输出文件的内容,为此,我们使用以下内容:
std::fs::File
: 打开文件std::io::Read
:将内容读取到字符串变量
让我们导入这些东西并编写read_document()
函数并使用它
use std::fs::File;
use std::io::Read;
pub struct Config<'a> {
file_names: &'a Vec<String>,
}
impl Config<'_> {
pub fn new(args: &Vec<String>){
Config{ file_names: args }
}
fn not_enough_arguments(){
eprintln!("cat: missing operand");
eprintln!("for help: cat --help");
}
fn help(){
eprintln!("Welcome to cat in Rust, This is a a little copy of the cat utility present in the GNU core utilities. I made this to practice my rust skills. Check out my articles at https://shafinmurani.medium.com");
eprintln!("To use: cat file_name1 file_name2 file_name3");
}
fn read_documents(file: String) -> std::io::Result<String>{
let mut file_buffer = File::open(file)?;
let mut file_content = String::new();
file_buffer.read_to_string(&mut file_content)?;
Ok(file_content)
}
pub fn run(&self){
if self.file_names.len() == 0 {
Self::not_enough_arguments();
} else if self.file_names.contain(&String::from("--help")){
Self::help();
} else {
for file in self.file_names {
let result = Self::read_document(file.to_string());
match result {
Ok(data) => println!("{}",data),
Err(e) => eprintln!("Application Error: `{}` {}", file, e),
};
}
}
}
}
现在这一切都完成了,我们可以处理我们的二进制箱并专注于运行它......
二进制箱
这里的逻辑很简单
- 导入
Config
结构体 - 获取命令行参数
- 删除第一个参数
- 实例化配置
- 配置.run()
让我们实践并运行它。
use cat::Config;
use std::env;
fn main(){
let mut args: Vec<String> = env::args().collect();
args.remove(0);
let config = Config::new(args);
config.run();
}
运行它
cat
我们将使用该工具来阅读我们工具的源代码cat
。谈论 Catception 💀
# ./cat lib.rs
use std::fs::File;
use std::io::Read;
pub struct Config<'a>{
file_names: &'a Vec<String>,
}
impl Config<'_>{
pub fn new(args: &Vec<String>) -> Config{
Config{ file_names: args }
}
fn help(){
eprintln!("Welcome to cat in Rust, This is a little copy of the cat utility present in the GNU core utilities. I made this to practice my rust skills. Check out my articles at https://shafinmurani.medium.com");
eprintln!("To use: cat file_name1 file_name2 ... file_nameN");
}
fn not_enough_arguments(){
eprintln!("cat: missing operand");
eprintln!("for help: cat --help");
}
fn read_document(file: String) -> std::io::Result<String>{
let mut file_buffer = File::open(file)?;
let mut file_content = String::new();
file_buffer.read_to_string(&mut file_content)?;
Ok(file_content)
}
pub fn run(&self){
if self.file_names.len() == 0 {
Self::not_enough_arguments();
} else if self.file_names.contains(&String::from("--help")) {
Self::help();
} else {
for file in self.file_names {
let result = Self::read_document(file.to_string());
match result {
Ok(r) => println!("{}",r),
Err(e) => eprintln!("Application Error: `{}` {}",file,e)
};
}
}
}
}
结论
库文件
- 该
lib.rs
文件定义了 crate 的主要功能cat
。 - 它从标准库(
std::fs::File
、std::io::Read
)导入必要的模块以进行文件处理。 - 它声明了一个
Config
带有生命周期参数的结构体'a
,其中包含一个file_names
对字符串向量的引用的字段。 impl
与 相关的方法有一个实现块 ( )Config
。- 该
new
方法是 的构造函数Config
,将对字符串向量的引用args
作为输入,并返回一个设置为 的Config
实例。file_names
args
- 该
help
方法打印欢迎消息和cat
命令的使用说明。 - 该
not_enough_arguments
方法向 stderr 打印一条错误消息,指示该cat
命令缺少操作数,建议使用cat --help
. - 该
read_document
方法读取由名称指定的文件的内容并将其作为 a 返回String
,处理潜在的 IO 错误。 - 该
run
方法是执行命令的主要函数cat
。 - 它检查是否没有提供文件名,在这种情况下它调用
not_enough_arguments
. - 然后它检查
--help
文件名中是否存在该标志,如果存在,则调用该help
方法。 - 否则,它会迭代每个文件名,使用 读取其内容
read_document
,并将其打印到标准输出。
主程序.rs
- 该
main.rs
文件充当可执行文件的入口点。 - 它从板条箱
Config
中导入结构cat
,env
从标准库中导入模块。 - 该
main
函数将命令行参数收集到字符串向量中,删除第一个参数(程序名称本身),然后Config
使用剩余参数创建一个实例Config::new
。 - 最后,它调用实例
run
上的方法Config
来执行cat
命令。