Skip to main content

sloop/sources/
markdown.rs

1use std::fs;
2use std::io;
3use std::path::{Path, PathBuf};
4
5use crate::flow::Flow;
6
7use super::{FlowSource, SourceError};
8
9pub struct MarkdownFlowSource {
10    root: PathBuf,
11}
12
13impl MarkdownFlowSource {
14    pub fn new(root: impl Into<PathBuf>) -> Self {
15        Self { root: root.into() }
16    }
17}
18
19impl FlowSource for MarkdownFlowSource {
20    fn pull(&self) -> Result<Vec<Flow>, SourceError> {
21        let directory = self.root.join(".agents/sloop/flows");
22        let entries = match fs::read_dir(&directory) {
23            Ok(entries) => entries,
24            Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(Vec::new()),
25            Err(error) => return Err(io_error(&directory, error)),
26        };
27        let mut paths = entries
28            .map(|entry| {
29                entry
30                    .map(|entry| entry.path())
31                    .map_err(|error| io_error(&directory, error))
32            })
33            .collect::<Result<Vec<_>, _>>()?;
34        paths.retain(|path| path.is_file() && path.extension().is_some_and(|ext| ext == "yaml"));
35        paths.sort();
36
37        paths
38            .into_iter()
39            .map(|path| {
40                let name = path
41                    .file_stem()
42                    .and_then(|stem| stem.to_str())
43                    .ok_or_else(|| {
44                        SourceError::new(format!(
45                            "{}: flow filename must be valid UTF-8",
46                            path.display()
47                        ))
48                    })?;
49                let contents = fs::read_to_string(&path).map_err(|error| io_error(&path, error))?;
50                crate::flow::parse(name, &contents)
51                    .map_err(|message| SourceError::new(format!("{}: {message}", path.display())))
52            })
53            .collect()
54    }
55}
56
57fn io_error(path: &Path, error: io::Error) -> SourceError {
58    SourceError::new(format!("{}: {error}", path.display()))
59}