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 再来一遍。感觉好烦,不写了