Repository: tauri-apps/tauri
File: tooling/bundler/src/bundle/windows/nsis.rs (Rust)

Code before:
  "Stubs/lzma-x86-unicode",
  "Stubs/lzma_solid-x86-unicode",
  "Plugins/x86-unicode/nsis_tauri_utils.dll",
  "Include/MUI2.nsh",
  "Include/FileFunc.nsh",
  "Include/x64.nsh",
  "Include/nsDialogs.nsh",
  "Include/WinMessages.nsh",
];
#[cfg(not(target_os = "windows"))]
const NSIS_REQUIRED_FILES: &[&str] = &["Plugins/x86-unicode/nsis_tauri_utils.dll"];

const NSIS_REQUIRED_FILES_HASH: &[(&str, &str, &str, HashAlgorithm)] = &[(
  "Plugins/x86-unicode/nsis_tauri_utils.dll",
  NSIS_TAURI_UTILS_URL,
  NSIS_TAURI_UTILS_SHA1,
  HashAlgorithm::Sha1,
)];

/// Runs all of the commands to build the NSIS installer.
/// Returns a vector of PathBuf that shows where the NSIS installer was created.
pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<PathBuf>> {
  let tauri_tools_path = dirs_next::cache_dir().unwrap().join("tauri");
  let nsis_toolset_path = tauri_tools_path.join("NSIS");

  if !nsis_toolset_path.exists() {
    get_and_extract_nsis(&nsis_toolset_path, &tauri_tools_path)?;
  } else if NSIS_REQUIRED_FILES
    .iter()
    .any(|p| !nsis_toolset_path.join(p).exists())
  {
    warn!("NSIS directory is missing some files. Recreating it.");
    std::fs::remove_dir_all(&nsis_toolset_path)?;
    get_and_extract_nsis(&nsis_toolset_path, &tauri_tools_path)?;
  } else {
    let mismatched = NSIS_REQUIRED_FILES_HASH
      .iter()
      .filter(|(p, _, hash, hash_algorithm)| {
        verify_file_hash(nsis_toolset_path.join(p), hash, *hash_algorithm).is_err()
      })
      .collect::<Vec<_>>();

    if !mismatched.is_empty() {
      warn!("NSIS directory contains mis-hashed files. Redownloading them.");
      for (path, url, hash, hash_algorithim) in mismatched {
        let data = download_and_verify(url, hash, *hash_algorithim)?;
        write(nsis_toolset_path.join(path), data)?;
      }
    }
  }


Review comment (bug):
Creating an NSIS folder in advance will cause subsequent renaming failures after extract

```rust
rename(_tauri_tools_path.join("nsis-3.08"), nsis_toolset_path)?;
```

Code after:
  "Stubs/lzma_solid-x86-unicode",
  "Plugins/x86-unicode/nsis_tauri_utils.dll",
  "Include/MUI2.nsh",
  "Include/FileFunc.nsh",
  "Include/x64.nsh",
  "Include/nsDialogs.nsh",
  "Include/WinMessages.nsh",
];
#[cfg(not(target_os = "windows"))]
const NSIS_REQUIRED_FILES: &[&str] = &["Plugins/x86-unicode/nsis_tauri_utils.dll"];

const NSIS_REQUIRED_FILES_HASH: &[(&str, &str, &str, HashAlgorithm)] = &[(
  "Plugins/x86-unicode/nsis_tauri_utils.dll",
  NSIS_TAURI_UTILS_URL,
  NSIS_TAURI_UTILS_SHA1,
  HashAlgorithm::Sha1,
)];

/// Runs all of the commands to build the NSIS installer.
/// Returns a vector of PathBuf that shows where the NSIS installer was created.
pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<PathBuf>> {
  let tauri_tools_path = dirs_next::cache_dir().unwrap().join("tauri");
  let nsis_toolset_path = tauri_tools_path.join("NSIS");

  if !nsis_toolset_path.exists() {
    create_dir_all(&nsis_toolset_path)?;
    get_and_extract_nsis(&nsis_toolset_path, &tauri_tools_path)?;
  } else if NSIS_REQUIRED_FILES
    .iter()
    .any(|p| !nsis_toolset_path.join(p).exists())
  {
    warn!("NSIS directory is missing some files. Recreating it.");
    std::fs::remove_dir_all(&nsis_toolset_path)?;
    get_and_extract_nsis(&nsis_toolset_path, &tauri_tools_path)?;
  } else {
    let mismatched = NSIS_REQUIRED_FILES_HASH
      .iter()
      .filter(|(p, _, hash, hash_algorithm)| {
        verify_file_hash(nsis_toolset_path.join(p), hash, *hash_algorithm).is_err()
      })
      .collect::<Vec<_>>();

    if !mismatched.is_empty() {
      warn!("NSIS directory contains mis-hashed files. Redownloading them.");
      for (path, url, hash, hash_algorithim) in mismatched {
        let data = download_and_verify(url, hash, *hash_algorithim)?;
        write(nsis_toolset_path.join(path), data)?;
      }
    }
  }