1use crate::CoreError;
4use camino::{Utf8Path, Utf8PathBuf};
5
6#[non_exhaustive]
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum RecipeSource {
13 Path(Utf8PathBuf),
15 Content {
17 text: String,
19 name: String,
21 },
22}
23
24#[non_exhaustive]
26#[derive(Debug, Clone, PartialEq, Eq, Default)]
27pub enum ConfigSource {
28 Path(Utf8PathBuf),
30 Inline(String),
32 #[default]
34 None,
35}
36
37impl ConfigSource {
38 pub fn read(&self) -> Result<Option<String>, CoreError> {
46 match self {
47 ConfigSource::None => Ok(None),
48 ConfigSource::Inline(text) => Ok(Some(text.clone())),
49 ConfigSource::Path(path) => match std::fs::read_to_string(path) {
50 Ok(text) => Ok(Some(text)),
51 Err(source) => Err(CoreError::Io {
52 path: path.clone(),
53 source,
54 }),
55 },
56 }
57 }
58
59 pub fn is_unset(&self) -> bool {
61 matches!(self, ConfigSource::None)
62 }
63
64 pub fn path(&self) -> Option<&Utf8Path> {
66 match self {
67 ConfigSource::Path(p) => Some(p),
68 _ => None,
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn none_reads_as_none() {
79 assert!(ConfigSource::None.read().unwrap().is_none());
80 assert!(ConfigSource::None.is_unset());
81 }
82
83 #[test]
84 fn inline_reads_its_text() {
85 let source = ConfigSource::Inline("[produce]\ntomato".to_string());
86 assert_eq!(source.read().unwrap().as_deref(), Some("[produce]\ntomato"));
87 assert!(!source.is_unset());
88 assert_eq!(source.path(), None);
89 }
90
91 #[test]
92 fn path_reads_from_disk() {
93 let dir = tempfile::TempDir::new().unwrap();
94 let path = dir.path().join("aisle.conf");
95 std::fs::write(&path, "[dairy]\nmilk").unwrap();
96 let utf8 = camino::Utf8PathBuf::from_path_buf(path).unwrap();
97
98 let source = ConfigSource::Path(utf8.clone());
99 assert_eq!(source.read().unwrap().as_deref(), Some("[dairy]\nmilk"));
100 assert_eq!(source.path(), Some(utf8.as_path()));
101 }
102
103 #[test]
104 fn missing_path_is_an_io_error_naming_the_path() {
105 let missing = camino::Utf8PathBuf::from("/nonexistent/aisle.conf");
106 let source = ConfigSource::Path(missing.clone());
107
108 match source.read() {
109 Err(CoreError::Io { path, source }) => {
110 assert_eq!(path, missing);
111 assert_eq!(source.kind(), std::io::ErrorKind::NotFound);
112 }
113 other => panic!("expected CoreError::Io, got {other:?}"),
114 }
115 }
116}