playdate_device/device/
command.rs

1use std::borrow::Cow;
2use std::fmt::Display;
3use std::io::Write;
4use std::path::Path;
5use std::path::PathBuf;
6
7
8#[derive(Debug, Clone)]
9#[cfg_attr(feature = "clap", derive(clap::Subcommand))]
10#[cfg_attr(feature = "clap", command(name = "COMMAND"))]
11pub enum Command {
12	/// Run custom pdx.
13	Run {
14		/// On-device path to the PDX package,
15		/// e.g. `/Games/MyGame.pdx` or `/System/Settings.pdx`
16		path: String,
17	},
18
19	/// Run system built-in pdx,
20	#[cfg_attr(feature = "clap", command(name = "run-sys"))]
21	RunSystem {
22		/// System built-in application,
23		#[cfg_attr(feature = "clap", arg(value_name = "NAME"))]
24		path: SystemPath,
25	},
26
27	/// Reboot into data segment USB disk
28	Datadisk,
29
30	/// Hibernate, semi-deep sleep mode.
31	#[cfg_attr(feature = "clap", command(visible_alias = "sleep"))]
32	Hibernate,
33
34	/// Turn console echo on or off.
35	Echo {
36		#[cfg_attr(feature = "clap", arg(default_value_t = Switch::On))]
37		value: Switch,
38	},
39
40	/// Request the device serial number.
41	#[cfg_attr(feature = "clap", command(visible_alias = "sn"))]
42	SerialNumber,
43
44	/// Request the device version info.
45	#[cfg_attr(feature = "clap", command(visible_alias = "V"))]
46	Version,
47
48	/// Simulate a button press.
49	///
50	/// +a/-a/a for down/up/both
51	#[cfg_attr(feature = "clap", command(visible_alias = "btn"))]
52	Button {
53		/// Button to press or release.
54		#[cfg_attr(feature = "clap", clap(subcommand))]
55		button: Button,
56	},
57
58	/// Send a message to a message handler in the current running program.
59	#[cfg_attr(feature = "clap", command(visible_alias = "msg"))]
60	Message {
61		/// Message to send.
62		message: String,
63	},
64
65	/// Send custom command.
66	#[cfg_attr(feature = "clap", command(visible_alias = "!"))]
67	Custom {
68		/// Command to send.
69		cmd: String,
70	},
71}
72
73
74#[derive(Debug, Clone)]
75#[cfg_attr(feature = "clap", derive(clap::Parser))]
76pub enum PdxPath {
77	System { path: SystemPath },
78	User { path: PathBuf },
79}
80
81
82#[derive(Debug, Clone)]
83#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
84pub enum SystemPath {
85	/// Launcher application, Home.
86	///
87	/// `/System/Launcher.pdx`.
88	Launcher,
89	/// Settings application.
90	///
91	/// `/System/Settings.pdx`.
92	Settings,
93	/// Playdate Catalog application.
94	///
95	/// `/System/Catalog.pdx`.
96	Catalog,
97}
98
99impl SystemPath {
100	pub fn as_path(&self) -> &Path {
101		match self {
102			Self::Launcher => Path::new("/System/Launcher.pdx"),
103			Self::Settings => Path::new("/System/Settings.pdx"),
104			Self::Catalog => Path::new("/System/Catalog.pdx"),
105		}
106	}
107}
108
109
110impl Command {
111	pub fn as_str(&self) -> Cow<'_, str> {
112		match self {
113			Command::Run { path } => format!("run {path}").into(),
114			Command::RunSystem { path } => format!("run {}", path.as_path().display()).into(),
115			Command::Datadisk => "datadisk".into(),
116			Command::Hibernate => "hibernate".into(),
117			Command::Echo { value: Switch::On } => "echo on".into(),
118			Command::Echo { value: Switch::Off } => "echo off".into(),
119			Command::SerialNumber => "serialread".into(),
120			Command::Version => "version".into(),
121			Command::Button { button } => format!("btn {}", button.as_btn_str()).into(),
122			Command::Message { message } => format!("msg {message}").into(),
123			Command::Custom { cmd } => cmd.into(),
124		}
125	}
126}
127
128
129impl Display for Command {
130	#[inline]
131	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.as_str().fmt(f) }
132}
133
134impl Command {
135	pub fn with_break(&self) -> String {
136		let cmd = self.as_str();
137		let mut line = String::with_capacity(cmd.len() + 2);
138		line.push('\n'); // extra break to ensure that the command starts from new line.
139		line.push_str(&cmd);
140		line.push('\n');
141		line
142	}
143
144	pub fn with_break_to<W: Write>(&self, mut writer: W) -> std::io::Result<()> { writeln!(writer, "\n{self}\n") }
145}
146
147
148#[derive(Debug, Clone, Copy)]
149#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
150#[cfg_attr(feature = "clap", clap(name = "BOOL"))]
151pub enum Switch {
152	/// Turn echo on.
153	/// [aliases: true]
154	#[cfg_attr(feature = "clap", value(alias = "true"))]
155	On,
156	/// Turn echo off.
157	/// [aliases: false]
158	#[cfg_attr(feature = "clap", value(alias = "false"))]
159	Off,
160}
161
162impl From<bool> for Switch {
163	fn from(value: bool) -> Self { if value { Switch::On } else { Switch::Off } }
164}
165
166impl From<Switch> for bool {
167	fn from(val: Switch) -> Self {
168		match val {
169			Switch::On => true,
170			Switch::Off => false,
171		}
172	}
173}
174
175impl std::fmt::Display for Switch {
176	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177		let value = match self {
178			Self::On => "on",
179			Self::Off => "off",
180		};
181		write!(f, "{value}")
182	}
183}
184
185
186#[derive(Debug, Clone, Copy)]
187#[cfg_attr(feature = "clap", derive(clap::Subcommand))]
188#[cfg_attr(feature = "clap", command(name = "BTN"))]
189pub enum Button {
190	A {
191		#[cfg_attr(feature = "clap", arg(required = false, default_value_t = ButtonAction::Both))]
192		action: ButtonAction,
193	},
194	B {
195		#[cfg_attr(feature = "clap", arg(required = false, default_value_t = ButtonAction::Both))]
196		action: ButtonAction,
197	},
198}
199
200impl std::fmt::Display for Button {
201	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202		match self {
203			Button::A { action } => write!(f, "{action}a"),
204			Button::B { action } => write!(f, "{action}b"),
205		}
206	}
207}
208
209impl Button {
210	pub fn as_btn_str(&self) -> String {
211		match self {
212			Button::A { action } => format!("{}a", action.as_btn_prefix()),
213			Button::B { action } => format!("{}b", action.as_btn_prefix()),
214		}
215	}
216}
217
218
219#[derive(Debug, Clone, Copy)]
220#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
221#[cfg_attr(feature = "clap", clap(name = "BTN"))]
222pub enum ButtonAction {
223	#[cfg_attr(feature = "clap", value(alias = "-"))]
224	Down,
225	#[cfg_attr(feature = "clap", value(alias = "+"))]
226	Up,
227	#[cfg_attr(feature = "clap", value(alias = "+-"), value(alias = "±"))]
228	Both,
229}
230
231impl Default for ButtonAction {
232	fn default() -> Self { Self::Both }
233}
234
235impl std::fmt::Display for ButtonAction {
236	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237		let value = match self {
238			Self::Down => "-",
239			Self::Up => "+",
240			Self::Both => "±",
241		};
242		write!(f, "{value}")
243	}
244}
245
246impl ButtonAction {
247	pub fn as_btn_prefix(&self) -> &'static str {
248		match self {
249			Self::Down => "+",
250			Self::Up => "-",
251			Self::Both => "",
252		}
253	}
254}