matrixcode_core/tools/
ls.rs1use anyhow::Result;
2use async_trait::async_trait;
3use serde_json::{Value, json};
4
5use super::{Tool, ToolDefinition};
6
7pub struct LsTool;
8
9#[async_trait]
10impl Tool for LsTool {
11 fn definition(&self) -> ToolDefinition {
12 ToolDefinition {
13 name: "ls".to_string(),
14 description: "列出目录的直接条目。每行一个条目,目录在前(以 / 结尾),\
15 文件在后并显示字节大小。不递归——递归查找请使用 'glob'。"
16 .to_string(),
17 parameters: json!({
18 "type": "object",
19 "properties": {
20 "path": {
21 "type": "string",
22 "description": "目录路径(默认 '.')"
23 }
24 },
25 "required": []
26 }),
27 }
28 }
29
30 async fn execute(&self, params: Value) -> Result<String> {
31 let path = params["path"].as_str().unwrap_or(".").to_string();
32
33 let mut dirs: Vec<String> = Vec::new();
37 let mut files: Vec<(String, u64)> = Vec::new();
38
39 let mut rd = tokio::fs::read_dir(&path).await?;
40 while let Some(entry) = rd.next_entry().await? {
41 let name = entry.file_name().to_string_lossy().into_owned();
42 let ft = entry.file_type().await?;
43 if ft.is_dir() {
44 dirs.push(format!("{}/", name));
45 } else {
46 let size = entry.metadata().await.map(|m| m.len()).unwrap_or(0);
47 files.push((name, size));
48 }
49 }
50
51 dirs.sort();
52 files.sort_by(|a, b| a.0.cmp(&b.0));
53
54 if dirs.is_empty() && files.is_empty() {
55 return Ok(format!("(empty) {}", path));
57 }
58
59 let mut out = String::new();
60 for d in dirs {
61 out.push_str(&d);
62 out.push('\n');
63 }
64 for (n, s) in files {
65 out.push_str(&format!("{} ({} B)\n", n, s));
66 }
67 while out.ends_with('\n') {
68 out.pop();
69 }
70
71 Ok(out)
73 }
74}