機能を拡張して、ファイルに保存するようにします
知見を別の件で使うだろう事を考慮し、形式はJSONにしました
実際のところ
tomlファイル
[package] name = "newproject" version = "0.1.0" edition = "2021" [dependencies] clap = { version = "4.5.2", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"
スクリプト
use std::error::Error; use clap::{Parser, Subcommand}; use serde::{Deserialize, Serialize}; use std::fs::{OpenOptions, File}; use std::io::{Read, Write}; #[derive(Debug, Parser)] #[command(author, version, about)] struct Cli { #[command(subcommand)] command: Commands, } #[derive(Debug, Subcommand)] enum Commands { Add { #[clap(value_parser)] name: String, #[clap(value_parser)] email: String, #[clap(value_parser)] age: u8, }, } #[derive(Debug, Serialize, Deserialize)] struct User { name: String, email: String, age: u8, } #[derive(Debug, Default, Serialize, Deserialize)] struct UserDatabase { users: Vec<User> } impl UserDatabase { fn new() -> Self { UserDatabase { users: Vec::new() } } fn add_user(&mut self, user: User) -> Result<(), String> { // メールアドレスの重複チェック if self.users.iter().any(|u| u.email == user.email) { return Err("Email already exists".to_string()); } self.users.push(user); Ok(()) } fn save_to_file(&self) -> Result<(), Box<dyn Error>> { let json = serde_json::to_string_pretty(&self)?; let mut file = OpenOptions::new() .write(true) .create(true) .truncate(true) .open("users.json")?; file.write_all(json.as_bytes())?; Ok(()) } fn load_from_file() -> Result<Self, Box<dyn Error>> { let mut file = match File::open("users.json") { Ok(file) => file, Err(_) => return Ok(UserDatabase::new()), }; let mut contents = String::new(); file.read_to_string(&mut contents)?; let db: UserDatabase = serde_json::from_str(&contents)?; Ok(db) } } fn main() { let args = Cli::parse(); // データベースの読み込み // ファイルが存在しない場合は新しいデータベースを作成(無名関数で処理を実装、errorの実体はe変数に格納) let mut db = UserDatabase::load_from_file().unwrap_or_else(|e| { println!("Failed to load database: {}", e); UserDatabase::new() // 新しいデータベースを作成 }); // コマンドの実行 match args.command { Commands::Add { name, email, age } => { let user = User { name: String::from(name), email: String::from(email), age, }; match db.add_user(user) { Ok(_) => { if let Err(e) = db.save_to_file() { println!("Failed to save database: {}", e); } else { println!("User added and saved successfully"); } }, Err(e) => println!("Failed to add user: {}", e), } // データベースの現在の状態を表示 println!("Current database state: {:?}", db); } } }
実行結果
まずは普通に追加
$ cargo.exe run -- add "John Doe" "jonhdoe@example.jp" 38 Running `target\debug\newproject.exe add "John Doe" jonhdoe@example.jp 38` User added and saved successfully Current database state: UserDatabase { users: [User { name: "John Doe", email: "jonhdoe@example.jp", age: 38 }] }
わざと同じ名前の人物を登録しても、メアドのバリデーションで弾かれる
$ cargo.exe run -- add "John Doe" "jonhdoe@example.jp" 38 Running `target\debug\newproject.exe add "John Doe" jonhdoe@example.jp 38` Failed to add user: Email already exists Current database state: UserDatabase { users: [User { name: "John Doe", email: "jonhdoe@example.jp", age: 38 }] }
新しい人を追加
$ cargo.exe run -- add "Taro Tanaka" "taroT@example.jp" 38 Running `target\debug\newproject.exe add "Taro Tanaka" taroT@example.jp 38` User added and saved successfully Current database state: UserDatabase { users: [User { name: "John Doe", email: "jonhdoe@example.jp", age: 38 }, User { name: "Taro Tanaka", email: "taroT@example.jp", age: 38 }] }
以上の結果を、ターゲットのJSONファイルに書かれているか確認
$ cat .\users.json { "users": [ { "name": "John Doe", "email": "jonhdoe@example.jp", "age": 38 }, { "name": "Taro Tanaka", "email": "taroT@example.jp", "age": 38 } ] }