browser_use/tools/
select.rs1use crate::error::{BrowserError, Result};
2use crate::tools::{Tool, ToolContext, ToolResult};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
8pub struct SelectParams {
9 #[serde(skip_serializing_if = "Option::is_none")]
11 pub selector: Option<String>,
12
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub index: Option<usize>,
16
17 pub value: String,
19}
20
21#[derive(Default)]
23pub struct SelectTool;
24
25const SELECT_JS: &str = include_str!("select.js");
26
27impl Tool for SelectTool {
28 type Params = SelectParams;
29
30 fn name(&self) -> &str {
31 "select"
32 }
33
34 fn execute_typed(&self, params: SelectParams, context: &mut ToolContext) -> Result<ToolResult> {
35 match (¶ms.selector, ¶ms.index) {
37 (Some(_), Some(_)) => {
38 return Err(BrowserError::ToolExecutionFailed {
39 tool: "select".to_string(),
40 reason: "Cannot specify both 'selector' and 'index'. Use one or the other."
41 .to_string(),
42 });
43 }
44 (None, None) => {
45 return Err(BrowserError::ToolExecutionFailed {
46 tool: "select".to_string(),
47 reason: "Must specify either 'selector' or 'index'.".to_string(),
48 });
49 }
50 _ => {}
51 }
52
53 let css_selector = if let Some(selector) = params.selector {
54 selector
55 } else if let Some(index) = params.index {
56 let dom = context.get_dom()?;
57 let selector = dom.get_selector(index).ok_or_else(|| {
58 BrowserError::ElementNotFound(format!("No element with index {}", index))
59 })?;
60 selector.clone()
61 } else {
62 unreachable!("Validation above ensures one field is Some")
63 };
64 let value = params.value;
65
66 let select_config = serde_json::json!({
67 "selector": css_selector,
68 "value": value,
69 });
70 let select_js = SELECT_JS.replace("__SELECT_CONFIG__", &select_config.to_string());
71
72 let result = context
73 .session
74 .tab()?
75 .evaluate(&select_js, false)
76 .map_err(|e| BrowserError::ToolExecutionFailed {
77 tool: "select".to_string(),
78 reason: e.to_string(),
79 })?;
80
81 let result_json: serde_json::Value = if let Some(serde_json::Value::String(json_str)) =
83 result.value
84 {
85 serde_json::from_str(&json_str)
86 .unwrap_or(serde_json::json!({"success": false, "error": "Failed to parse result"}))
87 } else {
88 result
89 .value
90 .unwrap_or(serde_json::json!({"success": false, "error": "No result returned"}))
91 };
92
93 if result_json["success"].as_bool() == Some(true) {
94 Ok(ToolResult::success_with(serde_json::json!({
95 "selector": css_selector,
96 "value": value,
97 "selectedText": result_json["selectedText"]
98 })))
99 } else {
100 Err(BrowserError::ToolExecutionFailed {
101 tool: "select".to_string(),
102 reason: result_json["error"]
103 .as_str()
104 .unwrap_or("Unknown error")
105 .to_string(),
106 })
107 }
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_select_params_css() {
117 let json = serde_json::json!({
118 "selector": "#country-select",
119 "value": "us"
120 });
121
122 let params: SelectParams = serde_json::from_value(json).unwrap();
123 assert_eq!(params.selector, Some("#country-select".to_string()));
124 assert_eq!(params.index, None);
125 assert_eq!(params.value, "us");
126 }
127
128 #[test]
129 fn test_select_params_index() {
130 let json = serde_json::json!({
131 "index": 5,
132 "value": "option2"
133 });
134
135 let params: SelectParams = serde_json::from_value(json).unwrap();
136 assert_eq!(params.selector, None);
137 assert_eq!(params.index, Some(5));
138 assert_eq!(params.value, "option2");
139 }
140}