minigrep
官方教程断断续续看了10章,写个小demo练习下。目前感觉还行,继续看完再来重构一个版本。
目录结构
cargo.toml
[package]
name = "minigrep"
version = "0.0.1"
edition = "2024"
[dependencies]
grepp.txt
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!
main.rs
use std::env::args;
fn main() {
let args: Vec<String> = args().collect();
// 去掉第一个参数
let params = &args[1..].to_vec();
minigrep::grep(params);
}
lib.rs
use std::io::{Error};
use std::io::ErrorKind::InvalidInput;
use std::process;
use crate::common::status::Status;
pub mod common;
pub fn grep(params:&Vec<String>){
check_params(¶ms);
use common::content::Content;
let result = Content::parse(¶ms).search();
if !result.is_empty(){
println!("{:?}", result);
}
}
/// 校验参数
fn check_params(params:&Vec<String>){
// 检验长度
let mut status = check_params_len(params.len()).unwrap_or_else(out_err);
if status.eq(&Status::Stop){
process::exit(0);
}
// 检验合法
status = check_params_invalid(params).unwrap_or_else(out_err);
if status.eq(&Status::Stop){
process::exit(0);
}
}
fn check_params_len(len:usize) -> Result<Status,Error>{
if len > 0 {
return Ok(Status::Continue )
}
Err(Error::new(InvalidInput,"未指定有效参数 --help 可查看支持的参数!"))
}
fn check_params_invalid(params:&Vec<String>) -> Result<Status, Error>{
use common::commands::Commands;
let commands = Commands::values();
for param in params {
for command in &commands {
if param.to_lowercase().starts_with(command.as_str()) {
let display = command.display();
if display.is_empty() {
return Ok(Status::Continue)
}else {
println!("{}", display);
return Ok(Status::Stop )
}
}
}
}
Ok(Status::Continue)
}
fn out_err(error:Error) -> Status{
eprintln!("参数错误:{}", error);
process::exit(1);
}
common/mod.rs
pub mod content;
pub mod commands;
pub mod status;
common/commands.rs
pub enum Commands {
Help,
Version,
FilePath,
LowerCase,
Keyword,
}
impl Commands {
pub fn as_str(&self) -> &str {
use Commands::*;
match *self {
Help => "--help",
Version => "--version",
FilePath => "--filepath",
LowerCase => "--lowercase",
Keyword => "--keyword",
}
}
pub fn values() -> Vec<Commands> {
use Commands::*;
vec![Help, Version, FilePath, LowerCase, Keyword]
}
pub fn display(&self) -> &str{
use Commands::*;
match *self {
Help => "--help \t 帮助 \n \
--version \t 版本 \n \
--filepath=xxx.txt \t 文件路径 \n \
--lowercase=false \t 是否忽略大小写 \n \
--keyword=key \t 搜索关键词 \n",
Version => "0.0.1",
_ => "",
}
}
}
common/content.rs
use std::{fs, process};
pub struct Content{
file_path: String, // 文件路径
search_keyword:String, //搜索关键字
context: String, // 文件内容
lowercase:bool,//忽略大小写
}
impl Content {
pub fn parse(params:&Vec<String>)->Content{
let mut content = Content {
file_path: "".to_string(),
search_keyword: "".to_string(),
context: "".to_string(),
lowercase: true
};
for param in params {
if param.to_lowercase().starts_with("--filepath="){
content.file_path = param.replace("--filepath=", "");
}
if param.to_lowercase().starts_with("--keyword="){
content.search_keyword = param.replace("--keyword=", "");
}
if param.to_lowercase().starts_with("--lowercase="){
content.lowercase = param.replace("--lowercase=", "").parse().unwrap_or_default();
}
}
if content.search_keyword.is_empty() {
eprintln!("No search keyword specified");
process::exit(1);
}
if !content.file_path.is_empty() {
let result = fs::read_to_string(&content.file_path)
.unwrap_or_else(|err| {
eprintln!("Error reading file: {}", err);
process::exit(1)
});
content.context = result.trim().to_string();
}
if content.context.is_empty() {
eprintln!("No context specified");
process::exit(1);
}
content
}
pub fn search(&self) -> Vec<String> {
let mut resut = vec![];
if self.context.is_empty() {
vec![]
}else {
let mut key = self.search_keyword.clone();
for line in self.context.lines() {
if !line.is_empty() {
let mut context = line.to_string();
if self.lowercase{
context = line.to_string().to_lowercase();
key = key.to_lowercase();
}
if context.contains(&key) {
resut.push(context);
}
}
}
resut
}
}
}
common/status.rs
#[derive(PartialEq)]
pub enum Status{
Stop,
Continue,
}