Skip to main content

systemprompt_loader/
include_resolver.rs

1use anyhow::{Context, Result};
2use serde::Deserialize;
3use std::fs;
4use std::path::{Path, PathBuf};
5
6#[derive(Debug, Clone)]
7pub struct IncludeResolver {
8    base_path: PathBuf,
9}
10
11impl IncludeResolver {
12    pub const fn new(base_path: PathBuf) -> Self {
13        Self { base_path }
14    }
15
16    pub fn resolve_string(&self, value: &str) -> Result<String> {
17        value.strip_prefix("!include ").map_or_else(
18            || Ok(value.to_string()),
19            |include_path| {
20                let full_path = self.base_path.join(include_path.trim());
21                fs::read_to_string(&full_path)
22                    .with_context(|| format!("Failed to read include: {}", full_path.display()))
23            },
24        )
25    }
26
27    pub fn resolve_yaml_file<T: for<'de> Deserialize<'de>>(&self, path: &str) -> Result<T> {
28        let full_path = self.base_path.join(path);
29        let content = fs::read_to_string(&full_path)
30            .with_context(|| format!("Failed to read YAML: {}", full_path.display()))?;
31
32        serde_yaml::from_str(&content)
33            .with_context(|| format!("Failed to parse YAML: {}", full_path.display()))
34    }
35
36    pub fn with_subpath(&self, subpath: &str) -> Self {
37        Self {
38            base_path: self.base_path.join(subpath),
39        }
40    }
41
42    pub fn base_path(&self) -> &Path {
43        &self.base_path
44    }
45
46    pub fn exists(&self, path: &str) -> bool {
47        self.base_path.join(path).exists()
48    }
49
50    pub fn read_file(&self, path: &str) -> Result<String> {
51        let full_path = self.base_path.join(path);
52        fs::read_to_string(&full_path)
53            .with_context(|| format!("Failed to read file: {}", full_path.display()))
54    }
55}