diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9b78034 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: "-Dwarnings" + +jobs: + check: + name: Check + runs-on: windows-latest + steps: + - uses: actions/checkout@v6 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Format check + run: cargo fmt --check + + - name: Clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Build + run: cargo build --verbose + + - name: Test + run: cargo test --verbose diff --git a/src/animation.rs b/src/animation.rs index f3a954e..b01506b 100644 --- a/src/animation.rs +++ b/src/animation.rs @@ -9,7 +9,6 @@ use windows::Win32::UI::WindowsAndMessaging::{ SWP_SHOWWINDOW, SetWindowLongPtrW, SetWindowPos, WS_EX_COMPOSITED, }; -use crate::error::AnimationError; use crate::tracking::WindowBounds; /// Slide direction @@ -106,7 +105,7 @@ pub fn run_animation( bounds: &WindowBounds, work_area: &RECT, slide_in: bool, -) -> Result<(), AnimationError> { +) { let duration = Duration::from_millis(config.duration_ms as u64); let start = Instant::now(); @@ -198,8 +197,6 @@ pub fn run_animation( let _ = InvalidateRect(Some(hwnd), None, true); SetWindowLongPtrW(hwnd, GWL_EXSTYLE, original_exstyle); } - - Ok(()) } #[cfg(test)] diff --git a/src/error.rs b/src/error.rs index cf3efc7..7b22f64 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,13 +2,6 @@ use thiserror::Error; -/// Animation errors (graceful degradation) -#[derive(Debug, Error)] -pub enum AnimationError { - #[error("GetMonitorInfo failed")] - MonitorInfo, -} - /// Focus tracking errors (graceful degradation) #[derive(Debug, Error)] pub enum FocusError { @@ -23,12 +16,6 @@ pub enum FocusError { mod tests { use super::*; - #[test] - fn test_animation_error_display() { - let err = AnimationError::MonitorInfo; - assert_eq!(err.to_string(), "GetMonitorInfo failed"); - } - #[test] fn test_focus_error_display() { let err = FocusError::HookInstall; diff --git a/src/main.rs b/src/main.rs index a0ce289..e4e1f42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -186,9 +186,7 @@ fn toggle_window() { } // 4. Slide out - if let Err(e) = run_animation(hwnd, &config, direction, &bounds, &work_area, false) { - error!("Animation error: {e}"); - } + run_animation(hwnd, &config, direction, &bounds, &work_area, false); WINDOW_VISIBLE.store(false, Ordering::SeqCst); info!(direction = ?direction, "Window: focus restored → slide out → hidden"); } else { @@ -205,9 +203,7 @@ fn toggle_window() { focus::save_previous(prev); // 4. Slide in - if let Err(e) = run_animation(hwnd, &config, direction, &bounds, &work_area, true) { - error!("Animation error: {e}"); - } + run_animation(hwnd, &config, direction, &bounds, &work_area, true); let _ = unsafe { SetForegroundWindow(hwnd) }; focus::set_target(hwnd); if let Err(e) = focus::install_hook(hwnd) { @@ -250,9 +246,7 @@ fn handle_focus_lost() { let direction = tracking::calc_direction(&bounds, &work_area); let config = AnimConfig::default(); - if let Err(e) = run_animation(target, &config, direction, &bounds, &work_area, false) { - error!("Animation error: {e}"); - } + run_animation(target, &config, direction, &bounds, &work_area, false); WINDOW_VISIBLE.store(false, Ordering::SeqCst); info!(direction = ?direction, "Window: focus lost → hidden"); } diff --git a/src/tracking.rs b/src/tracking.rs index d63d6aa..a7a6811 100644 --- a/src/tracking.rs +++ b/src/tracking.rs @@ -57,7 +57,7 @@ pub fn is_tracked_valid() -> bool { /// Returns captured bounds, or None if GetWindowRect fails pub fn save_bounds(hwnd: HWND) -> Option { let mut rect = RECT::default(); - if !unsafe { GetWindowRect(hwnd, &mut rect) }.is_ok() { + if unsafe { GetWindowRect(hwnd, &mut rect) }.is_err() { return None; } @@ -82,8 +82,9 @@ pub fn load_bounds() -> Option { } } -/// Clear stored bounds -pub fn clear_bounds() { +/// Clear stored bounds (test-only) +#[cfg(test)] +fn clear_bounds() { let ptr = STORED_BOUNDS.swap(null_mut(), Ordering::SeqCst); if !ptr.is_null() { // Safety: ptr was created by Box::into_raw