これはかなり面白い。というか、参考にさせていただきます!
use std::{collections::HashMap, fs::OpenOptions}; use chrono::NaiveDate; use clap::{Args, Parser, Subcommand}; use csv::{Writer, Reader, WriterBuilder}; use serde::{Deserialize, Serialize}; #[derive(Parser)] #[clap(version = "1.0")] struct App { #[clap(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { /// 新しい口座を作る New(NewArgs), /// 口座に入金する Deposit(DepositArgs), /// 口座から出金する Withdraw(WithdrawArgs), /// CSVからインポートする Import(ImportArgs), /// レポートを出力する Report(ReportArgs), } #[derive(Args)] struct NewArgs { account_name: String, } impl NewArgs { fn run(&self) { let file_name = format!("{}.csv", self.account_name); let mut writer = Writer::from_path(file_name).unwrap(); writer .write_record(["日付", "用途", "金額"]) .unwrap(); writer.flush().unwrap(); } } #[derive(Args)] struct DepositArgs { account_name: String, date: NaiveDate, usage: String, amount: u32, } impl DepositArgs { fn run(&self) { let open_option = OpenOptions::new() .write(true) .append(true) .open(format!("{}.csv", self.account_name)) .unwrap(); let mut writer = Writer::from_writer(open_option); writer .write_record([ self.date.format("%Y-%m-%d").to_string(), self.usage.to_string(), self.amount.to_string()]) .unwrap(); writer.flush().unwrap(); } } #[derive(Args)] struct WithdrawArgs { account_name: String, date: NaiveDate, usage: String, amount: u32, } impl WithdrawArgs { fn run(&self) { let open_option = OpenOptions::new() .write(true) .append(true) .open(format!("{}.csv", self.account_name)) .unwrap(); let mut writer = Writer::from_writer(open_option); writer .write_record([ self.date.format("%Y-%m-%d").to_string(), self.usage.to_string(), format!("-{}", self.amount), ]) .unwrap(); writer.flush().unwrap(); } } #[derive(Args)] struct ImportArgs { src_file_name: String, dst_account_name: String, } impl ImportArgs { fn run(&self) { let open_option = OpenOptions::new() .write(true) .append(true) .open(format!("{}.csv", self.dst_account_name)) .unwrap(); let mut writer = WriterBuilder::new() .has_headers(false) .from_writer(open_option); let mut reader = Reader::from_path(&self.src_file_name).unwrap(); for result in reader.deserialize() { let record: Record = result.unwrap(); writer.serialize(record).unwrap(); } } } #[derive(Serialize, Deserialize)] struct Record { 日付: NaiveDate, 用途: String, 金額: i32, } #[derive(Args)] struct ReportArgs { files: Vec<String>, } impl ReportArgs { fn run(&self) { let mut map = HashMap::new(); for file in &self.files { let mut reader = Reader::from_path(file).unwrap(); for result in reader.records() { let record = result.unwrap(); let amount : i32 = record[2].parse().unwrap(); let date: NaiveDate = record[0].parse().unwrap(); let sum = map.entry(date.format("%Y-%m").to_string()) .or_insert(0); *sum += amount; } } println!("{:?}", map); } } fn main() { let args = App::parse(); match args.command { Command::New(args) => args.run(), Command::Deposit(args) => args.run(), Command::Withdraw(args) => args.run(), Command::Import(args) => args.run(), Command::Report(args) => args.run(), } }