[易学易懂系列|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

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

  1. [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具]

    [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具] 项目实战 实战5:实现BTC价格转换工具 今天我们来开发一个简单的BTC实时价格转换工具. 我们首先 ...

  2. [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]

    [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链] 项目实战 实战4:从零实现BTC区块链 我们今天来开发我们的BTC区块链系统. 简单来说,从数据结构的 ...

  3. [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)] 项目实战 实战3:Http服务器 我们今天来进一步开发我们的Http服务器,用多线程实现. 我 ...

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

    [易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)] 项目实战 实战2:命令行工具minigrep 有了昨天的基础,我们今天来开始另一个稍微有点 ...

  5. [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏]

    [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏] 项目实战 实战1:猜数字游戏 我们今天来来开始简单的项目实战. 第一个简单项目是猜数字游戏. 简单来说,系统给了 ...

  6. [易学易懂系列|rustlang语言|零基础|快速入门|(5)|生命周期Lifetime]

    [易学易懂系列|rustlang语言|零基础|快速入门|(5)] Lifetimes 我们继续谈谈生命周期(lifttime),我们还是拿代码来说话: fn main() { let mut a = ...

  7. [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]

    [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...

  8. [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针]

    [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针] 实用知识 智能指针 我们今天来讲讲Rust中的智能指针. 什么是指针? 在Rust,指针(普通指针),就是保存内存地址的值 ...

  9. [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]

    [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理] 实用知识 错误处理 我们今天来讲讲Rust中的错误处理. 很多语言都有自己的错误处理方式,比如,java是异常处理机制. ...

随机推荐

  1. 【C/C++开发】try-cache-finnally捕获异常

    在c++中,可以直接抛出异常之后自己进行捕捉处理,如:(这样就可以在任何自己得到不想要的结果的时候进行中断,比如在进行数据库事务操作的时候,如果某一个语句返回SQL_ERROR则直接抛出异常,在cat ...

  2. 3道入门字典树例题,以及模板【HDU1251/HDU1305/HDU1671】

    HDU1251:http://acm.hdu.edu.cn/showproblem.php?pid=1251 题目大意:求得以该字符串为前缀的数目,注意输入格式就行了. #include<std ...

  3. Oracle- 数据库知识回顾

    数据库知识回顾: sql语句的语法规范: sql语句不区分大小写,习惯上系统的保留字.关键字.函数名称大写,表名和列名小写 使用空格和换行分隔单词效果一样,一般情况下独立的整句不换行,不同的子句需要换 ...

  4. Oracle创建表空间、创建用户,给用户分配表空间以及可操作权限

    创建表空间一共可分为四个步骤 具体脚本如下: 第1步:创建临时表空间 create temporary tablespace yd_temp       tempfile 'D:\oracledata ...

  5. SQL Server解惑——为什么你的查询结果超出了查询时间范围

    原文:SQL Server解惑--为什么你的查询结果超出了查询时间范围 废话少说,直接上SQL代码(有兴趣的测试验证一下),下面这个查询语句为什么将2008-11-27的记录查询出来了呢?这个是同事遇 ...

  6. 【模板】dijkstra与floyd

    (我永远喜欢floyd) 温馨提示:与SPFA一起食用效果更佳 传送门:https://www.cnblogs.com/Daz-Os0619/p/11388157.html Floyd 大概思路: 对 ...

  7. php 连接sqlserver

    本地环境windows 10+phpstudy2016+ SQL Server 2008 R2 x86+php7.0查看自己sql server 多少位可以在新建查询里输入 select @@VERS ...

  8. kafka运维填坑

    转载自:https://www.jianshu.com/p/d2cbaae38014 前提: 只针对Kafka 0.9.0.1版本; 说是运维,其实偏重于问题解决; 大部分解决方案都是google而来 ...

  9. python3爬虫图片验证码识别

    # 图片验证码识别 环境安装# sudo apt-get install -y tesseract-ocr libtesseract-dev libleptonica-dev# pip install ...

  10. js之常用正则

    //用户名正则,4到16位(字母,数字,下划线,减号) let uPattern = /^[a-zA-Z0-9_-]{4,16}$/; //密码强度正则,最少6位,包括至少1个大写字母,1个小写字母, ...