[易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]

项目实战

实战2:命令行工具minigrep

我们继续开发我们的minigrep。

我们现在以TDD测试驱动开发的模式,来开发新的功能search函数。

开始吧,我们先在src/lib.rs文件中,增加测试代码:

#[cfg(test)]
mod tests {
use super::*; #[test]
fn one_result() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Pick three."; assert_eq!(vec!["safe, fast, productive."], search(query, contents));
}
}

然后同样再写一个空函数:

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
vec![]
}

然后,我们要跑一下命令:

cargo test

结果显示:

$ cargo test
Compiling minigrep v0.1.0 (file:///projects/minigrep)
--warnings--
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
Running target/debug/deps/minigrep-abcabcabc running 1 test
test tests::one_result ... FAILED failures: ---- tests::one_result stdout ----
thread 'tests::one_result' panicked at 'assertion failed: `(left ==
right)`
left: `["safe, fast, productive."]`,
right: `[]`)', src/lib.rs:48:8
note: Run with `RUST_BACKTRACE=1` for a backtrace. failures:
tests::one_result test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--lib'

测试不通过。

我们的实现函数search,只简单返回一个空vector。当然,不通过。

怎么办?

重构一下search函数:

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new(); for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
} results
}

然后,我们跑一下命令:

cargo test

结果显示:

running 1 test
test tests::one_result ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

测试通过!漂亮!

这就是TDD的基本流程。

我们看看示图:

好吧,现在我们可以直接调用search函数了,把它放到src/lib.rs中的run函数里:

//重构从文件中读取内容的业务逻辑
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.filename)?; println!("With text:\n{}", contents); for line in search(&config.query, &contents) {
println!("-----search result ------{}", line);
}
Ok(())
}

然后,我们用运行命令:

cargo run frog poem.txt

结果为:

.......
-----search result ------How public, like a frog

我们找到包含frog单词的那一行!完美!

我们再找下body单词:

cargo run body poem.txt

结果为:

......
-----search result ------I'm nobody! Who are you?
-----search result ------Are you nobody, too?
-----search result ------How dreary to be somebody!

结果正确!

当然,我们也可以试试不存在的单词:

cargo run monomorphization poem.txt

结果为:

E:\code\rustProject\minigrep> cargo run monomorphization poem.txt
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target\debug\minigrep.exe monomorphization poem.txt`
With text:
I'm nobody! Who are you?
Are you nobody, too?
Then there's a pair of us - don't tell!
They'd banish us, you know. How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!

没有出现带“-----search result ------”的结果信息,说明没有找到单词:monomorphization。

结果符合期望。

现在我们再以TDD的开发方式来开发新的功能函数search_case_insensitive,新增测试代码:

#[test]
fn case_insensitive() {
let query = "rUsT";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me."; assert_eq!(
vec!["Rust:", "Trust me."],
search_case_insensitive(query, contents)
);
}

当然,我们可以简单实现一下search_case_insensitive函数,让它fail,因为这个逻辑比较简单,那就直接实现吧:

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
let mut results = Vec::new(); for line in contents.lines() {
if line.to_lowercase().contains(&query) {
results.push(line);
}
} results
}

好的,我们跑一下命令:

cargo test

结果:

$cargo test
Compiling minigrep v0.1.0 (E:\code\rustProject\minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 1.38s
Running target\debug\deps\minigrep-07da7ef1bffd9ef9.exe running 2 tests
test case_insensitive ... ok
test tests::one_result ... ok

测试通过,漂亮!

我们现在把这个方法整合到run函数里:

//重构从文件中读取内容的业务逻辑
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.filename)?; println!("With text:\n{}", contents); let results = if config.case_sensitive {
search(&config.query, &contents)
} else {
search_case_insensitive(&config.query, &contents)
};
for line in results {
println!("-----search result ------{}", line);
}
Ok(())
}

当然,我们也要把属性case_sensitive放到结构体Config里面:

//结构体Config用来封装参数属性
pub struct Config {
pub query: String,
pub filename: String,
pub case_sensitive: bool,
}

src/lib.rs的完整代码如下 :

use std::env;
use std::error::Error;
use std::fs;
//结构体Config用来封装参数属性
pub struct Config {
pub query: String,
pub filename: String,
pub case_sensitive: bool,
} //为结构体实现一个构造器,其主要功能也是读取和解析参数
impl Config {
pub fn new(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("参数个数不够!not enough arguments");
} let query = args[1].clone();
let filename = args[2].clone();
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
Ok(Config {
query,
filename,
case_sensitive,
})
}
}
//重构从文件中读取内容的业务逻辑
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.filename)?; println!("With text:\n{}", contents); let results = if config.case_sensitive {
search(&config.query, &contents)
} else {
search_case_insensitive(&config.query, &contents)
};
for line in results {
println!("-----search result ------{}", line);
}
Ok(())
}
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new(); for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
} results
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
let mut results = Vec::new(); for line in contents.lines() {
if line.to_lowercase().contains(&query) {
results.push(line);
}
} results
}
#[cfg(test)]
mod tests {
use super::*; #[test]
fn one_result() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Pick three."; assert_eq!(vec!["safe, fast, productive."], search(query, contents));
}
}
#[test]
fn case_insensitive() {
let query = "rUsT";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me."; assert_eq!(
vec!["Rust:", "Trust me."],
search_case_insensitive(query, contents)
);
}

我们现在运行命令:

cargo run to poem.txt

结果:

......
-----search result ------Are you nobody, too?
-----search result ------How dreary to be somebody!

现在我们把环境变量设置一下:

如果你的终端是powershell,用以下命令

 $env:CASE_INSENSITIVE=1

我们现在运行命令:

cargo run to poem.txt

结果:

......
-----search result ------Are you nobody, too?
-----search result ------How dreary to be somebody!
-----search result ------To tell your name the livelong day
-----search result ------To an admiring bog!

我们现在看看把错误信息输出到一个output.txt文件,运行以下命令:

cargo run > output.txt

我们观察到,控制台没有输出。

但当前工程目录有一个output.txt文件,打开文件。里面有错误信息。

我们现在来用eprintln!宏替换掉println!宏。src/main.rs代码更新如下:

use minigrep::run;
use minigrep::Config; use std::env;
use std::process;
//主函数,程序入口
fn main() {
let args: Vec<String> = env::args().collect(); let config = Config::new(&args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
}); if let Err(e) = run(config) {
//根据处理结果返回值 来处理,如果有错误,则打印信息,并直接退出当前程序
eprintln!("Application error: {}", e); process::exit(1);
}
}

运行以下命令:

cargo run > output.txt

我们观察到,控制台现在会输出错误信息。

我们运行如下命令:

 cargo run to poem.txt > output.txt

这里控制台没有输出。

但打开output.txt,里面的信息为:

With text:
I'm nobody! Who are you?
Are you nobody, too?
Then there's a pair of us - don't tell!
They'd banish us, you know. How dreary to be somebody!
How public, like a frog
To tell your name the livelong day
To an admiring bog!
-----search result ------Are you nobody, too?
-----search result ------How dreary to be somebody!
-----search result ------To tell your name the livelong day
-----search result ------To an admiring bog!

以上,希望对你有用。

如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust

参考文章:

https://doc.rust-lang.org/stable/book/ch12-05-working-with-environment-variables.html

最新文章

  1. linux集群运维工具:clustershell和pssh
  2. Cannot find executable for CFBundle 解决办法
  3. Unity中Mesh分解与边缘高亮加上深度检测
  4. Scrum 项目7.0--软件工程
  5. C 栈 链式存储
  6. Spark Streaming 结合FlumeNG使用实例
  7. SQLCMD的用法,使用CMD 执行sql语句
  8. c语言中的制表符\t与空格
  9. 【Java基础】通过getResourceAsStream() 加载资源文件
  10. Unity 动态加载 Prefab
  11. 云计算 --&gt; 三种服务模式IaaS,PaaS,SaaS
  12. windows下python3.5使用pip离线安装whl包
  13. 【一天一道LeetCode】#96. Unique Binary Search Trees
  14. Docker 新手入门
  15. 如何清除浮动(float)所带来的影响
  16. Treiber Stack介绍
  17. 优化sql用到的方法
  18. Python类和人类
  19. windows mac ssh 出国访问google等系列网站
  20. JavaScript判断对象 是什么类型的.

热门文章

  1. Min swaps to sort array
  2. 【Python】【demo实验14】【练习实例】【斐波那契数列】【经典兔子生小兔子问题】
  3. 查询统计SQL分组求和使用小技巧
  4. 第一章 impala的安装
  5. AMD平台如何使用Android Studio官方的高性能模拟器
  6. iptables笔记
  7. 【原创】大数据基础之Kudu(6)kudu tserver内存占用统计分析
  8. 单元操作和仓储模式 repository+unitOfWork
  9. JS基础_条件分支语句:switch语句
  10. 养成一个SQL好习惯