Repository: typst/typst
File: crates/typst-cli/src/update.rs (Rust)

Code before:
/// Compare the release version to the CLI version to see if an update is needed.
fn update_needed(release: &Release) -> StrResult<bool> {
    let current_tag: Version = env!("CARGO_PKG_VERSION").parse().unwrap();
    let new_tag: Version = release
        .tag_name
        .strip_prefix('v')
        .unwrap_or(&release.tag_name)
        .parse()
        .map_err(|_| "release tag not in semver format")?;

    Ok(new_tag > current_tag)
}

/// Path to a potential backup file.
///
/// The backup will be placed in one of the following directories, depending on
/// the platform:
///  - `$XDG_STATE_HOME` or `~/.local/state` on Linux
///    - `$XDG_DATA_HOME` or `~/.local/share` if the above path isn't available
///  - `~/Library/Application Support` on macOS
///  - `%APPDATA%` on Windows
///
/// If a custom backup path is provided via the environment variable
/// `TYPST_UPDATE_BACKUP_PATH`, it will be used instead of the default
/// directories determined by the platform.
fn backup_path(backup_path: Option<PathBuf>) -> StrResult<PathBuf> {
    let backup_dir = if let Some(backup_path) = backup_path {
        backup_path
    } else {
        #[cfg(target_os = "linux")]
        let root_backup_dir = dirs::state_dir()
            .or_else(dirs::data_dir)
            .ok_or("unable to locate local data or state directory")?;

        #[cfg(not(target_os = "linux"))]
        let root_backup_dir =
            dirs::data_dir().ok_or("unable to locate local data directory")?;

        root_backup_dir.join("typst")
    };

    fs::create_dir_all(&backup_dir)
        .map_err(|err| eco_format!("failed to create backup directory ({err})"))?;

    Ok(backup_dir.join("typst_backup.part"))
}


Review comment (suggestion):
I think it makes more sense if this function doesn't take a parameter and is only responsible for the default case. The `fs::create_dir_all` is already a bit weird and could be moved out of this function into the update function itself.

Code after:
        "x86_64-apple-darwin" => "typst-x86_64-apple-darwin",
        "aarch64-apple-darwin" => "typst-aarch64-apple-darwin",
        "x86_64-pc-windows-msvc" => "typst-x86_64-pc-windows-msvc",
        target => bail!("unsupported target: {target}"),
    })
}

/// Compare the release version to the CLI version to see if an update is needed.
fn update_needed(release: &Release) -> StrResult<bool> {
    let current_tag: Version = env!("CARGO_PKG_VERSION").parse().unwrap();
    let new_tag: Version = release
        .tag_name
        .strip_prefix('v')
        .unwrap_or(&release.tag_name)
        .parse()
        .map_err(|_| "release tag not in semver format")?;

    Ok(new_tag > current_tag)
}

/// Path to a potential backup file in the system.
///
/// The backup will be placed as `typst_backup.part` in one of the following
/// directories, depending on the platform:
///  - `$XDG_STATE_HOME` or `~/.local/state` on Linux
///    - `$XDG_DATA_HOME` or `~/.local/share` if the above path isn't available
///  - `~/Library/Application Support` on macOS
///  - `%APPDATA%` on Windows
///
/// If a custom backup path is provided via the environment variable
/// `TYPST_UPDATE_BACKUP_PATH`, it will be used instead of the default
/// directories determined by the platform. In that case, this function
/// shouldn't be called.
fn backup_path() -> StrResult<PathBuf> {
    #[cfg(target_os = "linux")]
    let root_backup_dir = dirs::state_dir()
        .or_else(dirs::data_dir)
        .ok_or("unable to locate local data or state directory")?;

    #[cfg(not(target_os = "linux"))]
    let root_backup_dir =
        dirs::data_dir().ok_or("unable to locate local data directory")?;

    Ok(root_backup_dir.join("typst").join("typst_backup.part"))
}