タスクを追加する関数を記述する

完了

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 を使用してファイルを開きます。これで、readwritecreate(ファイルがまだ存在しない場合) など、ファイルを操作するためのいくつかのモードを指定できます。

そのステートメントの後の疑問符記号 (?) は、定型のコードをあまり記述せずにエラーを伝達するために使用されます。 これは、エラーがその関数の戻り値の型と一致する場合にエラーを早期に返すためのシンタックス シュガーです。 そのため、これらのスニペットは同等です。

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 式の "ガード" を使用して OkErr の特定の一致をフィルター処理で除外し、特定のエラーが発生したときに空の Vec を構築します。 Vec は空の to-do リストを表します。

serde_json::Error は、From の特性が実装されているため、簡単に std::io::Error 型に変換できます。 そのため、? 演算子を使用して展開することや、早期に戻すことができます。

読み取り後にファイルを巻き戻す

カーソルをファイルの末尾に移動したため、再度書き込む前にファイルを巻き戻す必要があります。 ファイルを巻き戻さないと、カーソルの最後の位置から書き込みが開始され、JSON ファイルの形式が正しくなくなります。 ファイルを巻き戻すために、std::io モジュールの Seek の特性と SeekFrom 列挙型を使用します。

変更したタスク リストをファイルに書き戻す

最後に、関数パラメーターとして受け取った Task 値をタスク リストにプッシュし、serde_json を使用してタスク ベクトルをファイルに書き込みます。 次に、Ok 内の空のタプル値を返し、すべてが計画どおりに進んだことを示します。