ratatui_action/invocation.rs
1//! Resolved arguments and invocation requests.
2//!
3//! Use this module at the dispatch boundary:
4//!
5//! - [`ActionArgs`] stores resolved input values keyed by [`InputId`].
6//! - [`ActionInvocation`] names the action, carries resolved arguments, and records source.
7//! - [`InvocationSource`] identifies the UI or integration that requested the action.
8
9use std::collections::BTreeMap;
10
11use crate::id::{ActionId, InputId};
12
13/// Resolved arguments for an action invocation.
14///
15/// Arguments are keyed by [`InputId`] so dispatch code can read values without
16/// knowing which UI surface collected them.
17///
18/// Method groups:
19///
20/// - **Construction and mutation:** [`new`](Self::new) and [`insert`](Self::insert).
21/// - **Inspection:** [`get`](Self::get), [`iter`](Self::iter), and [`is_empty`](Self::is_empty).
22///
23/// # Examples
24///
25/// ```
26/// use ratatui_action::id::InputId;
27/// use ratatui_action::invocation::ActionArgs;
28///
29/// let mut args = ActionArgs::new();
30/// args.insert("theme", "github-dark");
31///
32/// assert_eq!(args.get(&InputId::new("theme")), Some("github-dark"));
33/// assert!(!args.is_empty());
34/// ```
35#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct ActionArgs {
37 values: BTreeMap<InputId, String>,
38}
39
40impl ActionArgs {
41 /// Creates an empty argument set.
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 /// Inserts or replaces an argument value.
47 pub fn insert(&mut self, id: impl Into<InputId>, value: impl Into<String>) -> Option<String> {
48 self.values.insert(id.into(), value.into())
49 }
50
51 /// Returns an argument value by input id.
52 pub fn get(&self, id: &InputId) -> Option<&str> {
53 self.values.get(id).map(String::as_str)
54 }
55
56 /// Iterates over resolved argument values in input-id order.
57 pub fn iter(&self) -> impl Iterator<Item = (&InputId, &str)> {
58 self.values
59 .iter()
60 .map(|(input, value)| (input, value.as_str()))
61 }
62
63 /// Returns true when no argument values have been resolved.
64 pub fn is_empty(&self) -> bool {
65 self.values.is_empty()
66 }
67}
68
69/// A request for the application to run an action.
70///
71/// Invocation is the handoff point between a UI surface and application-owned
72/// dispatch. The action crate does not execute callbacks or mutate application
73/// state.
74///
75/// Use [`new`](Self::new) for actions without arguments and [`with_args`](Self::with_args) when a
76/// UI surface has collected inputs. Dispatch code usually reads [`id`](Self::id),
77/// [`args`](Self::args), and [`source`](Self::source).
78///
79/// # Examples
80///
81/// ```
82/// use ratatui_action::id::InputId;
83/// use ratatui_action::invocation::{ActionArgs, ActionInvocation, InvocationSource};
84///
85/// let mut args = ActionArgs::new();
86/// args.insert(InputId::new("theme"), "github-dark");
87///
88/// let invocation = ActionInvocation::with_args("theme.switch", args, InvocationSource::Palette);
89///
90/// assert_eq!(invocation.id().as_str(), "theme.switch");
91/// assert_eq!(invocation.source(), InvocationSource::Palette);
92/// ```
93#[derive(Clone, Debug, PartialEq, Eq)]
94pub struct ActionInvocation {
95 id: ActionId,
96 args: ActionArgs,
97 source: InvocationSource,
98}
99
100impl ActionInvocation {
101 /// Creates an invocation with no arguments.
102 pub fn new(id: impl Into<ActionId>, source: InvocationSource) -> Self {
103 Self {
104 id: id.into(),
105 args: ActionArgs::new(),
106 source,
107 }
108 }
109
110 /// Creates an invocation with resolved arguments.
111 pub fn with_args(id: impl Into<ActionId>, args: ActionArgs, source: InvocationSource) -> Self {
112 Self {
113 id: id.into(),
114 args,
115 source,
116 }
117 }
118
119 /// Returns the action identifier to invoke.
120 pub fn id(&self) -> &ActionId {
121 &self.id
122 }
123
124 /// Returns resolved invocation arguments.
125 pub fn args(&self) -> &ActionArgs {
126 &self.args
127 }
128
129 /// Returns the surface that requested this invocation.
130 pub fn source(&self) -> InvocationSource {
131 self.source
132 }
133}
134
135/// UI or integration surface that requested an action invocation.
136///
137/// The source lets dispatch code distinguish user intent from a palette,
138/// keybinding, menu, mouse action, or automation path when that distinction
139/// affects behavior or telemetry.
140///
141/// Match this enum when dispatch needs different policy for palette, keybinding,
142/// menu, mouse, or automation callers.
143#[derive(Clone, Copy, Debug, PartialEq, Eq)]
144pub enum InvocationSource {
145 /// Invocation came from a command palette.
146 Palette,
147 /// Invocation came from a keybinding.
148 KeyBinding,
149 /// Invocation came from a menu item.
150 Menu,
151 /// Invocation came from a mouse interaction.
152 Mouse,
153 /// Invocation came from automation or a non-interactive integration.
154 Automation,
155}