今天即将是这个系列的最后一次内容,我们正在catRust 中从 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_namesargs
- 该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_namesargs
- 该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命令。










