Part 1: Make the tests
compile
配置好之后尝试运行 cargo test
,发现编译错误:
1 2 3 4 5 error[E0433]: failed to resolve: use of undeclared crate or module `assert_cmd` --> tests\tests.rs:1:5 | 1 | use assert_cmd::prelude::*; | ^^^^^^^^^^ use of undeclared crate or module `assert_cmd`
显然是缺少相应的 crate,在 cargo.toml
里面加上。
1 2 3 [dev-dependencies] assert_cmd = "0.11.0" predicates = "1.0.0"
dev-dependencies
和 dependencies
的区别可以看官方文档
再次运行 cargo test
,发现编译错误:
1 2 3 4 5 error[E0432]: unresolved import `kvs::KvStore` --> tests\tests.rs:2:5 | 2 | use kvs::KvStore; | ^^^^^^^^^^^^ no `KvStore` in the root
也就是还没有实现对应的接口,先在 kvs/src/lib.rs
中添加对应的接口,内容直接 panic!()
就行(也可以用
unimplemented!
,不过有点麻烦)。
观察 tests.rs
,根据里面的调用写对应的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 pub struct KvStore {}impl KvStore { pub fn new () -> Self { unimplemented! (); } pub fn set (&mut self , _key: String , _value: String ) { unimplemented! (); } pub fn get (&self , _key: String ) -> Option <String > { unimplemented! (); } pub fn remove (&mut self , _key: String ) { unimplemented! (); } }
然后 cargo test --no-run
,发现可以通过编译。
Part 2: Accept command
line arguments
使用 clap
crate 进行参数解析。
参考 docs.rs
上的教程 ,首先
cargo add clap --features derive
启用功能,然后依次添加对应的 Subcommand
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 use clap::{Parser, Subcommand};#[derive(Parser)] #[command(author, version, about, long_about = None)] pub struct Args { #[command(subcommand)] pub command: Command, } #[derive(Subcommand)] pub enum Command { Set { key: String , value: String }, Get { key: String }, Rm { key: String }, }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 use clap::Parser;use kvs::{Args, Command, KvStore};fn main () { let mut store = KvStore::new (); let args = Args::parse (); match args.command { Command::Set { key, value } => { store.set (key, value); } Command::Get { key } => { store.get (key); } Command::Rm { key } => { store.remove (key); } } }
然后使用 cargo test cli
进行测试。
Part 3: Cargo environment
variables
在 cargo.toml
中设置 clap
crate
的一些默认参数。一个简单例子:
1 2 3 4 5 6 [package] name = "kvs" version = "0.1.0" authors = ["MizukiCry <YuukaC@outlook.com>" ]description = "An in-memory key-value store for study." edition = "2021"
Part 4: Store values in
memory
实现 KvStore
的接口通过剩余测试,其实也就是套
HashMap
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 pub struct KvStore { store: HashMap<String , String >, } impl KvStore { pub fn new () -> Self { Self { store: HashMap::new (), } } pub fn set (&mut self , key: String , value: String ) { self .store.insert (key, value); } pub fn get (&self , key: String ) -> Option <String > { self .store.get (&key).cloned () } pub fn remove (&mut self , key: String ) { self .store.remove (&key).unwrap (); } }
Part 5: Documentation
为 src/lib.rs
内所有内容加上注释。
顺便将解析参数的内容移到了 src/bin/kvs.rs
中,更加方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #![deny(missing_docs)] use std::collections::HashMap;pub struct KvStore { store: HashMap<String , String >, } impl KvStore { pub fn new () -> Self { Self { store: HashMap::new (), } } pub fn set (&mut self , key: String , value: String ) { self .store.insert (key, value); } pub fn get (&self , key: String ) -> Option <String > { self .store.get (&key).cloned () } pub fn remove (&mut self , key: String ) { self .store.remove (&key).unwrap (); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 use clap::{Parser, Subcommand};use kvs::KvStore;#[derive(Parser)] #[command(author, version, about, long_about = None)] struct Args { #[command(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { Set { key: String , value: String }, Get { key: String }, Rm { key: String }, } fn main () { let mut store = KvStore::new (); let args = Args::parse (); match args.command { Command::Set { key, value } => { store.set (key, value); } Command::Get { key } => { store.get (key); } Command::Rm { key } => { store.remove (key); } } }
完成之后可以通过 cargo doc --open
查看文档,cargo test --doc
单独测试。
Part 6: Ensure
good style with clippy and rustfmt
安装 clippy
和 rustfmt
工具:
1 2 rustup component add clippy rustup component add rustfmt
接着运行 cargo clippy
和 cargo fmt
进行检查,并改进源代码。
1 2 3 4 5 6 impl Default for KvStore { fn default () -> Self { Self ::new () } }
Extension 1: structopt
使用 structopt
crate 代替 clap
crate
再来一遍。感觉好烦,不写了