経緯
今までdocomoのメモ帳を使っていましたが、ahamoへ切り替えた際に使えなくなりました。
なので、流行りののChat AIでメモをテキストファイルへ変換するRustプログラムを作成してみました。
最初の質問
以下のようにBEGIN:VEVENT, END:VEVENTで囲まれたものが複数あります。
それぞれのDESCRIPTIONとSUMMARYを抜き出しUnicode文字に変換するRust言語のコードを作成してください。
[docomoメモ帳のバックアップ(vcsファイル)の一部を抜き出して貼り付けしました]
最初の生成結果
まぁ最初はエラーになりますね。
ライフタイムが分かっていないのと、DESCRIPTIONが複数行の場合の対応ができていませんでした。
Rustが読みやすいからか修正の指示もしやすいですね。
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use url::form_urlencoded;
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: {} <file>", args[0]);
return Ok(());
}
let file = File::open(&args[1])?;
let reader = BufReader::new(file);
let mut events = Vec::new();
let mut event = HashMap::new();
for line in reader.lines() {
let line = line?;
if line == "BEGIN:VEVENT" {
event = HashMap::new();
} else if line == "END:VEVENT" {
events.push(event);
} else {
let parts: Vec<&str> = line.splitn(2, ':').collect();
if parts.len() == 2 {
let (key, value) = (parts[0], parts[1]);
if key == "DESCRIPTION;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE" || key == "SUMMARY;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE" {
let decoded = form_urlencoded::parse(value.as_bytes())
.map(|(key, _)| key)
.collect::<String>();
event.insert(key.to_string(), decoded);
}
}
}
}
for event in events {
let description = event.get("DESCRIPTION;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE").unwrap_or(&"".to_string());
let summary = event.get("SUMMARY;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE").unwrap_or(&"".to_string());
let mut file = File::create(format!("{}.txt", summary))?;
writeln!(file, "{}", description)?;
}
Ok(())
}
最終的な出力
いくつか修正指示を出して最終的には以下のようになりました。
まぁまぁ汚いですが短時間で目的は達成できたので良しとします。
以下で実行できます。
cargo run [vcsのファイルパス] [出力ディレクトリ]
use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::{self, BufRead, BufReader, Write};
use std::path::Path;
use url::form_urlencoded;
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
println!("Usage: {} <file> <directory>", args[0]);
return Ok(());
}
let file = File::open(&args[1])?;
let reader = BufReader::new(file);
let dir = &args[2];
// Create the directory if it does not exist
if !Path::new(dir).exists() {
fs::create_dir_all(dir)?;
}
let mut events = Vec::new();
let mut event = HashMap::new();
let mut current_key = String::new();
let mut current_value = String::new();
let empty_string = "".to_string();
for line in reader.lines() {
let line = line?;
if line == "BEGIN:VEVENT" {
event.clear();
} else if line == "END:VEVENT" {
events.push(event.clone());
} else if line.starts_with("DESCRIPTION;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE") || line.starts_with("SUMMARY;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE") {
let parts: Vec<&str> = line.splitn(2, ':').collect();
if parts.len() == 2 {
let (key, value) = (parts[0], parts[1]);
current_key = key.to_string();
current_value = value.to_string();
}
} else if line.starts_with('=') {
current_value.push_str(&line);
} else {
if !current_key.is_empty() {
let decoded = form_urlencoded::parse(current_value.replace("=", "").as_bytes())
.map(|(key, _)| key)
.collect::<String>();
let decoded_str = String::from_utf8(hex::decode(decoded).unwrap()).unwrap();
event.insert(current_key.clone(), decoded_str);
current_key.clear();
current_value.clear();
}
}
}
for event in &events {
let description = event.get("DESCRIPTION;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE").unwrap_or(&empty_string);
let summary = event.get("SUMMARY;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE").unwrap_or(&empty_string);
let summary = summary.replace("\n", "_");
let mut file = File::create(format!("{}/{}.txt", dir, summary))?;
writeln!(file, "{}", description)?;
}
Ok(())
}