askit_std_agents/
file.rs

1use std::fs;
2use std::path::Path;
3
4use agent_stream_kit::{
5    ASKit, AgentContext, AgentData, AgentError, AgentOutput, AgentSpec, AgentValue, AsAgent,
6    askit_agent, async_trait,
7};
8
9static CATEGORY: &str = "Std/File";
10
11static PIN_PATH: &str = "path";
12static PIN_FILES: &str = "files";
13static PIN_TEXT: &str = "text";
14static PIN_DATA: &str = "data";
15
16// List Files Agent
17#[askit_agent(
18    title = "List Files",
19    category = CATEGORY,
20    inputs = [PIN_PATH],
21    outputs = [PIN_FILES]
22)]
23struct ListFilesAgent {
24    data: AgentData,
25}
26
27#[async_trait]
28impl AsAgent for ListFilesAgent {
29    fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
30        Ok(Self {
31            data: AgentData::new(askit, id, spec),
32        })
33    }
34
35    async fn process(
36        &mut self,
37        ctx: AgentContext,
38        _pin: String,
39        value: AgentValue,
40    ) -> Result<(), AgentError> {
41        let path = value
42            .as_str()
43            .ok_or_else(|| AgentError::InvalidValue("path is not a string".to_string()))?;
44        let path = Path::new(path);
45
46        if !path.exists() {
47            return Err(AgentError::InvalidValue(format!(
48                "Path does not exist: {}",
49                path.display()
50            )));
51        }
52
53        if !path.is_dir() {
54            return Err(AgentError::InvalidValue(format!(
55                "Path is not a directory: {}",
56                path.display()
57            )));
58        }
59
60        let mut files = Vec::new();
61        let entries = fs::read_dir(path).map_err(|e| {
62            AgentError::InvalidValue(format!(
63                "Failed to read directory {}: {}",
64                path.display(),
65                e
66            ))
67        })?;
68
69        for entry in entries {
70            let entry = entry.map_err(|e| {
71                AgentError::InvalidValue(format!("Failed to read directory entry: {}", e))
72            })?;
73            let file_name = entry.file_name().to_string_lossy().to_string();
74            files.push(file_name.into());
75        }
76
77        let out_value = AgentValue::array(files);
78        self.try_output(ctx, PIN_FILES, out_value)
79    }
80}
81
82// Read Text File Agent
83#[askit_agent(
84    title = "Read Text File",
85    category = CATEGORY,
86    inputs = [PIN_PATH],
87    outputs = [PIN_TEXT]
88)]
89struct ReadTextFileAgent {
90    data: AgentData,
91}
92
93#[async_trait]
94impl AsAgent for ReadTextFileAgent {
95    fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
96        Ok(Self {
97            data: AgentData::new(askit, id, spec),
98        })
99    }
100
101    async fn process(
102        &mut self,
103        ctx: AgentContext,
104        _pin: String,
105        value: AgentValue,
106    ) -> Result<(), AgentError> {
107        let path = value
108            .as_str()
109            .ok_or_else(|| AgentError::InvalidValue("path is not a string".into()))?;
110        let path = Path::new(path);
111
112        if !path.exists() {
113            return Err(AgentError::InvalidValue(format!(
114                "Path does not exist: {}",
115                path.display()
116            )));
117        }
118
119        if !path.is_file() {
120            return Err(AgentError::InvalidValue(format!(
121                "Path is not a file: {}",
122                path.display()
123            )));
124        }
125
126        let content = fs::read_to_string(path).map_err(|e| {
127            AgentError::InvalidValue(format!("Failed to read file {}: {}", path.display(), e))
128        })?;
129        let out_value = AgentValue::string(content);
130        self.try_output(ctx, PIN_TEXT, out_value)
131    }
132}
133
134// Write Text File Agent
135#[askit_agent(
136    title = "Write Text File",
137    category = CATEGORY,
138    inputs = [PIN_DATA],
139    outputs = [PIN_DATA]
140)]
141struct WriteTextFileAgent {
142    data: AgentData,
143}
144
145#[async_trait]
146impl AsAgent for WriteTextFileAgent {
147    fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
148        Ok(Self {
149            data: AgentData::new(askit, id, spec),
150        })
151    }
152
153    async fn process(
154        &mut self,
155        ctx: AgentContext,
156        _pin: String,
157        value: AgentValue,
158    ) -> Result<(), AgentError> {
159        let input = value
160            .as_object()
161            .ok_or_else(|| AgentError::InvalidValue("Input is not an object".into()))?;
162
163        let path = input
164            .get("path")
165            .ok_or_else(|| AgentError::InvalidValue("Missing 'path' in input".into()))?
166            .as_str()
167            .ok_or_else(|| AgentError::InvalidValue("'path' is not a string".into()))?;
168
169        let text = input
170            .get("text")
171            .ok_or_else(|| AgentError::InvalidValue("Missing 'text' in input".into()))?
172            .as_str()
173            .ok_or_else(|| AgentError::InvalidValue("'text' is not a string".into()))?;
174
175        let path = Path::new(path);
176
177        // Ensure parent directories exist
178        if let Some(parent) = path.parent() {
179            if !parent.exists() {
180                fs::create_dir_all(parent).map_err(|e| {
181                    AgentError::InvalidValue(format!("Failed to create parent directories: {}", e))
182                })?
183            }
184        }
185
186        fs::write(path, text).map_err(|e| {
187            AgentError::InvalidValue(format!("Failed to write file {}: {}", path.display(), e))
188        })?;
189
190        self.try_output(ctx, PIN_DATA, value)
191    }
192}