style: apply cargo fmt and add formatting check to CI (#319)
Runs `cargo fmt --check` in the rust_crate CI action to catch formatting issues before they land.
This commit is contained in:
@@ -13,6 +13,11 @@ inputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Check Formatting
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
shell: ${{ inputs.shell }}
|
||||
run: cargo fmt --check
|
||||
|
||||
- name: Clippy
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
shell: ${{ inputs.shell }}
|
||||
|
||||
@@ -55,9 +55,10 @@ impl Seek for CFile {
|
||||
};
|
||||
let result = (self.file_callbacks.seek)(self.handle, offset, whence);
|
||||
if result < 0 {
|
||||
Err(std::io::Error::other(
|
||||
format!("CFile seek failed with error code: {}", result),
|
||||
))
|
||||
Err(std::io::Error::other(format!(
|
||||
"CFile seek failed with error code: {}",
|
||||
result
|
||||
)))
|
||||
} else {
|
||||
Ok(result as u64)
|
||||
}
|
||||
|
||||
@@ -219,8 +219,7 @@ fn to_update_result(status: anyhow::Result<UpdateStatus>) -> UpdateResult {
|
||||
let message = status.to_string();
|
||||
return UpdateResult {
|
||||
status: status as i32,
|
||||
message: allocate_c_string(message.as_str())
|
||||
.unwrap_or(std::ptr::null_mut()),
|
||||
message: allocate_c_string(message.as_str()).unwrap_or(std::ptr::null_mut()),
|
||||
};
|
||||
}
|
||||
Err(err) => UpdateResult {
|
||||
|
||||
Vendored
+3
-5
@@ -1,5 +1,5 @@
|
||||
use anyhow::{bail, Context};
|
||||
use crate::file_errors::{FileOperation, IoResultExt};
|
||||
use anyhow::{bail, Context};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::{
|
||||
fs::File,
|
||||
@@ -24,8 +24,7 @@ where
|
||||
std::fs::create_dir_all(containing_dir)
|
||||
.with_file_context(FileOperation::CreateDir, containing_dir)?;
|
||||
|
||||
let file = File::create(path)
|
||||
.with_file_context(FileOperation::CreateFile, path_as_ref)?;
|
||||
let file = File::create(path).with_file_context(FileOperation::CreateFile, path_as_ref)?;
|
||||
let writer = BufWriter::new(file);
|
||||
serde_json::to_writer_pretty(writer, serializable)
|
||||
.with_context(|| format!("failed to serialize to {:?}", path_as_ref))
|
||||
@@ -43,8 +42,7 @@ where
|
||||
bail!("File {} does not exist", path_as_ref.display());
|
||||
}
|
||||
|
||||
let file = File::open(path_as_ref)
|
||||
.with_file_context(FileOperation::ReadFile, path_as_ref)?;
|
||||
let file = File::open(path_as_ref).with_file_context(FileOperation::ReadFile, path_as_ref)?;
|
||||
let reader = BufReader::new(file);
|
||||
serde_json::from_reader(reader)
|
||||
.with_context(|| format!("failed to deserialize from {:?}", &path_as_ref))
|
||||
|
||||
Vendored
+5
-3
@@ -1,7 +1,7 @@
|
||||
use super::{disk_io, signing, PatchInfo};
|
||||
use crate::file_errors::{FileOperation, IoResultExt};
|
||||
use crate::yaml::PatchVerificationMode;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use crate::file_errors::{FileOperation, IoResultExt};
|
||||
use core::fmt::Debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@@ -233,7 +233,8 @@ impl PatchManager {
|
||||
}
|
||||
|
||||
let artifact_size_on_disk = std::fs::metadata(&artifact_path)
|
||||
.with_file_context(FileOperation::GetMetadata, &artifact_path)?.len();
|
||||
.with_file_context(FileOperation::GetMetadata, &artifact_path)?
|
||||
.len();
|
||||
if artifact_size_on_disk != patch.size {
|
||||
bail!(
|
||||
"Patch {} has size {} on disk, but expected size {}",
|
||||
@@ -404,7 +405,8 @@ impl ManagePatches for PatchManager {
|
||||
let new_patch = PatchMetadata {
|
||||
number: patch_number,
|
||||
size: std::fs::metadata(&patch_path)
|
||||
.with_file_context(FileOperation::GetMetadata, &patch_path)?.len(),
|
||||
.with_file_context(FileOperation::GetMetadata, &patch_path)?
|
||||
.len(),
|
||||
hash: hash.to_owned(),
|
||||
signature: signature.map(|s| s.to_owned()),
|
||||
};
|
||||
|
||||
Vendored
+44
-10
@@ -344,12 +344,20 @@ mod tests {
|
||||
let release_version = state.serialized_state.release_version.clone();
|
||||
assert!(state.save().is_ok());
|
||||
|
||||
let mut state =
|
||||
UpdaterState::load_or_new_on_error(&state.cache_dir, &release_version, None, PatchVerificationMode::default());
|
||||
let mut state = UpdaterState::load_or_new_on_error(
|
||||
&state.cache_dir,
|
||||
&release_version,
|
||||
None,
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
assert_eq!(state.next_boot_patch().unwrap().number, 1);
|
||||
|
||||
let mut next_version_state =
|
||||
UpdaterState::load_or_new_on_error(&state.cache_dir, "1.0.0+2", None, PatchVerificationMode::default());
|
||||
let mut next_version_state = UpdaterState::load_or_new_on_error(
|
||||
&state.cache_dir,
|
||||
"1.0.0+2",
|
||||
None,
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
assert!(next_version_state.next_boot_patch().is_none());
|
||||
}
|
||||
|
||||
@@ -367,8 +375,18 @@ mod tests {
|
||||
#[test]
|
||||
fn creates_updater_state_with_client_id() {
|
||||
let tmp_dir = TempDir::new().unwrap();
|
||||
let state = UpdaterState::load_or_new_on_error(tmp_dir.path(), "1.0.0+1", None, PatchVerificationMode::default());
|
||||
let saved_state = UpdaterState::load_or_new_on_error(tmp_dir.path(), "1.0.0+1", None, PatchVerificationMode::default());
|
||||
let state = UpdaterState::load_or_new_on_error(
|
||||
tmp_dir.path(),
|
||||
"1.0.0+1",
|
||||
None,
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
let saved_state = UpdaterState::load_or_new_on_error(
|
||||
tmp_dir.path(),
|
||||
"1.0.0+1",
|
||||
None,
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
state.serialized_state.client_id,
|
||||
saved_state.serialized_state.client_id
|
||||
@@ -389,7 +407,12 @@ mod tests {
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
|
||||
let new_loaded = UpdaterState::load_or_new_on_error(&state.cache_dir, "1.0.0+2", None, PatchVerificationMode::default());
|
||||
let new_loaded = UpdaterState::load_or_new_on_error(
|
||||
&state.cache_dir,
|
||||
"1.0.0+2",
|
||||
None,
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
original_loaded.serialized_state.client_id,
|
||||
@@ -416,7 +439,8 @@ mod tests {
|
||||
let new_state_path = new_tmp_dir.path().join(STATE_FILE_NAME);
|
||||
std::fs::rename(original_state_path, new_state_path).unwrap();
|
||||
|
||||
let new_state = UpdaterState::load(new_tmp_dir.path(), None, PatchVerificationMode::default()).unwrap();
|
||||
let new_state =
|
||||
UpdaterState::load(new_tmp_dir.path(), None, PatchVerificationMode::default()).unwrap();
|
||||
assert_eq!(new_state.cache_dir, new_tmp_dir.path());
|
||||
}
|
||||
|
||||
@@ -557,7 +581,12 @@ mod tests {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
// Create a new state, add a patch, and save it.
|
||||
let mut state = UpdaterState::load_or_new_on_error(tmp_dir.path(), "1.0.0+1", None, PatchVerificationMode::default());
|
||||
let mut state = UpdaterState::load_or_new_on_error(
|
||||
tmp_dir.path(),
|
||||
"1.0.0+1",
|
||||
None,
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
let patch = fake_patch(&tmp_dir, 1);
|
||||
state.install_patch(&patch, "hash", None)?;
|
||||
state.save()?;
|
||||
@@ -568,7 +597,12 @@ mod tests {
|
||||
std::fs::write(&state_file, "corrupt json")?;
|
||||
|
||||
// Ensure that, by corrupting the file, we've reset the patches state.
|
||||
let mut state = UpdaterState::load_or_new_on_error(tmp_dir.path(), "1.0.0+2", None, PatchVerificationMode::default());
|
||||
let mut state = UpdaterState::load_or_new_on_error(
|
||||
tmp_dir.path(),
|
||||
"1.0.0+2",
|
||||
None,
|
||||
PatchVerificationMode::default(),
|
||||
);
|
||||
assert!(state.next_boot_patch().is_none());
|
||||
|
||||
Ok(())
|
||||
|
||||
+13
-24
@@ -38,17 +38,8 @@ impl std::fmt::Display for FileOperation {
|
||||
/// This function takes an IO error and adds context about what operation failed,
|
||||
/// what path was involved, and provides hints about possible causes based on
|
||||
/// the error type.
|
||||
pub fn enhance_io_error(
|
||||
error: &std::io::Error,
|
||||
operation: FileOperation,
|
||||
path: &Path,
|
||||
) -> String {
|
||||
let base_message = format!(
|
||||
"Failed to {} '{}': {}",
|
||||
operation,
|
||||
path.display(),
|
||||
error
|
||||
);
|
||||
pub fn enhance_io_error(error: &std::io::Error, operation: FileOperation, path: &Path) -> String {
|
||||
let base_message = format!("Failed to {} '{}': {}", operation, path.display(), error);
|
||||
|
||||
let hint = get_error_hint(error, operation);
|
||||
|
||||
@@ -70,9 +61,7 @@ fn get_error_hint(error: &std::io::Error, operation: FileOperation) -> String {
|
||||
ErrorKind::StorageFull => {
|
||||
"The device storage is full. Free up space and try again.".to_string()
|
||||
}
|
||||
ErrorKind::ReadOnlyFilesystem => {
|
||||
"The filesystem is mounted as read-only.".to_string()
|
||||
}
|
||||
ErrorKind::ReadOnlyFilesystem => "The filesystem is mounted as read-only.".to_string(),
|
||||
_ => {
|
||||
// Check raw OS error for cases not covered by ErrorKind
|
||||
if let Some(os_error) = error.raw_os_error() {
|
||||
@@ -90,9 +79,7 @@ fn get_permission_denied_hint(operation: FileOperation) -> String {
|
||||
FileOperation::CreateDir | FileOperation::CreateFile | FileOperation::WriteFile => {
|
||||
"The app may not have write access to this location.".to_string()
|
||||
}
|
||||
FileOperation::ReadFile => {
|
||||
"The app may not have read access to this file.".to_string()
|
||||
}
|
||||
FileOperation::ReadFile => "The app may not have read access to this file.".to_string(),
|
||||
FileOperation::DeleteFile | FileOperation::DeleteDir => {
|
||||
"The app may not have permission to delete this item.".to_string()
|
||||
}
|
||||
@@ -114,9 +101,7 @@ fn get_not_found_hint(operation: FileOperation) -> String {
|
||||
FileOperation::RenameFile => {
|
||||
"The source file or destination directory may not exist.".to_string()
|
||||
}
|
||||
_ => {
|
||||
"The file or directory does not exist.".to_string()
|
||||
}
|
||||
_ => "The file or directory does not exist.".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +111,9 @@ fn get_os_error_hint(os_error: i32) -> String {
|
||||
match os_error {
|
||||
28 => "The device storage is full (ENOSPC). Free up space and try again.".to_string(),
|
||||
30 => "The filesystem is mounted as read-only (EROFS).".to_string(),
|
||||
122 => "Disk quota exceeded (EDQUOT). The user's storage quota has been reached.".to_string(),
|
||||
122 => {
|
||||
"Disk quota exceeded (EDQUOT). The user's storage quota has been reached.".to_string()
|
||||
}
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
@@ -162,7 +149,10 @@ mod tests {
|
||||
assert_eq!(format!("{}", FileOperation::DeleteFile), "delete file");
|
||||
assert_eq!(format!("{}", FileOperation::DeleteDir), "delete directory");
|
||||
assert_eq!(format!("{}", FileOperation::RenameFile), "rename/move file");
|
||||
assert_eq!(format!("{}", FileOperation::GetMetadata), "get file metadata");
|
||||
assert_eq!(
|
||||
format!("{}", FileOperation::GetMetadata),
|
||||
"get file metadata"
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== enhance_io_error Tests ====================
|
||||
@@ -459,8 +449,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_io_result_ext_preserves_error_chain() {
|
||||
let result: std::io::Result<i32> =
|
||||
Err(Error::new(ErrorKind::NotFound, "No such file"));
|
||||
let result: std::io::Result<i32> = Err(Error::new(ErrorKind::NotFound, "No such file"));
|
||||
let path = Path::new("/missing/file.txt");
|
||||
|
||||
let converted = result.with_file_context(FileOperation::ReadFile, path);
|
||||
|
||||
@@ -457,9 +457,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn handle_network_result_ok() {
|
||||
let body = ureq::Body::builder()
|
||||
.mime_type("text/plain")
|
||||
.data("");
|
||||
let body = ureq::Body::builder().mime_type("text/plain").data("");
|
||||
let response = ureq::http::Response::builder()
|
||||
.status(200)
|
||||
.body(body)
|
||||
|
||||
@@ -36,8 +36,8 @@ pub fn install_fake_patch(patch_number: usize) -> anyhow::Result<()> {
|
||||
pub fn write_fake_apk(apk_path: &str, libapp_contents: &[u8]) {
|
||||
use std::io::Write;
|
||||
let mut zip = zip::ZipWriter::new(std::fs::File::create(apk_path).unwrap());
|
||||
let options = zip::write::SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Stored);
|
||||
let options =
|
||||
zip::write::SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
|
||||
let app_path = crate::android::get_relative_lib_path("libapp.so");
|
||||
zip.start_file(app_path.to_str().unwrap(), options).unwrap();
|
||||
zip.write_all(libapp_contents).unwrap();
|
||||
|
||||
+17
-21
@@ -261,7 +261,9 @@ pub fn check_for_downloadable_update(channel: Option<&str>) -> anyhow::Result<bo
|
||||
let (request, url, request_fn) = with_config(|config| {
|
||||
let mut config = config.clone();
|
||||
|
||||
if let Some(channel) = channel { config.channel = channel.to_string() }
|
||||
if let Some(channel) = channel {
|
||||
config.channel = channel.to_string()
|
||||
}
|
||||
|
||||
Ok((
|
||||
PatchCheckRequest::new(&config, &client_id),
|
||||
@@ -657,14 +659,11 @@ const ZSTD_MAGIC: [u8; 4] = [0x28, 0xB5, 0x2F, 0xFD];
|
||||
|
||||
/// Validates that a downloaded patch file is a non-empty, valid zstd archive.
|
||||
fn validate_compressed_patch(patch_path: &Path) -> anyhow::Result<()> {
|
||||
let metadata = fs::metadata(patch_path)
|
||||
.with_file_context(FileOperation::GetMetadata, patch_path)?;
|
||||
let metadata =
|
||||
fs::metadata(patch_path).with_file_context(FileOperation::GetMetadata, patch_path)?;
|
||||
let size = metadata.len();
|
||||
if size == 0 {
|
||||
bail!(
|
||||
"Downloaded patch file is empty: {:?}",
|
||||
patch_path
|
||||
);
|
||||
bail!("Downloaded patch file is empty: {:?}", patch_path);
|
||||
}
|
||||
// A valid zstd frame is at least 4 bytes (magic number).
|
||||
if size < 4 {
|
||||
@@ -674,8 +673,8 @@ fn validate_compressed_patch(patch_path: &Path) -> anyhow::Result<()> {
|
||||
patch_path
|
||||
);
|
||||
}
|
||||
let mut file = fs::File::open(patch_path)
|
||||
.with_file_context(FileOperation::ReadFile, patch_path)?;
|
||||
let mut file =
|
||||
fs::File::open(patch_path).with_file_context(FileOperation::ReadFile, patch_path)?;
|
||||
let mut magic = [0u8; 4];
|
||||
file.read_exact(&mut magic)
|
||||
.with_file_context(FileOperation::ReadFile, patch_path)?;
|
||||
@@ -721,9 +720,8 @@ where
|
||||
// Spawn a thread to run the decompression in parallel to the patching.
|
||||
// decompress.copy will block on the pipe being full (I think) and then
|
||||
// when it returns the thread will exit.
|
||||
let decompress_handle = std::thread::spawn(move || {
|
||||
decompress.copy(compressed_patch_r, patch_w)
|
||||
});
|
||||
let decompress_handle =
|
||||
std::thread::spawn(move || decompress.copy(compressed_patch_r, patch_w));
|
||||
|
||||
// Do the patch, using the uncompressed patch data from the pipe.
|
||||
let mut fresh_r =
|
||||
@@ -1422,11 +1420,7 @@ patch_verification: bogus_mode
|
||||
let err = super::validate_compressed_patch(&patch_path)
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert!(
|
||||
err.contains("empty"),
|
||||
"Expected 'empty' in error: {}",
|
||||
err
|
||||
);
|
||||
assert!(err.contains("empty"), "Expected 'empty' in error: {}", err);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1511,7 +1505,7 @@ patch_verification: bogus_mode
|
||||
let mut uncompressed = Vec::new();
|
||||
uncompressed.extend_from_slice(&0xB1DFu32.to_le_bytes()); // bipatch magic
|
||||
uncompressed.extend_from_slice(&0x1000u32.to_le_bytes()); // bipatch version
|
||||
uncompressed.extend_from_slice(&vec![0u8; 1024]); // padding
|
||||
uncompressed.extend_from_slice(&vec![0u8; 1024]); // padding
|
||||
|
||||
// Compress the valid data into one complete zstd frame.
|
||||
let mut compressed = std::io::Cursor::new(Vec::new());
|
||||
@@ -1525,7 +1519,7 @@ patch_verification: bogus_mode
|
||||
// successfully decompress the first frame (delivering the bipatch
|
||||
// header so Reader::new succeeds), then fail on this corrupt frame.
|
||||
compressed.extend_from_slice(&[0x28, 0xB5, 0x2F, 0xFD]); // zstd magic
|
||||
compressed.extend_from_slice(&[0xFF; 64]); // garbage
|
||||
compressed.extend_from_slice(&[0xFF; 64]); // garbage
|
||||
|
||||
let patch_path = tmp_dir.path().join("corrupt_frame.patch");
|
||||
fs::write(&patch_path, &compressed).unwrap();
|
||||
@@ -2730,8 +2724,10 @@ mod multi_engine_tests {
|
||||
|
||||
use crate::{
|
||||
network::{testing_set_network_hooks, PatchCheckResponse, UNEXPECTED_DOWNLOAD},
|
||||
report_launch_start, report_launch_success, test_utils::install_fake_patch,
|
||||
updater::tests::init_for_testing, with_mut_state, with_state,
|
||||
report_launch_start, report_launch_success,
|
||||
test_utils::install_fake_patch,
|
||||
updater::tests::init_for_testing,
|
||||
with_mut_state, with_state,
|
||||
};
|
||||
|
||||
/// Sets up no-op network hooks so that the fire-and-forget thread spawned
|
||||
|
||||
Reference in New Issue
Block a user