Fix clippy lints (#399)
* cargo fmt * fix: remove redundant `use libc` import (clippy::single_component_path_imports) * fix: use `.first()` instead of `.get(0)` (clippy::get_first) * fix: use `.copied()` instead of `.map(|s| *s)` (clippy::map_clone) * fix: allow too_many_arguments on ensure_daemon (clippy::too_many_arguments) * fix: use `then_some` instead of `then` with closure (clippy::unnecessary_lazy_evaluations) * fix: use pattern match instead of redundant guard (clippy::redundant_guards) * fix: use pattern match instead of redundant guard in commands.rs (clippy::redundant_guards) * fix: use `contains()` instead of `iter().any()` for simple equality (clippy::manual_contains) * Add changeset
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"agent-browser": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix all Clippy lint warnings in the Rust CLI: remove redundant import, use `.first()` instead of `.get(0)`, use `.copied()` instead of `.map(|s| *s)`, use `.contains()` instead of `.iter().any()`, use `then_some` instead of lazy `then`, and simplify redundant match guards.
|
||||||
+72
-62
@@ -81,7 +81,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
match cmd {
|
match cmd {
|
||||||
// === Navigation ===
|
// === Navigation ===
|
||||||
"open" | "goto" | "navigate" => {
|
"open" | "goto" | "navigate" => {
|
||||||
let url = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let url = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: cmd.to_string(),
|
context: cmd.to_string(),
|
||||||
usage: "open <url>",
|
usage: "open <url>",
|
||||||
})?;
|
})?;
|
||||||
@@ -117,63 +117,63 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
|
|
||||||
// === Core Actions ===
|
// === Core Actions ===
|
||||||
"click" => {
|
"click" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "click".to_string(),
|
context: "click".to_string(),
|
||||||
usage: "click <selector>",
|
usage: "click <selector>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "click", "selector": sel }))
|
Ok(json!({ "id": id, "action": "click", "selector": sel }))
|
||||||
}
|
}
|
||||||
"dblclick" => {
|
"dblclick" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "dblclick".to_string(),
|
context: "dblclick".to_string(),
|
||||||
usage: "dblclick <selector>",
|
usage: "dblclick <selector>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "dblclick", "selector": sel }))
|
Ok(json!({ "id": id, "action": "dblclick", "selector": sel }))
|
||||||
}
|
}
|
||||||
"fill" => {
|
"fill" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "fill".to_string(),
|
context: "fill".to_string(),
|
||||||
usage: "fill <selector> <text>",
|
usage: "fill <selector> <text>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "fill", "selector": sel, "value": rest[1..].join(" ") }))
|
Ok(json!({ "id": id, "action": "fill", "selector": sel, "value": rest[1..].join(" ") }))
|
||||||
}
|
}
|
||||||
"type" => {
|
"type" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "type".to_string(),
|
context: "type".to_string(),
|
||||||
usage: "type <selector> <text>",
|
usage: "type <selector> <text>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "type", "selector": sel, "text": rest[1..].join(" ") }))
|
Ok(json!({ "id": id, "action": "type", "selector": sel, "text": rest[1..].join(" ") }))
|
||||||
}
|
}
|
||||||
"hover" => {
|
"hover" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "hover".to_string(),
|
context: "hover".to_string(),
|
||||||
usage: "hover <selector>",
|
usage: "hover <selector>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "hover", "selector": sel }))
|
Ok(json!({ "id": id, "action": "hover", "selector": sel }))
|
||||||
}
|
}
|
||||||
"focus" => {
|
"focus" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "focus".to_string(),
|
context: "focus".to_string(),
|
||||||
usage: "focus <selector>",
|
usage: "focus <selector>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "focus", "selector": sel }))
|
Ok(json!({ "id": id, "action": "focus", "selector": sel }))
|
||||||
}
|
}
|
||||||
"check" => {
|
"check" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "check".to_string(),
|
context: "check".to_string(),
|
||||||
usage: "check <selector>",
|
usage: "check <selector>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "check", "selector": sel }))
|
Ok(json!({ "id": id, "action": "check", "selector": sel }))
|
||||||
}
|
}
|
||||||
"uncheck" => {
|
"uncheck" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "uncheck".to_string(),
|
context: "uncheck".to_string(),
|
||||||
usage: "uncheck <selector>",
|
usage: "uncheck <selector>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "uncheck", "selector": sel }))
|
Ok(json!({ "id": id, "action": "uncheck", "selector": sel }))
|
||||||
}
|
}
|
||||||
"select" => {
|
"select" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "select".to_string(),
|
context: "select".to_string(),
|
||||||
usage: "select <selector> <value...>",
|
usage: "select <selector> <value...>",
|
||||||
})?;
|
})?;
|
||||||
@@ -189,7 +189,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"drag" => {
|
"drag" => {
|
||||||
let src = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let src = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "drag".to_string(),
|
context: "drag".to_string(),
|
||||||
usage: "drag <source> <target>",
|
usage: "drag <source> <target>",
|
||||||
})?;
|
})?;
|
||||||
@@ -200,14 +200,14 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
Ok(json!({ "id": id, "action": "drag", "source": src, "target": tgt }))
|
Ok(json!({ "id": id, "action": "drag", "source": src, "target": tgt }))
|
||||||
}
|
}
|
||||||
"upload" => {
|
"upload" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "upload".to_string(),
|
context: "upload".to_string(),
|
||||||
usage: "upload <selector> <files...>",
|
usage: "upload <selector> <files...>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "upload", "selector": sel, "files": &rest[1..] }))
|
Ok(json!({ "id": id, "action": "upload", "selector": sel, "files": &rest[1..] }))
|
||||||
}
|
}
|
||||||
"download" => {
|
"download" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "download".to_string(),
|
context: "download".to_string(),
|
||||||
usage: "download <selector> <path>",
|
usage: "download <selector> <path>",
|
||||||
})?;
|
})?;
|
||||||
@@ -220,21 +220,21 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
|
|
||||||
// === Keyboard ===
|
// === Keyboard ===
|
||||||
"press" | "key" => {
|
"press" | "key" => {
|
||||||
let key = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let key = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "press".to_string(),
|
context: "press".to_string(),
|
||||||
usage: "press <key>",
|
usage: "press <key>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "press", "key": key }))
|
Ok(json!({ "id": id, "action": "press", "key": key }))
|
||||||
}
|
}
|
||||||
"keydown" => {
|
"keydown" => {
|
||||||
let key = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let key = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "keydown".to_string(),
|
context: "keydown".to_string(),
|
||||||
usage: "keydown <key>",
|
usage: "keydown <key>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "keydown", "key": key }))
|
Ok(json!({ "id": id, "action": "keydown", "key": key }))
|
||||||
}
|
}
|
||||||
"keyup" => {
|
"keyup" => {
|
||||||
let key = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let key = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "keyup".to_string(),
|
context: "keyup".to_string(),
|
||||||
usage: "keyup <key>",
|
usage: "keyup <key>",
|
||||||
})?;
|
})?;
|
||||||
@@ -243,7 +243,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
|
|
||||||
// === Scroll ===
|
// === Scroll ===
|
||||||
"scroll" => {
|
"scroll" => {
|
||||||
let dir = rest.get(0).unwrap_or(&"down");
|
let dir = rest.first().unwrap_or(&"down");
|
||||||
let amount = rest
|
let amount = rest
|
||||||
.get(1)
|
.get(1)
|
||||||
.and_then(|s| s.parse::<i32>().ok())
|
.and_then(|s| s.parse::<i32>().ok())
|
||||||
@@ -251,7 +251,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
Ok(json!({ "id": id, "action": "scroll", "direction": dir, "amount": amount }))
|
Ok(json!({ "id": id, "action": "scroll", "direction": dir, "amount": amount }))
|
||||||
}
|
}
|
||||||
"scrollintoview" | "scrollinto" => {
|
"scrollintoview" | "scrollinto" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "scrollintoview".to_string(),
|
context: "scrollintoview".to_string(),
|
||||||
usage: "scrollintoview <selector>",
|
usage: "scrollintoview <selector>",
|
||||||
})?;
|
})?;
|
||||||
@@ -332,7 +332,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default: selector or timeout
|
// Default: selector or timeout
|
||||||
if let Some(arg) = rest.get(0) {
|
if let Some(arg) = rest.first() {
|
||||||
if arg.parse::<u64>().is_ok() {
|
if arg.parse::<u64>().is_ok() {
|
||||||
Ok(
|
Ok(
|
||||||
json!({ "id": id, "action": "wait", "timeout": arg.parse::<u64>().unwrap() }),
|
json!({ "id": id, "action": "wait", "timeout": arg.parse::<u64>().unwrap() }),
|
||||||
@@ -353,7 +353,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
// screenshot [selector] [path]
|
// screenshot [selector] [path]
|
||||||
// selector: @ref or CSS selector
|
// selector: @ref or CSS selector
|
||||||
// path: file path (contains / or . or ends with known extension)
|
// path: file path (contains / or . or ends with known extension)
|
||||||
let (selector, path) = match (rest.get(0), rest.get(1)) {
|
let (selector, path) = match (rest.first(), rest.get(1)) {
|
||||||
(Some(first), Some(second)) => {
|
(Some(first), Some(second)) => {
|
||||||
// Two args: first is selector, second is path
|
// Two args: first is selector, second is path
|
||||||
(Some(*first), Some(*second))
|
(Some(*first), Some(*second))
|
||||||
@@ -383,7 +383,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
"pdf" => {
|
"pdf" => {
|
||||||
let path = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let path = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "pdf".to_string(),
|
context: "pdf".to_string(),
|
||||||
usage: "pdf <path>",
|
usage: "pdf <path>",
|
||||||
})?;
|
})?;
|
||||||
@@ -442,17 +442,22 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
let script = if is_stdin {
|
let script = if is_stdin {
|
||||||
// Read script from stdin
|
// Read script from stdin
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
let lines: Vec<String> = stdin.lock().lines()
|
let lines: Vec<String> = stdin
|
||||||
|
.lock()
|
||||||
|
.lines()
|
||||||
.map(|l| l.unwrap_or_default())
|
.map(|l| l.unwrap_or_default())
|
||||||
.collect();
|
.collect();
|
||||||
lines.join("\n")
|
lines.join("\n")
|
||||||
} else {
|
} else {
|
||||||
let raw_script = script_parts.join(" ");
|
let raw_script = script_parts.join(" ");
|
||||||
if is_base64 {
|
if is_base64 {
|
||||||
let decoded = STANDARD.decode(&raw_script).map_err(|_| ParseError::InvalidValue {
|
let decoded =
|
||||||
message: "Invalid base64 encoding".to_string(),
|
STANDARD
|
||||||
usage: "eval -b <base64-encoded-script>",
|
.decode(&raw_script)
|
||||||
})?;
|
.map_err(|_| ParseError::InvalidValue {
|
||||||
|
message: "Invalid base64 encoding".to_string(),
|
||||||
|
usage: "eval -b <base64-encoded-script>",
|
||||||
|
})?;
|
||||||
String::from_utf8(decoded).map_err(|_| ParseError::InvalidValue {
|
String::from_utf8(decoded).map_err(|_| ParseError::InvalidValue {
|
||||||
message: "Base64 decoded to invalid UTF-8".to_string(),
|
message: "Base64 decoded to invalid UTF-8".to_string(),
|
||||||
usage: "eval -b <base64-encoded-script>",
|
usage: "eval -b <base64-encoded-script>",
|
||||||
@@ -483,7 +488,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
} else {
|
} else {
|
||||||
// It's a port number - validate and use cdpPort field
|
// It's a port number - validate and use cdpPort field
|
||||||
let port: u16 = match endpoint.parse::<u32>() {
|
let port: u16 = match endpoint.parse::<u32>() {
|
||||||
Ok(p) if p == 0 => {
|
Ok(0) => {
|
||||||
return Err(ParseError::InvalidValue {
|
return Err(ParseError::InvalidValue {
|
||||||
message: "Invalid port: port must be greater than 0".to_string(),
|
message: "Invalid port: port must be greater than 0".to_string(),
|
||||||
usage: "connect <port|url>",
|
usage: "connect <port|url>",
|
||||||
@@ -536,7 +541,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
|
|
||||||
// === Cookies ===
|
// === Cookies ===
|
||||||
"cookies" => {
|
"cookies" => {
|
||||||
let op = rest.get(0).unwrap_or(&"get");
|
let op = rest.first().unwrap_or(&"get");
|
||||||
match *op {
|
match *op {
|
||||||
"set" => {
|
"set" => {
|
||||||
let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
@@ -650,7 +655,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
}
|
}
|
||||||
|
|
||||||
// === Tabs ===
|
// === Tabs ===
|
||||||
"tab" => match rest.get(0).map(|s| *s) {
|
"tab" => match rest.first().copied() {
|
||||||
Some("new") => {
|
Some("new") => {
|
||||||
let mut cmd = json!({ "id": id, "action": "tab_new" });
|
let mut cmd = json!({ "id": id, "action": "tab_new" });
|
||||||
if let Some(url) = rest.get(1) {
|
if let Some(url) = rest.get(1) {
|
||||||
@@ -675,7 +680,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
// === Window ===
|
// === Window ===
|
||||||
"window" => {
|
"window" => {
|
||||||
const VALID: &[&str] = &["new"];
|
const VALID: &[&str] = &["new"];
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("new") => Ok(json!({ "id": id, "action": "window_new" })),
|
Some("new") => Ok(json!({ "id": id, "action": "window_new" })),
|
||||||
Some(sub) => Err(ParseError::UnknownSubcommand {
|
Some(sub) => Err(ParseError::UnknownSubcommand {
|
||||||
subcommand: sub.to_string(),
|
subcommand: sub.to_string(),
|
||||||
@@ -690,10 +695,10 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
|
|
||||||
// === Frame ===
|
// === Frame ===
|
||||||
"frame" => {
|
"frame" => {
|
||||||
if rest.get(0).map(|s| *s) == Some("main") {
|
if rest.first().copied() == Some("main") {
|
||||||
Ok(json!({ "id": id, "action": "mainframe" }))
|
Ok(json!({ "id": id, "action": "mainframe" }))
|
||||||
} else {
|
} else {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "frame".to_string(),
|
context: "frame".to_string(),
|
||||||
usage: "frame <selector|main>",
|
usage: "frame <selector|main>",
|
||||||
})?;
|
})?;
|
||||||
@@ -704,7 +709,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
// === Dialog ===
|
// === Dialog ===
|
||||||
"dialog" => {
|
"dialog" => {
|
||||||
const VALID: &[&str] = &["accept", "dismiss"];
|
const VALID: &[&str] = &["accept", "dismiss"];
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("accept") => {
|
Some("accept") => {
|
||||||
let mut cmd = json!({ "id": id, "action": "dialog", "response": "accept" });
|
let mut cmd = json!({ "id": id, "action": "dialog", "response": "accept" });
|
||||||
if let Some(prompt_text) = rest.get(1) {
|
if let Some(prompt_text) = rest.get(1) {
|
||||||
@@ -726,7 +731,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
// === Debug ===
|
// === Debug ===
|
||||||
"trace" => {
|
"trace" => {
|
||||||
const VALID: &[&str] = &["start", "stop"];
|
const VALID: &[&str] = &["start", "stop"];
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("start") => Ok(json!({ "id": id, "action": "trace_start" })),
|
Some("start") => Ok(json!({ "id": id, "action": "trace_start" })),
|
||||||
Some("stop") => {
|
Some("stop") => {
|
||||||
let path = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let path = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
@@ -749,7 +754,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
// === Recording (Playwright native video recording) ===
|
// === Recording (Playwright native video recording) ===
|
||||||
"record" => {
|
"record" => {
|
||||||
const VALID: &[&str] = &["start", "stop", "restart"];
|
const VALID: &[&str] = &["start", "stop", "restart"];
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("start") => {
|
Some("start") => {
|
||||||
let path = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let path = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "record start".to_string(),
|
context: "record start".to_string(),
|
||||||
@@ -800,15 +805,15 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"console" => {
|
"console" => {
|
||||||
let clear = rest.iter().any(|&s| s == "--clear");
|
let clear = rest.contains(&"--clear");
|
||||||
Ok(json!({ "id": id, "action": "console", "clear": clear }))
|
Ok(json!({ "id": id, "action": "console", "clear": clear }))
|
||||||
}
|
}
|
||||||
"errors" => {
|
"errors" => {
|
||||||
let clear = rest.iter().any(|&s| s == "--clear");
|
let clear = rest.contains(&"--clear");
|
||||||
Ok(json!({ "id": id, "action": "errors", "clear": clear }))
|
Ok(json!({ "id": id, "action": "errors", "clear": clear }))
|
||||||
}
|
}
|
||||||
"highlight" => {
|
"highlight" => {
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "highlight".to_string(),
|
context: "highlight".to_string(),
|
||||||
usage: "highlight <selector>",
|
usage: "highlight <selector>",
|
||||||
})?;
|
})?;
|
||||||
@@ -818,7 +823,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
// === State ===
|
// === State ===
|
||||||
"state" => {
|
"state" => {
|
||||||
const VALID: &[&str] = &["save", "load"];
|
const VALID: &[&str] = &["save", "load"];
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("save") => {
|
Some("save") => {
|
||||||
let path = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let path = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "state save".to_string(),
|
context: "state save".to_string(),
|
||||||
@@ -847,14 +852,14 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
// === iOS-specific commands ===
|
// === iOS-specific commands ===
|
||||||
"tap" => {
|
"tap" => {
|
||||||
// Alias for click (semantic clarity for touch interfaces)
|
// Alias for click (semantic clarity for touch interfaces)
|
||||||
let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "tap".to_string(),
|
context: "tap".to_string(),
|
||||||
usage: "tap <selector>",
|
usage: "tap <selector>",
|
||||||
})?;
|
})?;
|
||||||
Ok(json!({ "id": id, "action": "tap", "selector": sel }))
|
Ok(json!({ "id": id, "action": "tap", "selector": sel }))
|
||||||
}
|
}
|
||||||
"swipe" => {
|
"swipe" => {
|
||||||
let direction = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let direction = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "swipe".to_string(),
|
context: "swipe".to_string(),
|
||||||
usage: "swipe <up|down|left|right> [distance]",
|
usage: "swipe <up|down|left|right> [distance]",
|
||||||
})?;
|
})?;
|
||||||
@@ -868,13 +873,15 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
|||||||
let mut cmd = json!({ "id": id, "action": "swipe", "direction": direction });
|
let mut cmd = json!({ "id": id, "action": "swipe", "direction": direction });
|
||||||
if let Some(distance) = rest.get(1) {
|
if let Some(distance) = rest.get(1) {
|
||||||
if let Ok(d) = distance.parse::<u32>() {
|
if let Ok(d) = distance.parse::<u32>() {
|
||||||
cmd.as_object_mut().unwrap().insert("distance".to_string(), json!(d));
|
cmd.as_object_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert("distance".to_string(), json!(d));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
"device" => {
|
"device" => {
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("list") | None => {
|
Some("list") | None => {
|
||||||
// List available iOS simulators
|
// List available iOS simulators
|
||||||
Ok(json!({ "id": id, "action": "device_list" }))
|
Ok(json!({ "id": id, "action": "device_list" }))
|
||||||
@@ -897,7 +904,7 @@ fn parse_get(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
"text", "html", "value", "attr", "url", "title", "count", "box", "styles",
|
"text", "html", "value", "attr", "url", "title", "count", "box", "styles",
|
||||||
];
|
];
|
||||||
|
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("text") => {
|
Some("text") => {
|
||||||
let sel = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "get text".to_string(),
|
context: "get text".to_string(),
|
||||||
@@ -967,7 +974,7 @@ fn parse_get(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
fn parse_is(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
fn parse_is(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
||||||
const VALID: &[&str] = &["visible", "enabled", "checked"];
|
const VALID: &[&str] = &["visible", "enabled", "checked"];
|
||||||
|
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("visible") => {
|
Some("visible") => {
|
||||||
let sel = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let sel = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "is visible".to_string(),
|
context: "is visible".to_string(),
|
||||||
@@ -1014,14 +1021,14 @@ fn parse_find(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
"nth",
|
"nth",
|
||||||
];
|
];
|
||||||
|
|
||||||
let locator = rest.get(0).ok_or_else(|| ParseError::MissingArguments {
|
let locator = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "find".to_string(),
|
context: "find".to_string(),
|
||||||
usage: "find <locator> <value> [action] [text]",
|
usage: "find <locator> <value> [action] [text]",
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let name_idx = rest.iter().position(|&s| s == "--name");
|
let name_idx = rest.iter().position(|&s| s == "--name");
|
||||||
let name = name_idx.and_then(|i| rest.get(i + 1).map(|s| *s));
|
let name = name_idx.and_then(|i| rest.get(i + 1).copied());
|
||||||
let exact = rest.iter().any(|&s| s == "--exact");
|
let exact = rest.contains(&"--exact");
|
||||||
|
|
||||||
match *locator {
|
match *locator {
|
||||||
"role" | "text" | "label" | "placeholder" | "alt" | "title" | "testid" | "first"
|
"role" | "text" | "label" | "placeholder" | "alt" | "title" | "testid" | "first"
|
||||||
@@ -1140,7 +1147,7 @@ fn parse_find(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
fn parse_mouse(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
fn parse_mouse(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
||||||
const VALID: &[&str] = &["move", "down", "up", "wheel"];
|
const VALID: &[&str] = &["move", "down", "up", "wheel"];
|
||||||
|
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("move") => {
|
Some("move") => {
|
||||||
let x_str = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let x_str = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "mouse move".to_string(),
|
context: "mouse move".to_string(),
|
||||||
@@ -1202,7 +1209,7 @@ fn parse_set(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
"media",
|
"media",
|
||||||
];
|
];
|
||||||
|
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("viewport") => {
|
Some("viewport") => {
|
||||||
let w_str = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let w_str = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "set viewport".to_string(),
|
context: "set viewport".to_string(),
|
||||||
@@ -1288,14 +1295,14 @@ fn parse_set(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
Ok(json!({ "id": id, "action": "credentials", "username": user, "password": pass }))
|
Ok(json!({ "id": id, "action": "credentials", "username": user, "password": pass }))
|
||||||
}
|
}
|
||||||
Some("media") => {
|
Some("media") => {
|
||||||
let color = if rest.iter().any(|&s| s == "dark") {
|
let color = if rest.contains(&"dark") {
|
||||||
"dark"
|
"dark"
|
||||||
} else if rest.iter().any(|&s| s == "light") {
|
} else if rest.contains(&"light") {
|
||||||
"light"
|
"light"
|
||||||
} else {
|
} else {
|
||||||
"no-preference"
|
"no-preference"
|
||||||
};
|
};
|
||||||
let reduced = if rest.iter().any(|&s| s == "reduced-motion") {
|
let reduced = if rest.contains(&"reduced-motion") {
|
||||||
"reduce"
|
"reduce"
|
||||||
} else {
|
} else {
|
||||||
"no-preference"
|
"no-preference"
|
||||||
@@ -1318,15 +1325,15 @@ fn parse_set(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
fn parse_network(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
fn parse_network(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
||||||
const VALID: &[&str] = &["route", "unroute", "requests"];
|
const VALID: &[&str] = &["route", "unroute", "requests"];
|
||||||
|
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("route") => {
|
Some("route") => {
|
||||||
let url = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
let url = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
|
||||||
context: "network route".to_string(),
|
context: "network route".to_string(),
|
||||||
usage: "network route <url> [--abort|--body <json>]",
|
usage: "network route <url> [--abort|--body <json>]",
|
||||||
})?;
|
})?;
|
||||||
let abort = rest.iter().any(|&s| s == "--abort");
|
let abort = rest.contains(&"--abort");
|
||||||
let body_idx = rest.iter().position(|&s| s == "--body");
|
let body_idx = rest.iter().position(|&s| s == "--body");
|
||||||
let body = body_idx.and_then(|i| rest.get(i + 1).map(|s| *s));
|
let body = body_idx.and_then(|i| rest.get(i + 1).copied());
|
||||||
Ok(json!({ "id": id, "action": "route", "url": url, "abort": abort, "body": body }))
|
Ok(json!({ "id": id, "action": "route", "url": url, "abort": abort, "body": body }))
|
||||||
}
|
}
|
||||||
Some("unroute") => {
|
Some("unroute") => {
|
||||||
@@ -1337,9 +1344,9 @@ fn parse_network(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
Ok(cmd)
|
Ok(cmd)
|
||||||
}
|
}
|
||||||
Some("requests") => {
|
Some("requests") => {
|
||||||
let clear = rest.iter().any(|&s| s == "--clear");
|
let clear = rest.contains(&"--clear");
|
||||||
let filter_idx = rest.iter().position(|&s| s == "--filter");
|
let filter_idx = rest.iter().position(|&s| s == "--filter");
|
||||||
let filter = filter_idx.and_then(|i| rest.get(i + 1).map(|s| *s));
|
let filter = filter_idx.and_then(|i| rest.get(i + 1).copied());
|
||||||
let mut cmd = json!({ "id": id, "action": "requests", "clear": clear });
|
let mut cmd = json!({ "id": id, "action": "requests", "clear": clear });
|
||||||
if let Some(f) = filter {
|
if let Some(f) = filter {
|
||||||
cmd["filter"] = json!(f);
|
cmd["filter"] = json!(f);
|
||||||
@@ -1360,9 +1367,9 @@ fn parse_network(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
|||||||
fn parse_storage(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
fn parse_storage(rest: &[&str], id: &str) -> Result<Value, ParseError> {
|
||||||
const VALID: &[&str] = &["local", "session"];
|
const VALID: &[&str] = &["local", "session"];
|
||||||
|
|
||||||
match rest.get(0).map(|s| *s) {
|
match rest.first().copied() {
|
||||||
Some("local") | Some("session") => {
|
Some("local") | Some("session") => {
|
||||||
let storage_type = rest.get(0).unwrap();
|
let storage_type = rest.first().unwrap();
|
||||||
let op = rest.get(1).unwrap_or(&"get");
|
let op = rest.get(1).unwrap_or(&"get");
|
||||||
let key = rest.get(2);
|
let key = rest.get(2);
|
||||||
let value = rest.get(3);
|
let value = rest.get(3);
|
||||||
@@ -2159,8 +2166,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_eval_base64_long_flag() {
|
fn test_eval_base64_long_flag() {
|
||||||
// "document.title" in base64
|
// "document.title" in base64
|
||||||
let cmd =
|
let cmd = parse_command(
|
||||||
parse_command(&args("eval --base64 ZG9jdW1lbnQudGl0bGU="), &default_flags()).unwrap();
|
&args("eval --base64 ZG9jdW1lbnQudGl0bGU="),
|
||||||
|
&default_flags(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(cmd["action"], "evaluate");
|
assert_eq!(cmd["action"], "evaluate");
|
||||||
assert_eq!(cmd["script"], "document.title");
|
assert_eq!(cmd["script"], "document.title");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ pub struct DaemonResult {
|
|||||||
pub already_running: bool,
|
pub already_running: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn ensure_daemon(
|
pub fn ensure_daemon(
|
||||||
session: &str,
|
session: &str,
|
||||||
headed: bool,
|
headed: bool,
|
||||||
|
|||||||
+7
-14
@@ -10,9 +10,6 @@ use std::env;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
use libc;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use windows_sys::Win32::Foundation::CloseHandle;
|
use windows_sys::Win32::Foundation::CloseHandle;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@@ -141,7 +138,7 @@ fn main() {
|
|||||||
let has_version = args.iter().any(|a| a == "--version" || a == "-V");
|
let has_version = args.iter().any(|a| a == "--version" || a == "-V");
|
||||||
|
|
||||||
if has_help {
|
if has_help {
|
||||||
if let Some(cmd) = clean.get(0) {
|
if let Some(cmd) = clean.first() {
|
||||||
if print_command_help(cmd) {
|
if print_command_help(cmd) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -161,14 +158,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle install separately
|
// Handle install separately
|
||||||
if clean.get(0).map(|s| s.as_str()) == Some("install") {
|
if clean.first().map(|s| s.as_str()) == Some("install") {
|
||||||
let with_deps = args.iter().any(|a| a == "--with-deps" || a == "-d");
|
let with_deps = args.iter().any(|a| a == "--with-deps" || a == "-d");
|
||||||
run_install(with_deps);
|
run_install(with_deps);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle session separately (doesn't need daemon)
|
// Handle session separately (doesn't need daemon)
|
||||||
if clean.get(0).map(|s| s.as_str()) == Some("session") {
|
if clean.first().map(|s| s.as_str()) == Some("session") {
|
||||||
run_session(&clean, &flags.session, flags.json);
|
run_session(&clean, &flags.session, flags.json);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -247,11 +244,7 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
if flags.cli_args {
|
if flags.cli_args { Some("--args") } else { None },
|
||||||
Some("--args")
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
if flags.cli_user_agent {
|
if flags.cli_user_agent {
|
||||||
Some("--user-agent")
|
Some("--user-agent")
|
||||||
} else {
|
} else {
|
||||||
@@ -267,8 +260,8 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
flags.ignore_https_errors.then(|| "--ignore-https-errors"),
|
flags.ignore_https_errors.then_some("--ignore-https-errors"),
|
||||||
flags.cli_allow_file_access.then(|| "--allow-file-access"),
|
flags.cli_allow_file_access.then_some("--allow-file-access"),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@@ -372,7 +365,7 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
// It's a port number - validate and use cdpPort field
|
// It's a port number - validate and use cdpPort field
|
||||||
let cdp_port: u16 = match cdp_value.parse::<u32>() {
|
let cdp_port: u16 = match cdp_value.parse::<u32>() {
|
||||||
Ok(p) if p == 0 => {
|
Ok(0) => {
|
||||||
let msg = "Invalid CDP port: port must be greater than 0".to_string();
|
let msg = "Invalid CDP port: port must be greater than 0".to_string();
|
||||||
if flags.json {
|
if flags.json {
|
||||||
println!(r#"{{"success":false,"error":"{}"}}"#, msg);
|
println!(r#"{{"success":false,"error":"{}"}}"#, msg);
|
||||||
|
|||||||
+22
-5
@@ -88,17 +88,28 @@ pub fn print_response(resp: &Response, json_mode: bool, action: Option<&str>) {
|
|||||||
// Separate real devices from simulators
|
// Separate real devices from simulators
|
||||||
let real_devices: Vec<_> = devices
|
let real_devices: Vec<_> = devices
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|d| d.get("isRealDevice").and_then(|v| v.as_bool()).unwrap_or(false))
|
.filter(|d| {
|
||||||
|
d.get("isRealDevice")
|
||||||
|
.and_then(|v| v.as_bool())
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let simulators: Vec<_> = devices
|
let simulators: Vec<_> = devices
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|d| !d.get("isRealDevice").and_then(|v| v.as_bool()).unwrap_or(false))
|
.filter(|d| {
|
||||||
|
!d.get("isRealDevice")
|
||||||
|
.and_then(|v| v.as_bool())
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !real_devices.is_empty() {
|
if !real_devices.is_empty() {
|
||||||
println!("Connected Devices:\n");
|
println!("Connected Devices:\n");
|
||||||
for device in real_devices.iter() {
|
for device in real_devices.iter() {
|
||||||
let name = device.get("name").and_then(|v| v.as_str()).unwrap_or("Unknown");
|
let name = device
|
||||||
|
.get("name")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("Unknown");
|
||||||
let runtime = device.get("runtime").and_then(|v| v.as_str()).unwrap_or("");
|
let runtime = device.get("runtime").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let udid = device.get("udid").and_then(|v| v.as_str()).unwrap_or("");
|
let udid = device.get("udid").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
println!(" {} {} ({})", color::green("●"), name, runtime);
|
println!(" {} {} ({})", color::green("●"), name, runtime);
|
||||||
@@ -110,9 +121,15 @@ pub fn print_response(resp: &Response, json_mode: bool, action: Option<&str>) {
|
|||||||
if !simulators.is_empty() {
|
if !simulators.is_empty() {
|
||||||
println!("Simulators:\n");
|
println!("Simulators:\n");
|
||||||
for device in simulators.iter() {
|
for device in simulators.iter() {
|
||||||
let name = device.get("name").and_then(|v| v.as_str()).unwrap_or("Unknown");
|
let name = device
|
||||||
|
.get("name")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("Unknown");
|
||||||
let runtime = device.get("runtime").and_then(|v| v.as_str()).unwrap_or("");
|
let runtime = device.get("runtime").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let state = device.get("state").and_then(|v| v.as_str()).unwrap_or("Unknown");
|
let state = device
|
||||||
|
.get("state")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("Unknown");
|
||||||
let udid = device.get("udid").and_then(|v| v.as_str()).unwrap_or("");
|
let udid = device.get("udid").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let state_indicator = if state == "Booted" {
|
let state_indicator = if state == "Booted" {
|
||||||
color::green("●")
|
color::green("●")
|
||||||
|
|||||||
Reference in New Issue
Block a user