diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc4c028..8e0d2bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,4 +49,6 @@ jobs: run: msbuild -t:build examples/example.wixproj - name: Install example MSI if: ${{ success() && (inputs.full || github.event_name != 'workflow_call') }} - run: start-process msiexec.exe -args '/i', "$PWD\\target\\debug\\example.msi", '/qn' -wait + run: | + $p = start-process msiexec.exe -args '/i', "$PWD\target\debug\example.msi", '/qn' -wait -passthru + exit $p.ExitCode diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b649997..044ad2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ The following software is recommended: To build and test, simply run: ```powershell -cargo test --features nightly +cargo test --all-features ``` By default, this will build x64 custom action DLLs from under _examples_. diff --git a/Cargo.toml b/Cargo.toml index 95beaa2..5f3a19a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "msica" -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "MIT" description = "Rust for Windows Installer Custom Actions" diff --git a/README.md b/README.md index 23cb151..b62d874 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ that can be called in immediate and deferred custom actions. You can define custom actions in Rust using its [foreign function interface][ffi] like: ```rust -use msica::*; +use msica::prelude::*; const ERROR_SUCCESS: u32 = 0; const ERROR_INSTALL_FAILURE: u32 = 1603; @@ -43,7 +43,7 @@ If you enable the `nightly` feature and use the nightly toolchain, you can use t propagate errors: ```rust -use msica::*; +use msica::prelude::*; #[no_mangle] pub extern "C" fn MyCustomAction(session: Session) -> CustomActionResult { @@ -52,7 +52,7 @@ pub extern "C" fn MyCustomAction(session: Session) -> CustomActionResult { vec![Field::IntegerData(1), Field::StringData("example".to_owned())], )?; session.message(MessageType::User, &record); - CustomActionResult::Succeed + Success } ``` diff --git a/examples/deferred/lib.rs b/examples/deferred/lib.rs index 713e8e2..f0e66f0 100644 --- a/examples/deferred/lib.rs +++ b/examples/deferred/lib.rs @@ -1,8 +1,7 @@ // Copyright 2022 Heath Stewart. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -use msica::CustomActionResult::Succeed; -use msica::*; +use msica::prelude::*; #[no_mangle] pub extern "C" fn DeferredExampleCustomAction(session: Session) -> CustomActionResult { @@ -32,7 +31,7 @@ pub extern "C" fn DeferredExampleCustomAction(session: Session) -> CustomActionR ); session.do_deferred_action("DeferredExampleCustomActionDeferred", &data)?; } - Succeed + Success } #[no_mangle] @@ -49,5 +48,5 @@ pub extern "C" fn DeferredExampleCustomActionDeferred(session: Session) -> Custo ], )?; session.message(MessageType::Info, &record); - Succeed + Success } diff --git a/examples/skip/lib.rs b/examples/skip/lib.rs index 9329fcd..7d84626 100644 --- a/examples/skip/lib.rs +++ b/examples/skip/lib.rs @@ -1,8 +1,7 @@ // Copyright 2022 Heath Stewart. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -use msica::CustomActionResult::{Skip, Succeed}; -use msica::*; +use msica::prelude::*; #[no_mangle] pub extern "C" fn SkipExampleCustomAction(session: Session) -> CustomActionResult { @@ -17,6 +16,10 @@ pub extern "C" fn SkipExampleCustomAction(session: Session) -> CustomActionResul } true => { let data = session.property("CustomActionData")?; + if data.is_empty() { + return Success; + } + // Unnecessarily parsing the string demonstrates using ? for any possible error. let data = data.parse::()?; if data == 2 { @@ -24,5 +27,5 @@ pub extern "C" fn SkipExampleCustomAction(session: Session) -> CustomActionResul } } } - Succeed + Success } diff --git a/src/error/mod.rs b/src/error.rs similarity index 100% rename from src/error/mod.rs rename to src/error.rs diff --git a/src/error/experimental.rs b/src/error/experimental.rs index 8d6c6c2..87ab079 100644 --- a/src/error/experimental.rs +++ b/src/error/experimental.rs @@ -2,8 +2,8 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. #![cfg(feature = "nightly")] -use super::{Error, ErrorKind}; use crate::ffi; +use crate::{Error, ErrorKind}; use std::convert::Infallible; use std::fmt::Display; use std::num::NonZeroU32; @@ -17,7 +17,7 @@ use std::ops::{ControlFlow, FromResidual, Try}; /// /// ```no_run /// use std::ffi::OsString; -/// use msica::{Session, CustomActionResult}; +/// use msica::prelude::*; /// /// #[no_mangle] /// pub extern "C" fn MyCustomAction(session: Session) -> CustomActionResult { @@ -25,14 +25,14 @@ use std::ops::{ControlFlow, FromResidual, Try}; /// /// // Do something with `productName`. /// -/// CustomActionResult::Succeed +/// Success /// } /// ``` #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] pub enum CustomActionResult { /// Completed actions successfully. - Succeed = ffi::ERROR_SUCCESS, + Success = ffi::ERROR_SUCCESS, /// Skip remaining actions. Not an error. Skip = ffi::ERROR_NO_MORE_ITEMS, @@ -41,7 +41,7 @@ pub enum CustomActionResult { Cancel = ffi::ERROR_INSTALL_USEREXIT, /// Unrecoverable error occurred. - Fail = ffi::ERROR_INSTALL_FAILURE, + Failure = ffi::ERROR_INSTALL_FAILURE, /// Action not executed. NotExecuted = ffi::ERROR_FUNCTION_NOT_CALLED, @@ -50,10 +50,10 @@ pub enum CustomActionResult { impl Display for CustomActionResult { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let error = match &self { - Self::Succeed => "succeeded", - Self::Skip => "skip remaining actions", - Self::Cancel => "user canceled", - Self::Fail => "failed", + Self::Success => "completed successfully", + Self::Skip => "skipped remaining actions", + Self::Cancel => "user canceled installation", + Self::Failure => "fatal error during installation", Self::NotExecuted => "not executed", }; @@ -64,54 +64,49 @@ impl Display for CustomActionResult { impl From for CustomActionResult { fn from(code: u32) -> Self { match code { - ffi::ERROR_SUCCESS => CustomActionResult::Succeed, + ffi::ERROR_SUCCESS => CustomActionResult::Success, ffi::ERROR_NO_MORE_ITEMS => CustomActionResult::Skip, ffi::ERROR_INSTALL_USEREXIT => CustomActionResult::Cancel, ffi::ERROR_FUNCTION_NOT_CALLED => CustomActionResult::NotExecuted, - _ => CustomActionResult::Fail, + _ => CustomActionResult::Failure, } } } -#[allow(clippy::from_over_into)] -impl Into for CustomActionResult { - fn into(self) -> u32 { - self as u32 +impl From for u32 { + fn from(value: CustomActionResult) -> Self { + value as Self } } -/// This type is an implementation detail and not intended for direct use. -#[doc(hidden)] -pub struct CustomActionResultCode(NonZeroU32); - -impl From for CustomActionResultCode { +impl From for NonZeroU32 { fn from(value: CustomActionResult) -> Self { - CustomActionResultCode(NonZeroU32::new(value.into()).unwrap()) + NonZeroU32::new(value.into()).unwrap() } } impl Try for CustomActionResult { type Output = u32; - type Residual = CustomActionResultCode; + type Residual = NonZeroU32; fn branch(self) -> ControlFlow { match self { - Self::Succeed => ControlFlow::Continue(ffi::ERROR_SUCCESS), + Self::Success => ControlFlow::Continue(ffi::ERROR_SUCCESS), _ => ControlFlow::Break(self.into()), } } fn from_output(_: Self::Output) -> Self { - CustomActionResult::Succeed + CustomActionResult::Success } } impl FromResidual for CustomActionResult { - fn from_residual(residual: CustomActionResultCode) -> Self { - match residual.0.into() { + fn from_residual(residual: ::Residual) -> Self { + match residual.into() { ffi::ERROR_NO_MORE_ITEMS => CustomActionResult::Skip, ffi::ERROR_INSTALL_USEREXIT => CustomActionResult::Cancel, - ffi::ERROR_INSTALL_FAILURE => CustomActionResult::Fail, + ffi::ERROR_INSTALL_FAILURE => CustomActionResult::Failure, ffi::ERROR_FUNCTION_NOT_CALLED => CustomActionResult::NotExecuted, code => panic!("unexpected error code {}", code), } @@ -123,14 +118,14 @@ impl FromResidual> for CustomActionResult { let error = residual.unwrap_err(); match error.kind() { ErrorKind::ErrorCode(code) => CustomActionResult::from(code.get()), - _ => CustomActionResult::Fail, + _ => CustomActionResult::Failure, } } } impl FromResidual> for CustomActionResult { default fn from_residual(_: std::result::Result) -> Self { - CustomActionResult::Fail + CustomActionResult::Failure } } @@ -142,7 +137,7 @@ mod tests { #[test] fn from_u32() { - assert_eq!(CustomActionResult::Succeed, CustomActionResult::from(0u32)); + assert_eq!(CustomActionResult::Success, CustomActionResult::from(0u32)); assert_eq!(CustomActionResult::Skip, CustomActionResult::from(259u32)); assert_eq!( CustomActionResult::Cancel, @@ -152,16 +147,19 @@ mod tests { CustomActionResult::NotExecuted, CustomActionResult::from(1626u32) ); - assert_eq!(CustomActionResult::Fail, CustomActionResult::from(1603u32)); - assert_eq!(CustomActionResult::Fail, CustomActionResult::from(1u32)); + assert_eq!( + CustomActionResult::Failure, + CustomActionResult::from(1603u32) + ); + assert_eq!(CustomActionResult::Failure, CustomActionResult::from(1u32)); } #[test] fn into_u32() { - assert_eq!(0u32, Into::::into(CustomActionResult::Succeed)); + assert_eq!(0u32, Into::::into(CustomActionResult::Success)); assert_eq!(259u32, Into::::into(CustomActionResult::Skip)); assert_eq!(1602u32, Into::::into(CustomActionResult::Cancel)); - assert_eq!(1603u32, Into::::into(CustomActionResult::Fail)); + assert_eq!(1603u32, Into::::into(CustomActionResult::Failure)); assert_eq!(1626u32, Into::::into(CustomActionResult::NotExecuted)); } diff --git a/src/lib.rs b/src/lib.rs index de51323..fe4c902 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,15 @@ pub use record::{Field, Record}; pub use session::{MessageType, RunMode, Session}; pub use view::{ModifyMode, View}; +pub mod prelude { + #[cfg(feature = "nightly")] + pub use crate::error::experimental::CustomActionResult::{self, *}; + // Export objects and enums used in inputs to those objects' methods. + pub use crate::{ + Database, Error, Field, MessageType, ModifyMode, Record, Result, RunMode, Session, View, + }; +} + /// Gets the last Windows Installer error for the current process. /// /// # Example diff --git a/src/session.rs b/src/session.rs index ed9f80b..589d52b 100644 --- a/src/session.rs +++ b/src/session.rs @@ -10,7 +10,7 @@ use std::ffi::CString; /// # Example /// /// ```no_run -/// use msica::*; +/// use msica::prelude::*; /// const ERROR_SUCCESS: u32 = 0; /// /// #[no_mangle] @@ -62,7 +62,7 @@ impl Session { /// # Example /// /// ```no_run - /// use msica::*; + /// use msica::prelude::*; /// const ERROR_SUCCESS: u32 = 0; /// /// #[no_mangle] @@ -103,7 +103,7 @@ impl Session { /// You could use the same custom action entry point for scheduling and executing deferred actions: /// /// ```no_run - /// use msica::*; + /// use msica::prelude::*; /// const ERROR_SUCCESS: u32 = 0; /// /// #[no_mangle]