browser_control/cli/
key.rs1use std::time::Duration;
11
12use anyhow::Result;
13
14use crate::cli::env_resolver::Source;
15use crate::cli::route;
16use crate::cli::trace::CommandTrace;
17use crate::cli::type_cmd::target_matching;
18use crate::session::backend::open_backend;
19use crate::session::keys::{parse_chord, Chord};
20use crate::session::with_scratch_recovery;
21
22const KEY_TIMEOUT: Duration = Duration::from_secs(30);
23
24pub async fn run(browser: Option<String>, key: String, target: Option<String>) -> Result<()> {
25 let mut trace = CommandTrace::new("key");
26 let result = run_inner(browser, &key, target, &mut trace).await;
27 trace.finish(result)?;
28 println!("pressed {key}");
29 Ok(())
30}
31
32async fn run_inner(
33 browser: Option<String>,
34 key: &str,
35 target: Option<String>,
36 trace: &mut CommandTrace,
37) -> Result<()> {
38 let chord = parse_chord(key)?;
41
42 let r = route::preamble(browser, target.as_deref(), trace).await?;
43 let resolved = &r.resolved;
44
45 match (r.tab_name.clone(), target) {
46 (Some(name), None) => {
48 trace.route("named-tab").tab_name(&name);
49 let chord = chord.clone();
50 route::run_named_tab(
51 &r,
52 &name,
53 "named tabs (`<browser>/<name>`) require a registered browser; \
54 external endpoints can't carry tab names",
55 move |b, target_id| {
56 let chord: Chord = chord.clone();
57 async move { b.press_key_on_tab(&target_id, &chord, KEY_TIMEOUT).await }
58 },
59 )
60 .await
61 }
62 (None, None) => {
64 let browser_name = match &resolved.source {
65 Source::Registered { name } => name.clone(),
66 Source::External => anyhow::bail!(
69 "`key` needs a registered browser or an explicit `--target`; \
70 external endpoints have no tab to focus"
71 ),
72 };
73 trace.route("scratch");
74 let backend = open_backend(&resolved.endpoint, resolved.engine).await?;
75 with_scratch_recovery(&backend, &r.registry, &browser_name, move |b, target_id| {
76 let chord: Chord = chord.clone();
77 async move { b.press_key_on_tab(&target_id, &chord, KEY_TIMEOUT).await }
78 })
79 .await
80 }
81 (None, Some(regex)) => {
86 trace.route("target-regex");
87 let backend = open_backend(&resolved.endpoint, resolved.engine).await?;
88 let target_id = target_matching(&backend, ®ex).await?;
89 let out = backend
90 .press_key_on_tab(&target_id, &chord, KEY_TIMEOUT)
91 .await;
92 backend.release().await;
93 out
94 }
95 _ => unreachable!("mutual exclusion checked in preamble"),
96 }
97}