タスクを追加する関数を記述する
add_task
関数を使用して、JSON ファイルにエンコードされている、使用できる既存のタスク コレクションに新しい Task
値を追加する必要があります。
そのため、そのコレクションにタスクを挿入する前に、まずそのファイルを読み取り、その内容からタスクのベクトルを組み立てる必要があります。
最初のバージョンは次のようになります。
use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use std::io::{Result, Seek, SeekFrom};
// ...
pub fn add_task(journal_path: PathBuf, task: Task) -> Result<()> {
// Open the file.
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(journal_path)?;
// Consume the file's contents as a vector of tasks.
let mut tasks: Vec<Task> = match serde_json::from_reader(&file) {
Ok(tasks) => tasks,
Err(e) if e.is_eof() => Vec::new(),
Err(e) => Err(e)?,
};
// Rewind the file after reading from it.
file.seek(SeekFrom::Start(0))?;
// Write the modified task list back into the file.
tasks.push(task);
serde_json::to_writer(file, &tasks)?;
Ok(())
}
4 つの手順でこの関数について見ていきましょう。
ファイルを開く。
まず、OpenOptions
を使用してファイルを開きます。これで、read
、write
、create
(ファイルがまだ存在しない場合) など、ファイルを操作するためのいくつかのモードを指定できます。
そのステートメントの後の疑問符記号 (?
) は、定型のコードをあまり記述せずにエラーを伝達するために使用されます。 これは、エラーがその関数の戻り値の型と一致する場合にエラーを早期に返すためのシンタックス シュガーです。 そのため、これらのスニペットは同等です。
fn function_1() -> Result(Success, Failure) {
match operation_that_might_fail() {
Ok(success) => success,
Err(failure) => return Err(failure),
}
}
fn function_2() -> Result(Success, Failure) {
operation_that_might_fail()?
}
このパターンは、このプログラムのように、複数の I/O 操作を実行する必要があるコードで多く使用されます。
リーダーを作成し、その内容をタスクのベクトルとして使用する
2 つ目の手順は、実際にファイルを読み取ることです。 ファイルを読み取るために、serde_json
から Reader
の特性を実装する型が要求されます。 File
型にはその特性が実装されているため、パラメーターとして serde_json.from_reader
関数に渡し、さらにそこから Vec<Task>
を受け取ることを宣言します。
ファイル システムへのアクセスは I/O アクションであり、さまざまな理由で失敗する可能性があることに注意してください。 そのため、特定のケースでプログラムがどのように動作するか (場合によっては復旧するか) を検討する必要があります。 たとえば、serde_json
では、解析対象が見つからずにファイルの末尾に達すると、エラーが返されます。 このイベントは常に空のファイルで発生し、そこから復旧できる必要があります。
特定の種類のエラーから復旧するために、match
式の "ガード" を使用して Ok と Err の特定の一致をフィルター処理で除外し、特定のエラーが発生したときに空の Vec
を構築します。 Vec
は空の to-do リストを表します。
serde_json::Error
は、From
の特性が実装されているため、簡単に std::io::Error
型に変換できます。 そのため、?
演算子を使用して展開することや、早期に戻すことができます。
読み取り後にファイルを巻き戻す
カーソルをファイルの末尾に移動したため、再度書き込む前にファイルを巻き戻す必要があります。 ファイルを巻き戻さないと、カーソルの最後の位置から書き込みが開始され、JSON ファイルの形式が正しくなくなります。 ファイルを巻き戻すために、std::io
モジュールの Seek
の特性と SeekFrom
列挙型を使用します。
変更したタスク リストをファイルに書き戻す
最後に、関数パラメーターとして受け取った Task
値をタスク リストにプッシュし、serde_json
を使用してタスク ベクトルをファイルに書き込みます。 次に、Ok
内の空のタプル値を返し、すべてが計画どおりに進んだことを示します。