Skip to content

Commit

Permalink
Add prelude module, rename CustomActionResults (#30)
Browse files Browse the repository at this point in the history
* Add prelude module, rename CustomActionResults

Also fixes the SkipExampleCustomAction that wasn't actually working properly.

* Check msiexec exit code in CI

* Better align CustomActionResult errors with Windows text

Also use crate:: instead of super::
  • Loading branch information
heaths authored Feb 17, 2024
1 parent 4a30c72 commit ab28086
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 50 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
```

Expand Down
7 changes: 3 additions & 4 deletions examples/deferred/lib.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -32,7 +31,7 @@ pub extern "C" fn DeferredExampleCustomAction(session: Session) -> CustomActionR
);
session.do_deferred_action("DeferredExampleCustomActionDeferred", &data)?;
}
Succeed
Success
}

#[no_mangle]
Expand All @@ -49,5 +48,5 @@ pub extern "C" fn DeferredExampleCustomActionDeferred(session: Session) -> Custo
],
)?;
session.message(MessageType::Info, &record);
Succeed
Success
}
9 changes: 6 additions & 3 deletions examples/skip/lib.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -17,12 +16,16 @@ 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::<u32>()?;
if data == 2 {
return Skip;
}
}
}
Succeed
Success
}
File renamed without changes.
66 changes: 32 additions & 34 deletions src/error/experimental.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,22 +17,22 @@ 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 {
/// let productName = session.property("ProductName")?;
///
/// // 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,
Expand All @@ -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,
Expand All @@ -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",
};

Expand All @@ -64,54 +64,49 @@ impl Display for CustomActionResult {
impl From<u32> 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<u32> for CustomActionResult {
fn into(self) -> u32 {
self as u32
impl From<CustomActionResult> 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<CustomActionResult> for CustomActionResultCode {
impl From<CustomActionResult> 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<Self::Residual, Self::Output> {
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: <CustomActionResult as Try>::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),
}
Expand All @@ -123,14 +118,14 @@ impl FromResidual<Result<Infallible, Error>> for CustomActionResult {
let error = residual.unwrap_err();
match error.kind() {
ErrorKind::ErrorCode(code) => CustomActionResult::from(code.get()),
_ => CustomActionResult::Fail,
_ => CustomActionResult::Failure,
}
}
}

impl<E: std::error::Error> FromResidual<std::result::Result<Infallible, E>> for CustomActionResult {
default fn from_residual(_: std::result::Result<Infallible, E>) -> Self {
CustomActionResult::Fail
CustomActionResult::Failure
}
}

Expand All @@ -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,
Expand All @@ -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::<u32>::into(CustomActionResult::Succeed));
assert_eq!(0u32, Into::<u32>::into(CustomActionResult::Success));
assert_eq!(259u32, Into::<u32>::into(CustomActionResult::Skip));
assert_eq!(1602u32, Into::<u32>::into(CustomActionResult::Cancel));
assert_eq!(1603u32, Into::<u32>::into(CustomActionResult::Fail));
assert_eq!(1603u32, Into::<u32>::into(CustomActionResult::Failure));
assert_eq!(1626u32, Into::<u32>::into(CustomActionResult::NotExecuted));
}

Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::ffi::CString;
/// # Example
///
/// ```no_run
/// use msica::*;
/// use msica::prelude::*;
/// const ERROR_SUCCESS: u32 = 0;
///
/// #[no_mangle]
Expand Down Expand Up @@ -62,7 +62,7 @@ impl Session {
/// # Example
///
/// ```no_run
/// use msica::*;
/// use msica::prelude::*;
/// const ERROR_SUCCESS: u32 = 0;
///
/// #[no_mangle]
Expand Down Expand Up @@ -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]
Expand Down

0 comments on commit ab28086

Please sign in to comment.