Skip to main content

aurora_modules/
clip.rs

1use aurora_core::{AuroraResult, Pipeline, Value};
2use std::process::Command;
3
4fn try_cmds(cmds: &[&[&str]]) -> Option<String> {
5    for args in cmds {
6        if let Ok(output) = Command::new(args[0]).args(&args[1..]).output() {
7            if output.status.success() {
8                let s = String::from_utf8_lossy(&output.stdout).trim().to_string();
9                if !s.is_empty() {
10                    return Some(s);
11                }
12            }
13        }
14    }
15    None
16}
17
18fn exec_cmd(args: &[&str]) -> AuroraResult<()> {
19    let result = Command::new(args[0])
20        .args(&args[1..])
21        .output()
22        .map_err(|e| aurora_core::AuroraError::ModuleError(
23            format!("clipboard command failed: {}", e)
24        ))?;
25    if !result.status.success() {
26        let stderr = String::from_utf8_lossy(&result.stderr);
27        return Err(aurora_core::AuroraError::ModuleError(
28            format!("clipboard command failed: {}", stderr)
29        ));
30    }
31    Ok(())
32}
33
34pub fn clip_get() -> AuroraResult<Pipeline> {
35    let content = try_cmds(&[
36        &["xclip", "-o", "-selection", "clipboard"],
37        &["wl-paste"],
38        &["pbpaste"],
39        &["powershell", "-Command", "Get-Clipboard"],
40    ])
41    .unwrap_or_default();
42
43    Ok(Pipeline::table(
44        vec!["clipboard".into()],
45        vec![vec![Value::String(content)]],
46    ))
47}
48
49pub fn clip_set(text: &[String]) -> AuroraResult<Pipeline> {
50    let input = text.join(" ");
51
52    let cmds: &[&[&str]] = &[
53        &["xclip", "-i", "-selection", "clipboard"],
54        &["wl-copy"],
55        &["pbcopy"],
56    ];
57
58    let mut success = false;
59    for args in cmds {
60        let child = Command::new(args[0])
61            .args(&args[1..])
62            .stdin(std::process::Stdio::piped())
63            .spawn()
64            .ok();
65
66        if let Some(mut c) = child {
67            use std::io::Write;
68            if let Some(mut stdin) = c.stdin.take() {
69                let _ = stdin.write_all(input.as_bytes());
70                drop(stdin);
71            }
72            if let Ok(status) = c.wait() {
73                if status.success() {
74                    success = true;
75                    break;
76                }
77            }
78        }
79    }
80
81    if !success && cfg!(target_os = "windows") {
82        let escaped = input.replace('\'', "''");
83        exec_cmd(&["powershell", "-Command", &format!("Set-Clipboard '{}'", escaped)])?;
84        success = true;
85    }
86
87    if !success {
88        return Err(aurora_core::AuroraError::CommandNotFound(
89            "no clipboard tool found (install xclip, wl-clipboard, or pbcopy)".into()
90        ));
91    }
92
93    Ok(Pipeline::table(
94        vec!["action".into(), "content".into()],
95        vec![vec![
96            Value::String("set".into()),
97            Value::String(input),
98        ]],
99    ))
100}
101
102pub fn clip_clear() -> AuroraResult<Pipeline> {
103    clip_set(&[String::new()])
104}