從迷你todo?命令行入門Rust示例詳解
一個迷你 todo 應(yīng)用
該文章將使用 Rust 從零去做一個入門級別的 TODO 命令行應(yīng)用
你將學(xué)會什么?
- 基本的命令行操作
- 文件讀寫和文件結(jié)構(gòu)組織
我們將會通過命令行輸入來實現(xiàn)對根目錄下 state.json 文件的編輯,如:
cargo run create 買菜
cargo run get 買菜
cargo run delete 買菜
cargo run edit 買菜
我們的 todo 狀態(tài)會有 pending 和 done 這兩種,create 操作將創(chuàng)建一個 {"買菜":"pending"} 的狀態(tài),edit 操作主要就是將 pending 狀態(tài)轉(zhuǎn)變?yōu)?done 狀態(tài)
需要安裝的依賴
我們要操作 json 數(shù)據(jù)結(jié)構(gòu),所以要安裝下面這個 crate
[package]
edition = "2021"
name = "todo_app"
version = "0.1.0"
[dependencies]
serde_json = {default-feature = false, version = "1.0", feature = ["alloc"]}
文件目錄組織
| main.rs 主文件
| process.rs 處理 todo 命令行輸入
| state.rs 讀寫文件
|
\---todo
| mod.rs 類似于 js 里面的 index.js
|
\---structs struct 數(shù)據(jù)結(jié)構(gòu)組織
| base.rs 基礎(chǔ)參數(shù)的數(shù)據(jù)結(jié)構(gòu)
| done.rs 完成狀態(tài)的數(shù)據(jù)結(jié)構(gòu)
| mod.rs 類似于 js 里面的 index.js
| pending.rs 處理中狀態(tài)的數(shù)據(jù)結(jié)構(gòu)
|
\---traits (特征)類似于 ts 里面的 interface
create.rs 創(chuàng)建操作
delete.rs 刪除操作
edit.rs 編輯操作
get.rs 查詢操作
mod.rs 類似于 js 里面的 index.js
主文件
// 有一點點像 import process from './process.rs'
mod process;
mod state;
mod todo;
// 使用外部的庫、標準庫或自己定義的工具
use process::process_input;
use serde_json::Map;
use serde_json::Value;
use state::read_file;
use std::env;
use todo::todo_factory;
fn main() {
// 收集所有命令行的參數(shù),轉(zhuǎn)換成數(shù)組
let args: Vec<String> = env::args().collect();
// 拿到第一個參數(shù)如 get、delete、edit、create
let command: &String = &args[1];
// 第二個參數(shù)是用來記錄事件的
let title: &String = &args[2];
// 讀取根目錄的 state.json 文件并轉(zhuǎn)換成 map json 結(jié)構(gòu)
let state: Map<String, Value> = read_file("./state.json");
// 如果事件已經(jīng)存取過了,那就直接獲取,沒有就將其狀態(tài)設(shè)置為 pending
let status: String;
match &state.get(title) {
Some(result) => status = result.to_string().replace('\"', ""),
None => status = "pending".to_string(),
}
// todo_factory 工廠函數(shù)用于處理 pending 和 done 狀態(tài)的輸入
let item = todo_factory(&status, title).expect(&status);
// 將狀態(tài)輸入到本地文件中
process_input(item, command.to_string(), &state);
}
上面我們看到了 3 個主要我們需要自己編寫的函數(shù):
- read_file 讀取文件
- todo_factory 狀態(tài)工廠函數(shù)
- process_input 處理輸入
接下來讓我們一個一個來看下這幾個函數(shù)
讀取文件
read_file 函數(shù)位于 src/state.rs 路徑下,該文件主要是用來存儲狀態(tài)操作的,里面包含讀取和寫入兩個函數(shù),讓我們主要看下 read_file 這個函數(shù),它的功能:
- 打開文件
- 讀取文件
- 將讀取到的文件內(nèi)容轉(zhuǎn)成 json 對象
- 返回讀取到的 json 對象
use std::fs::{self, File};
use std::io::Read;
use serde_json::json;
use serde_json::value::Value;
use serde_json::Map;
/** 讀取文件 */
pub fn read_file(file_name: &str) -> Map<String, Value> {
// 打開文件
let mut file = File::open(file_name).unwrap();
// 創(chuàng)建一個 string buffer 用于存儲數(shù)據(jù)
let mut data = String::new();
// 讀取數(shù)據(jù)寫入到 buffer
file.read_to_string(&mut data).unwrap();
// 將讀取到的字符串轉(zhuǎn)換成 json 文本格式 Value 類型
let json: Value = serde_json::from_str(&data).unwrap();
// 將 json 文本格式轉(zhuǎn)換成 json 對象
let state: Map<String, Value> = json.as_object().unwrap().clone();
// 將這個對象返回出去
return state;
}
/** 寫入文件 */
pub fn write_to_file(file_name: &str, state: &mut Map<String, Value>) {
// json! 這個宏用于將 json 字面量對象轉(zhuǎn)換回 json 文本 Value 格式
let new_data = json!(state);
// 將 json 文本寫入到文件中
fs::write(file_name, new_data.to_string()).expect("unable to write to file");
}
狀態(tài)處理工廠函數(shù)
看完文件讀取操作我們再來看下第二個主要函數(shù) todo_factory,這個函數(shù)主要是根據(jù)事件的狀態(tài)和通過命令行輸入的 title 事件名稱,然后構(gòu)建出一個相應(yīng)的數(shù)據(jù)結(jié)構(gòu)
該文件位于 src/todo/mod.rs 路徑下
它的作用主要是根據(jù)輸入的 pending/done 狀態(tài),然后創(chuàng)建出一個對應(yīng)的數(shù)據(jù)結(jié)構(gòu)
// 將 structs 向外部導(dǎo)出
pub mod structs;
use structs::done::Done;
use structs::pending::Pending;
// 創(chuàng)建一個包含 pending 和 done 狀態(tài)的枚舉
pub enum ItemTypes {
Pending(Pending),
Done(Done),
}
/** 狀態(tài)處理工廠函數(shù) */
pub fn todo_factory(item_type: &str, item_title: &str) -> Result<ItemTypes, &'static str> {
match item_type {
"pending" => {
// 創(chuàng)建一個 pending 狀態(tài)的數(shù)據(jù)結(jié)構(gòu),然后返回出去
let pending_item = Pending::new(item_title);
Ok(ItemTypes::Pending(pending_item))
}
"done" => {
// 創(chuàng)建一個 done 狀態(tài)的數(shù)據(jù)結(jié)構(gòu),然后返回出去
let done_item = Done::new(item_title);
Ok(ItemTypes::Done(done_item))
}
// 如果不是這兩個狀態(tài)就返回一個錯誤
_ => Err("This is not accepted!"),
}
}
從上面的代碼中我們可以看到下面這兩行代碼,這是我們主要需要定義的兩個狀態(tài)數(shù)據(jù)結(jié)構(gòu),他們位于 src/todo/structs 路徑下
use structs::done::Done; use structs::pending::Pending;
不過在看上面兩個數(shù)據(jù)結(jié)構(gòu)之前,我們需要先來看下另一個基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),就是 base,因為上面兩個數(shù)據(jù)結(jié)構(gòu)是基于 base 的,實現(xiàn)一個類似于面向?qū)ο笳Z言里繼承的做法,base 是它們倆的基類
該文件位于 src/todo/structs/base.rs 路徑下
pub struct Base {
pub title: String,
pub status: String,
}
// 為這個 Base 數(shù)據(jù)結(jié)構(gòu)實現(xiàn)一個 new 方法,并返回一個實例化后的數(shù)據(jù)結(jié)構(gòu)
impl Base {
pub fn new(input_title: &str, input_status: &str) -> Base {
Base {
title: input_title.to_string(),
status: input_status.to_string(),
}
}
}
現(xiàn)在我們用 Pending 和 Done 兩個 struct 來 "繼承" Base
該文件位于 src/todo/structs/pending.rs 路徑下
use super::base::Base;
pub struct Pending {
pub super_struct: Base,
}
impl Pending {
pub fn new(input_title: &str) -> Pending {
Pending {
super_struct: Base::new(input_title, "pending"),
}
}
}
該文件位于 src/todo/structs/done.rs 路徑下
use super::base::Base;
pub struct Done {
pub super_struct: Base,
}
impl Done {
pub fn new(input_title: &str) -> Done {
Done {
super_struct: Base::new(input_title, "done"),
}
}
}
Trait(特征)
Trait 類似于 TS 里的 interface 接口,現(xiàn)在我們要為我們的 struct 來實現(xiàn)一些在 Trait 里面定義的方法,文件路徑在 src/todo/structs/traits
Create trait
文件路徑在 src/todo/structs/traits/create
use serde_json::{json, Map, Value};
use crate::state::write_to_file;
pub trait Create {
// 為這個 trait 實現(xiàn)一個默認的 create 方法
fn create(&self, title: &String, status: &String, state: &mut Map<String, Value>) {
state.insert(title.to_string(), json!(status));
write_to_file("./state.json", state);
println!("\n\n{} is being created\n\n", title);
}
}
Get trait
文件路徑在 src/todo/structs/traits/get
use serde_json::{Map, Value};
pub trait Get {
fn get(&self, title: &String, state: &Map<String, Value>) {
let item = state.get(title);
match item {
Some(result) => {
println!("\n\nItem: {}", title);
println!("Status: {} \n\n", result);
}
None => println!("item: {} was not find", title),
}
}
}
Delete trait
文件路徑在 src/todo/structs/traits/delete
use serde_json::{Map, Value};
use crate::state::write_to_file;
pub trait Delete {
fn delete(&self, title: &String, state: &mut Map<String, Value>) {
state.remove(title);
write_to_file("./state.json", state);
println!("\n\n{} is being deleted\n\n", title);
}
}
Edit trait
該 Trait 主要實現(xiàn)兩個方法,一個是將事件設(shè)置為 pending 狀態(tài),一個將事件設(shè)置為 done 狀態(tài)
文件路徑在 src/todo/structs/traits/edit
use serde_json::{json, Map, Value};
use crate::state::write_to_file;
pub trait Edit {
fn set_to_done(&self, title: &String, state: &mut Map<String, Value>) {
state.insert(title.to_string(), json!("done".to_string()));
write_to_file("./state.json", state);
println!("\n\n{} is being set to done\n\n", title);
}
fn set_to_pending(&self, title: &String, state: &mut Map<String, Value>) {
state.insert(title.to_string(), json!("pending".to_string()));
write_to_file("./state.json", state);
println!("\n\n{} is being set to pending\n\n", title);
}
}
導(dǎo)出 trait
文件路徑在 src/todo/structs/traits/mod
pub mod create; pub mod delete; pub mod edit; pub mod get;
為 struct 實現(xiàn) trait
接下來我們?yōu)?Pending 和 Done 兩個 struct 來實現(xiàn)這幾個 trait
Pending
我們?yōu)?Pending 實現(xiàn)所有的 4 個 trait
use super::base::Base;
use super::traits::create::Create;
use super::traits::delete::Delete;
use super::traits::edit::Edit;
use super::traits::get::Get;
pub struct Pending {
pub super_struct: Base,
}
impl Pending {
pub fn new(input_title: &str) -> Pending {
Pending {
super_struct: Base::new(input_title, "pending"),
}
}
}
impl Delete for Pending {}
impl Create for Pending {}
impl Edit for Pending {}
impl Get for Pending {}
Done
為 Done 實現(xiàn)除了 Create 以外的 trait
文件路徑在 src/todo/structs/done
use super::base::Base;
use super::traits::delete::Delete;
use super::traits::edit::Edit;
use super::traits::get::Get;
pub struct Done {
pub super_struct: Base,
}
impl Done {
pub fn new(input_title: &str) -> Done {
Done {
super_struct: Base::new(input_title, "done"),
}
}
}
impl Delete for Done {}
impl Edit for Done {}
impl Get for Done {}
導(dǎo)出 struct
mod base; pub mod done; pub mod pending; pub mod traits;
Process 輸入處理
文件位于 src/process.rs 路徑下
use serde_json::{Map, Value};
use crate::todo::{
structs::{
done::Done,
pending::Pending,
traits::{create::Create, delete::Delete, edit::Edit, get::Get},
},
ItemTypes,
};
// 處理 pending 狀態(tài)
fn process_pending(item: Pending, command: String, state: &Map<String, Value>) {
let mut state = state.clone();
// 根據(jù)用戶的輸入來調(diào)用不同的方法
match command.as_str() {
"get" => item.get(&item.super_struct.title, &state),
"create" => item.create(
&item.super_struct.title,
&item.super_struct.status,
&mut state,
),
"delete" => item.delete(&item.super_struct.title, &mut state),
"edit" => item.set_to_done(&item.super_struct.title, &mut state),
_ => println!("command: {} is not supported", command),
}
}
// 處理 done 狀態(tài)
fn process_done(item: Done, command: String, state: &Map<String, Value>) {
let mut state = state.clone();
match command.as_str() {
"get" => item.get(&item.super_struct.title, &state),
"delete" => item.delete(&item.super_struct.title, &mut state),
"edit" => item.set_to_pending(&item.super_struct.title, &mut state),
_ => println!("command: {} is not supported", command),
}
}
// 處理用戶的輸入,根據(jù)輸入來匹配枚舉,然后執(zhí)行不同的操作
pub fn process_input(item: ItemTypes, command: String, state: &Map<String, Value>) {
match item {
ItemTypes::Pending(item) => process_pending(item, command, state),
ItemTypes::Done(item) => process_done(item, command, state),
}
}
最后
按照上面的代碼一步一步來完成就可以執(zhí)行程序了,在根目錄下新建一個 state.json 文件,寫入一個空對象,不然會報錯(代碼沒做處理)
{}
最后再在控制臺上去執(zhí)行
cargo run create shopping
然后就能看到 state.json 中多了一條記錄
{ "shopping": "pending" }
其它的方法就交由你們自己去嘗試好了~
以上就是從迷你 todo 命令行入門 Rust的詳細內(nèi)容,更多關(guān)于todo 命令行入門Rust的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何使用VSCode配置Rust開發(fā)環(huán)境(Rust新手教程)
這篇文章主要介紹了如何使用VSCode配置Rust開發(fā)環(huán)境(Rust新手教程),本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
教你使用RustDesk?搭建一個自己的遠程桌面中繼服務(wù)器
這篇文章主要介紹了RustDesk?搭建一個自己的遠程桌面中繼服務(wù)器,主要包括服務(wù)端安裝和客戶端配置方法,配置好相關(guān)操作輸入控制碼即可發(fā)起遠程或文件傳輸,本文通過圖文給大家講解的非常詳細,需要的朋友可以參考下2022-08-08
Rust實現(xiàn)一個表達式Parser小結(jié)
這篇文章主要為大家介紹了Rust實現(xiàn)一個表達式Parser小結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
Rust錯誤處理之`foo(...)?`的用法與錯誤類型轉(zhuǎn)換小結(jié)
foo(...)?語法糖為Rust的錯誤處理提供了極大的便利,通過結(jié)合map_err方法和From?trait的實現(xiàn),你可以輕松地處理不同類型的錯誤,并保持代碼的簡潔性和可讀性,這篇文章主要介紹了Rust錯誤處理:`foo(...)?`的用法與錯誤類型轉(zhuǎn)換,需要的朋友可以參考下2024-05-05

