artifactize_core/config/
site.rs1use super::discovery::{FileTargets, Found, Reach, declaration, declare, discovery_ignore, walk};
5use super::*;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Site {
10 pub kind: ArtifactKind,
11 pub path: PathBuf,
12}
13
14impl Site {
15 pub fn folder(&self) -> &Path {
17 folder_of(self.kind, &self.path)
18 }
19
20 pub fn declaration_path(&self) -> PathBuf {
22 declaration_of(self.kind, &self.path)
23 }
24}
25
26pub(super) fn folder_of(kind: ArtifactKind, path: &Path) -> &Path {
27 match kind {
28 ArtifactKind::Folder | ArtifactKind::Graph => path,
29 ArtifactKind::File => path.parent().unwrap_or(Path::new("")),
30 }
31}
32
33pub(super) fn declaration_of(kind: ArtifactKind, path: &Path) -> PathBuf {
34 match kind {
35 ArtifactKind::Graph => PathBuf::from(".artf"),
36 ArtifactKind::Folder => logical_join(path, std::ffi::OsStr::new(CONFIG_FILE)),
37 ArtifactKind::File => {
38 let mut path = path.as_os_str().to_owned();
39 path.push(".artf");
40 PathBuf::from(path)
41 }
42 }
43}
44
45pub fn read_sites(repo: &Path) -> Result<BTreeMap<ArtifactName, Site>, ConfigError> {
48 let root =
49 crate::platform::canonicalize(repo).map_err(|error| ConfigError::new(repo, error))?;
50 let ignored = discovery_ignore(&root)?;
51 let mut sites = BTreeMap::new();
52 walk(
53 &root,
54 &ignored,
55 Reach::Workspace,
56 FileTargets::DeclarationsOnly,
57 &mut |found: Found<'_>| {
58 let name = declared_name(&found.source)
59 .map_err(|error| ConfigError::new(found.file, error))?;
60 let site = Site {
61 kind: found.kind,
62 path: found.path,
63 };
64 if sites.insert(name.clone(), site).is_some() {
65 return Err(ConfigError::declaration(
66 found.file,
67 &["name"],
68 format!("Duplicate Artifact name: {name}."),
69 ));
70 }
71 Ok(name)
72 },
73 )?;
74 Ok(sites)
75}
76
77fn declared_name(source: &str) -> Result<ArtifactName, String> {
79 let table = toml::de::DeTable::parse(source).map_err(|error| error.to_string())?;
80 match table.get_ref().get("name").map(|name| name.get_ref()) {
81 Some(toml::de::DeValue::String(name)) => name.parse(),
82 Some(_) => Err("name must be a string.".into()),
83 None => Err("missing field `name`".into()),
84 }
85}
86
87#[derive(Debug, Error)]
89pub enum ScopeReadError {
90 #[error("{name} is no longer declared at {}.", crate::platform::path_text(.path))]
92 Moved { name: ArtifactName, path: PathBuf },
93 #[error(transparent)]
94 Config(#[from] ConfigError),
95}
96
97pub(crate) fn read_artifact_scope(
102 repo: &Path,
103 name: &ArtifactName,
104 site: &Site,
105) -> Result<RepoConfig, ScopeReadError> {
106 let root =
107 crate::platform::canonicalize(repo).map_err(|error| ConfigError::new(repo, error))?;
108 let moved = || ScopeReadError::Moved {
109 name: name.clone(),
110 path: site.declaration_path(),
111 };
112 let ignored = discovery_ignore(&root)?;
113 let folder = site.folder();
114 if !crate::platform::path_kind(&root.join(site.declaration_path()))
115 .is_ok_and(|kind| kind == crate::platform::FileKind::File)
116 || !folder.as_os_str().is_empty()
117 && ignored
118 .matched_path_or_any_parents(root.join(folder), true)
119 .is_ignore()
120 {
121 return Err(moved());
122 }
123 let reach = match site.kind {
124 ArtifactKind::Folder => Reach::Folder(&site.path),
125 ArtifactKind::File | ArtifactKind::Graph => Reach::File(
126 folder,
127 site.path
128 .file_name()
129 .and_then(|target| target.to_str())
130 .unwrap_or_default(),
131 ),
132 };
133 let mut config = RepoConfig {
134 root: root.clone(),
135 artifacts: BTreeMap::new(),
136 evals: Vec::new(),
137 relations: Vec::new(),
138 };
139 let mut schemas = super::schemas::Schemas::default();
140 walk(
141 &root,
142 &ignored,
143 reach,
144 FileTargets::Required,
145 &mut |found: Found<'_>| {
146 let own = config.artifacts.is_empty();
147 if own && (found.kind != site.kind || found.path != site.path) {
148 return Err(moved());
149 }
150 let declaration = declaration(&found, &mut schemas)?;
151 if own && &declaration.name != name {
152 return Err(moved());
153 }
154 Ok(declare(&mut config, found, declaration)?)
155 },
156 )?;
157 if !config.artifacts.contains_key(name) {
158 return Err(moved());
159 }
160 config.evals.clear();
161 Ok(config)
162}
163
164#[cfg(test)]
165mod tests;