InputVerifier: make action_button a field of the action enum

Only MotionEvents with a button press or release action should have an
action button. Let's express this in the data structure by making
action_button a field of the relevant MotionActions, instead of a
separate field of MotionEvent that could potentially be set on
non-button actions.

This involves moving the translation from input_bindgen constants to
MotionAction into lib.rs, which I think makes more sense (as it keeps
the language interfacing code contained there), but also means we have
to move one piece of validation there too.

Bug: 245989146
Test: $ atest --host libinput_rust_test
Test: enable the verifier, check everything works as usual
Flag: EXEMPT refactor
Change-Id: Ida17429b0e12247b63a3ae44bab63e421d9fff0f
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 30e1c86..35ba04f 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -101,7 +101,7 @@
 
 /// A rust enum representation of a MotionEvent action.
 #[repr(u32)]
-#[derive(Eq, PartialEq)]
+#[derive(Clone, Copy, Eq, PartialEq)]
 pub enum MotionAction {
     /// ACTION_DOWN
     Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
@@ -132,9 +132,15 @@
     /// ACTION_SCROLL
     Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
     /// ACTION_BUTTON_PRESS
-    ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
+    ButtonPress {
+        /// The button being pressed.
+        action_button: MotionButton,
+    } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
     /// ACTION_BUTTON_RELEASE
-    ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+    ButtonRelease {
+        /// The button being released.
+        action_button: MotionButton,
+    } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
 }
 
 impl fmt::Display for MotionAction {
@@ -153,14 +159,20 @@
             MotionAction::Scroll => write!(f, "SCROLL"),
             MotionAction::HoverEnter => write!(f, "HOVER_ENTER"),
             MotionAction::HoverExit => write!(f, "HOVER_EXIT"),
-            MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"),
-            MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"),
+            MotionAction::ButtonPress { action_button } => {
+                write!(f, "BUTTON_PRESS({action_button:?})")
+            }
+            MotionAction::ButtonRelease { action_button } => {
+                write!(f, "BUTTON_RELEASE({action_button:?})")
+            }
         }
     }
 }
 
-impl From<u32> for MotionAction {
-    fn from(action: u32) -> Self {
+impl MotionAction {
+    /// Creates a [`MotionAction`] from an `AMOTION_EVENT_ACTION_…` constant and an action button
+    /// (which should be empty for all actions except `BUTTON_PRESS` and `…_RELEASE`).
+    pub fn from_code(action: u32, action_button: MotionButton) -> Self {
         let (action_masked, action_index) = MotionAction::breakdown_action(action);
         match action_masked {
             input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
@@ -178,14 +190,16 @@
             input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
             input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
             input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
-            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
-            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
+            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => {
+                MotionAction::ButtonPress { action_button }
+            }
+            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => {
+                MotionAction::ButtonRelease { action_button }
+            }
             _ => panic!("Unknown action: {}", action),
         }
     }
-}
 
-impl MotionAction {
     fn breakdown_action(action: u32) -> (u32, usize) {
         let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
         let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index f05936b..f87dd41 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -32,11 +32,8 @@
     /// The type of device that emitted the event.
     pub source: Source,
 
-    /// The type of event that took place, as one of the `input_bindgen::AMOTION_EVENT_…` constants.
-    pub action: u32,
-
-    /// For `BUTTON_PRESS` and `BUTTON_RELEASE` actions, the button being pressed or released.
-    pub action_button: MotionButton,
+    /// The type of event that took place.
+    pub action: MotionAction,
 
     /// The properties of each of the pointers involved in the event.
     pub pointer_properties: &'a [RustPointerProperties],
@@ -64,21 +61,11 @@
 
 /// Verifies the properties of an event that should always be true, regardless of the current state.
 fn verify_event(event: NotifyMotionArgs<'_>, verify_buttons: bool) -> Result<(), String> {
-    let action: MotionAction = event.action.into();
     let pointer_count = event.pointer_properties.len();
     if pointer_count < 1 {
-        return Err(format!("Invalid {action} event: no pointers"));
+        return Err(format!("Invalid {} event: no pointers", event.action));
     }
-    if event.action_button != MotionButton::empty()
-        && action != MotionAction::ButtonPress
-        && action != MotionAction::ButtonRelease
-    {
-        return Err(format!(
-            "Invalid {} event: has action button {:?} but is not a button action",
-            action, event.action_button
-        ));
-    }
-    match action {
+    match event.action {
         MotionAction::Down
         | MotionAction::HoverEnter
         | MotionAction::HoverExit
@@ -86,7 +73,8 @@
         | MotionAction::Up => {
             if pointer_count != 1 {
                 return Err(format!(
-                    "Invalid {action} event: there are {pointer_count} pointers in the event",
+                    "Invalid {} event: there are {} pointers in the event",
+                    event.action, pointer_count
                 ));
             }
         }
@@ -102,17 +90,22 @@
 
         MotionAction::PointerDown { action_index } | MotionAction::PointerUp { action_index } => {
             if action_index >= pointer_count {
-                return Err(format!("Got {action}, but event has {pointer_count} pointer(s)"));
+                return Err(format!(
+                    "Got {}, but event has {} pointer(s)",
+                    event.action, pointer_count
+                ));
             }
         }
 
-        MotionAction::ButtonPress | MotionAction::ButtonRelease => {
+        MotionAction::ButtonPress { action_button }
+        | MotionAction::ButtonRelease { action_button } => {
             if verify_buttons {
-                let button_count = event.action_button.iter().count();
+                let button_count = action_button.iter().count();
                 if button_count != 1 {
                     return Err(format!(
-                        "Invalid {action} event: must specify a single action button, not \
-                         {button_count} action buttons"
+                        "Invalid {} event: must specify a single action button, not {} action \
+                         buttons",
+                        event.action, button_count
                     ));
                 }
             }
@@ -136,23 +129,24 @@
 
 impl ButtonVerifier {
     pub fn process_event(&mut self, event: NotifyMotionArgs<'_>) -> Result<(), String> {
-        let action: MotionAction = event.action.into();
         if !self.pending_buttons.is_empty() {
             // We just saw a DOWN with some additional buttons in its state, so it should be
             // immediately followed by ButtonPress events for those buttons.
-            if action != MotionAction::ButtonPress
-                || !self.pending_buttons.contains(event.action_button)
-            {
-                return Err(format!(
-                    "After DOWN event, expected BUTTON_PRESS event(s) for {:?}, but got {} with \
-                     action button {:?}",
-                    self.pending_buttons, action, event.action_button
-                ));
-            } else {
-                self.pending_buttons -= event.action_button;
+            match event.action {
+                MotionAction::ButtonPress { action_button }
+                    if self.pending_buttons.contains(action_button) =>
+                {
+                    self.pending_buttons -= action_button;
+                }
+                _ => {
+                    return Err(format!(
+                        "After DOWN event, expected BUTTON_PRESS event(s) for {:?}, but got {}",
+                        self.pending_buttons, event.action
+                    ));
+                }
             }
         }
-        let expected_state = match action {
+        let expected_state = match event.action {
             MotionAction::Down => {
                 if self.button_state - event.button_state != MotionButton::empty() {
                     return Err(format!(
@@ -165,34 +159,32 @@
                 // extra buttons are valid on DOWN actions, so bypass the expected state check.
                 event.button_state
             }
-            MotionAction::ButtonPress => {
-                if self.button_state.contains(event.action_button) {
+            MotionAction::ButtonPress { action_button } => {
+                if self.button_state.contains(action_button) {
                     return Err(format!(
-                        "Duplicate BUTTON_PRESS; button state already contains {:?}",
-                        event.action_button
+                        "Duplicate BUTTON_PRESS; button state already contains {action_button:?}"
                     ));
                 }
-                self.button_state | event.action_button
+                self.button_state | action_button
             }
-            MotionAction::ButtonRelease => {
-                if !self.button_state.contains(event.action_button) {
+            MotionAction::ButtonRelease { action_button } => {
+                if !self.button_state.contains(action_button) {
                     return Err(format!(
-                        "Invalid BUTTON_RELEASE; button state doesn't contain {:?}",
-                        event.action_button
+                        "Invalid BUTTON_RELEASE; button state doesn't contain {action_button:?}",
                     ));
                 }
-                self.button_state - event.action_button
+                self.button_state - action_button
             }
             _ => self.button_state,
         };
         if event.button_state != expected_state {
             return Err(format!(
-                "Expected {action} button state to be {expected_state:?}, but was {:?}",
-                event.button_state
+                "Expected {} button state to be {:?}, but was {:?}",
+                event.action, expected_state, event.button_state
             ));
         }
         // DOWN events can have pending buttons, so don't update the state for them.
-        if action != MotionAction::Down {
+        if event.action != MotionAction::Down {
             self.button_state = event.button_state;
         }
         Ok(())
@@ -237,7 +229,7 @@
         if self.should_log {
             info!(
                 "Processing {} for device {:?} ({} pointer{}) on {}",
-                MotionAction::from(event.action).to_string(),
+                event.action,
                 event.device_id,
                 event.pointer_properties.len(),
                 if event.pointer_properties.len() == 1 { "" } else { "s" },
@@ -254,7 +246,7 @@
                 .process_event(event)?;
         }
 
-        match event.action.into() {
+        match event.action {
             MotionAction::Down => {
                 if self.touching_pointer_ids_by_device.contains_key(&event.device_id) {
                     return Err(format!(
@@ -430,6 +422,7 @@
     use crate::input_verifier::InputVerifier;
     use crate::input_verifier::NotifyMotionArgs;
     use crate::DeviceId;
+    use crate::MotionAction;
     use crate::MotionFlags;
     use crate::RustPointerProperties;
     use crate::Source;
@@ -438,8 +431,7 @@
     const BASE_EVENT: NotifyMotionArgs = NotifyMotionArgs {
         device_id: DeviceId(1),
         source: Source::Touchscreen,
-        action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-        action_button: MotionButton::empty(),
+        action: MotionAction::Down,
         pointer_properties: &BASE_POINTER_PROPERTIES,
         flags: MotionFlags::empty(),
         button_state: MotionButton::empty(),
@@ -458,7 +450,7 @@
             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                action: MotionAction::Down,
                 pointer_properties: &pointer_properties,
                 ..BASE_EVENT
             })
@@ -472,21 +464,21 @@
         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                action: MotionAction::Down,
                 pointer_properties: &pointer_properties,
                 ..BASE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                action: MotionAction::Move,
                 pointer_properties: &pointer_properties,
                 ..BASE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_UP,
+                action: MotionAction::Up,
                 pointer_properties: &pointer_properties,
                 ..BASE_EVENT
             })
@@ -500,7 +492,7 @@
         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                action: MotionAction::Down,
                 pointer_properties: &pointer_properties,
                 ..BASE_EVENT
             })
@@ -510,8 +502,7 @@
             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
-                    | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                action: MotionAction::PointerDown { action_index: 1 },
                 pointer_properties: &two_pointer_properties,
                 ..BASE_EVENT
             })
@@ -519,8 +510,7 @@
         // POINTER 0 UP
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP
-                    | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                action: MotionAction::PointerUp { action_index: 0 },
                 pointer_properties: &two_pointer_properties,
                 ..BASE_EVENT
             })
@@ -529,7 +519,7 @@
         let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_UP,
+                action: MotionAction::Up,
                 pointer_properties: &pointer_1_properties,
                 ..BASE_EVENT
             })
@@ -543,35 +533,35 @@
         assert!(verifier
             .process_movement(NotifyMotionArgs {
                 device_id: DeviceId(1),
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                action: MotionAction::Down,
                 ..BASE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
                 device_id: DeviceId(1),
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                action: MotionAction::Move,
                 ..BASE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
                 device_id: DeviceId(2),
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                action: MotionAction::Down,
                 ..BASE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
                 device_id: DeviceId(2),
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                action: MotionAction::Move,
                 ..BASE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
                 device_id: DeviceId(1),
-                action: input_bindgen::AMOTION_EVENT_ACTION_UP,
+                action: MotionAction::Up,
                 ..BASE_EVENT
             })
             .is_ok());
@@ -583,14 +573,14 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                action: MotionAction::Down,
                 flags: MotionFlags::empty(),
                 ..BASE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
+                action: MotionAction::Cancel,
                 flags: MotionFlags::CANCELED,
                 ..BASE_EVENT
             })
@@ -602,16 +592,10 @@
         let mut verifier =
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::Down, ..BASE_EVENT })
             .is_ok());
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::Cancel, ..BASE_EVENT })
             .is_err());
     }
 
@@ -620,10 +604,7 @@
         let mut verifier =
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_UP,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::Up, ..BASE_EVENT })
             .is_err());
     }
 
@@ -632,31 +613,19 @@
         let mut verifier =
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverMove, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverExit, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_ok());
     }
 
@@ -665,17 +634,11 @@
         let mut verifier =
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                ..BASE_EVENT
-            })
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_err());
     }
 
@@ -689,7 +652,7 @@
             .process_movement(NotifyMotionArgs {
                 device_id: DeviceId(2),
                 source: Source::MouseRelative,
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                action: MotionAction::Move,
                 ..BASE_EVENT
             })
             .is_ok());
@@ -703,7 +666,7 @@
         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                action: MotionAction::Down,
                 pointer_properties: &pointer_properties,
                 ..BASE_EVENT
             })
@@ -713,8 +676,7 @@
             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
-                    | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                action: MotionAction::PointerDown { action_index: 1 },
                 pointer_properties: &two_pointer_properties,
                 ..BASE_EVENT
             })
@@ -722,7 +684,7 @@
         // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+                action: MotionAction::Move,
                 pointer_properties: &pointer_properties,
                 ..BASE_EVENT
             })
@@ -735,8 +697,7 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Primary,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
@@ -749,8 +710,7 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::empty(),
+                action: MotionAction::ButtonPress { action_button: MotionButton::empty() },
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
@@ -763,8 +723,9 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Back | MotionButton::Forward,
+                action: MotionAction::ButtonPress {
+                    action_button: MotionButton::Back | MotionButton::Forward
+                },
                 button_state: MotionButton::Back | MotionButton::Forward,
                 ..BASE_MOUSE_EVENT
             })
@@ -777,8 +738,7 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Primary,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
@@ -791,44 +751,14 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Primary,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-                action_button: MotionButton::Primary,
-                button_state: MotionButton::Primary,
-                ..BASE_MOUSE_EVENT
-            })
-            .is_err());
-    }
-
-    #[test]
-    fn nonbutton_action_with_action_button() {
-        let mut verifier =
-            InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
-        assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                action_button: MotionButton::Primary,
-                button_state: MotionButton::empty(),
-                ..BASE_MOUSE_EVENT
-            })
-            .is_err());
-    }
-
-    #[test]
-    fn nonbutton_action_with_action_button_and_state() {
-        let mut verifier =
-            InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
-        assert!(verifier
-            .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                action_button: MotionButton::Primary,
+                action: MotionAction::ButtonRelease { action_button: MotionButton::Primary },
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
@@ -841,16 +771,14 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                action_button: MotionButton::empty(),
+                action: MotionAction::HoverEnter,
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
-                action_button: MotionButton::empty(),
+                action: MotionAction::HoverMove,
                 button_state: MotionButton::Back,
                 ..BASE_MOUSE_EVENT
             })
@@ -863,24 +791,21 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                action_button: MotionButton::empty(),
+                action: MotionAction::HoverEnter,
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Back,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
                 button_state: MotionButton::Back,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
-                action_button: MotionButton::empty(),
+                action: MotionAction::HoverMove,
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
@@ -893,16 +818,14 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Down,
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Primary,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
@@ -910,8 +833,7 @@
         // This UP event shouldn't change the button state; a BUTTON_RELEASE before it should.
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_UP,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Up,
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
@@ -924,16 +846,14 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Back,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
                 button_state: MotionButton::Back,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Back,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
                 button_state: MotionButton::Back,
                 ..BASE_MOUSE_EVENT
             })
@@ -946,8 +866,7 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-                action_button: MotionButton::Back,
+                action: MotionAction::ButtonRelease { action_button: MotionButton::Back },
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
@@ -960,16 +879,14 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Back,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
                 button_state: MotionButton::Back,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Forward,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Forward },
                 button_state: MotionButton::Back | MotionButton::Forward,
                 ..BASE_MOUSE_EVENT
             })
@@ -982,24 +899,21 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Down,
                 button_state: MotionButton::Primary | MotionButton::Secondary,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Primary,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Secondary,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Secondary },
                 button_state: MotionButton::Primary | MotionButton::Secondary,
                 ..BASE_MOUSE_EVENT
             })
@@ -1008,8 +922,7 @@
         // enough BUTTON_PRESSes were sent.
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Move,
                 button_state: MotionButton::Primary | MotionButton::Secondary,
                 ..BASE_MOUSE_EVENT
             })
@@ -1022,8 +935,7 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Down,
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
@@ -1031,8 +943,7 @@
         // The DOWN event itself is OK, but it needs to be immediately followed by a BUTTON_PRESS.
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Move,
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
@@ -1045,8 +956,7 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Down,
                 button_state: MotionButton::Primary | MotionButton::Secondary,
                 ..BASE_MOUSE_EVENT
             })
@@ -1055,16 +965,14 @@
         // BUTTON_PRESSes, one for each button.
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Primary,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Move,
                 button_state: MotionButton::Primary,
                 ..BASE_MOUSE_EVENT
             })
@@ -1077,16 +985,14 @@
             InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true);
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                action_button: MotionButton::Back,
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
                 button_state: MotionButton::Back,
                 ..BASE_MOUSE_EVENT
             })
             .is_ok());
         assert!(verifier
             .process_movement(NotifyMotionArgs {
-                action: input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                action_button: MotionButton::empty(),
+                action: MotionAction::Down,
                 button_state: MotionButton::empty(),
                 ..BASE_MOUSE_EVENT
             })
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 8b8a39e..7638559 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -152,11 +152,23 @@
              buttons need to be added to MotionButton."
         );
     };
+    let motion_action = MotionAction::from_code(action, motion_action_button);
+    if motion_action_button != MotionButton::empty() {
+        match motion_action {
+            MotionAction::ButtonPress { action_button: _ }
+            | MotionAction::ButtonRelease { action_button: _ } => {}
+            _ => {
+                return format!(
+                    "Invalid {motion_action} event: has action button {motion_action_button:?} but \
+                     is not a button action"
+                );
+            }
+        }
+    }
     let result = verifier.process_movement(NotifyMotionArgs {
         device_id: DeviceId(device_id),
         source: Source::from_bits(source).unwrap(),
-        action,
-        action_button: motion_action_button,
+        action: motion_action,
         pointer_properties,
         flags: motion_flags,
         button_state: motion_button_state,
@@ -227,3 +239,44 @@
     }
     classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap());
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::create_input_verifier;
+    use crate::process_movement;
+    use crate::RustPointerProperties;
+
+    const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }];
+
+    #[test]
+    fn verify_nonbutton_action_with_action_button() {
+        let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
+        assert!(process_movement(
+            &mut verifier,
+            1,
+            input_bindgen::AINPUT_SOURCE_MOUSE,
+            input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+            input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+            &BASE_POINTER_PROPERTIES,
+            0,
+            0,
+        )
+        .contains("button action"));
+    }
+
+    #[test]
+    fn verify_nonbutton_action_with_action_button_and_button_state() {
+        let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
+        assert!(process_movement(
+            &mut verifier,
+            1,
+            input_bindgen::AINPUT_SOURCE_MOUSE,
+            input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+            input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+            &BASE_POINTER_PROPERTIES,
+            0,
+            input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+        )
+        .contains("button action"));
+    }
+}