1use std::collections::BTreeMap;
2
3use ephemeris_documents::DocumentSpec;
4use facet::Facet;
5
6use crate::path::RepositoryPath;
7use crate::path_policy::PathRuleState;
8use crate::{FileMode, FilesError, FormatterError};
9
10#[derive(Debug, Facet)]
12#[facet(deny_unknown_fields)]
13pub struct FileState {
14 pub(crate) path_rules: Vec<PathRuleState>,
16 pub(crate) files: BTreeMap<RepositoryPath, FileSpec>,
18 pub(crate) formatter: Option<FormatterSpec>,
20}
21
22#[derive(Debug, Clone, Facet, PartialEq, Eq, PartialOrd, Ord)]
24#[facet(deny_unknown_fields)]
25pub(crate) struct FormatterSpec {
26 pub(crate) command: String,
28 pub(crate) args: Vec<String>,
30 pub(crate) inputs: Vec<RepositoryPath>,
32 pub(crate) project_root_file: RepositoryPath,
35}
36
37#[derive(Debug, Clone, Facet)]
39#[facet(deny_unknown_fields)]
40pub(crate) struct FileSpec {
41 pub(crate) executable: bool,
43 pub(crate) formatter: Option<FormatterSpec>,
45 pub(crate) document: DocumentSpec,
47}
48
49impl FileSpec {
50 pub(crate) fn validate(&self, path: &RepositoryPath) -> Result<(), FilesError> {
51 if self.is_partial() && self.formatter.is_some() {
52 return Err(FormatterError::PartialOverride { path: path.clone() }.into());
53 }
54 self.document
55 .validate()
56 .map_err(|source| FilesError::Document {
57 path: path.clone(),
58 source,
59 })
60 }
61
62 pub(crate) const fn is_partial(&self) -> bool {
63 self.document.is_partial()
64 }
65
66 pub(crate) const fn mode(&self) -> FileMode {
67 if self.executable {
68 FileMode::Executable
69 } else {
70 FileMode::Regular
71 }
72 }
73
74 pub(crate) fn formatter<'a>(
75 &'a self,
76 default: Option<&'a FormatterSpec>,
77 ) -> Option<&'a FormatterSpec> {
78 self.formatter.as_ref().or(default)
79 }
80
81 pub(crate) fn reconciled_content(
82 &self,
83 path: &RepositoryPath,
84 current: Option<&[u8]>,
85 ) -> Result<Vec<u8>, FilesError> {
86 self.document
87 .reconcile(current)
88 .map_err(|source| FilesError::Document {
89 path: path.clone(),
90 source,
91 })
92 }
93}