Skip to main content

pgevolve_core/lint/profile/
mod.rs

1//! Layout-profile lint rules. Spec ยง12.
2//!
3//! Each profile expresses *where* an object should live on disk. The
4//! universal rules don't care about paths; this module does.
5
6pub mod custom;
7pub mod feature_grouped;
8pub mod free_form;
9pub mod kind_grouped;
10pub mod schema_mirror;
11
12use std::path::Path;
13
14pub use custom::{Assertion, CustomProfile, PathPattern};
15
16use super::finding::Finding;
17use super::source_tree::SourceTree;
18
19/// One of the five layout profiles.
20#[derive(Debug, Clone)]
21pub enum Profile {
22    /// `schema/<schema>/<kind>/<name>.sql` โ€” strictest.
23    SchemaMirror,
24    /// `schema/<kind>/<schema>.<name>.sql`.
25    KindGrouped,
26    /// `schema/<feature>/*.sql` โ€” files grouped by feature.
27    FeatureGrouped,
28    /// No path constraints; universal rules only.
29    FreeForm,
30    /// User-defined regex + assertion rules.
31    Custom(CustomProfile),
32}
33
34impl Profile {
35    /// Resolve a profile name (built-in keyword or path to a custom TOML).
36    ///
37    /// Recognized keywords: `schema-mirror`, `kind-grouped`,
38    /// `feature-grouped`, `free-form`. Any other string is treated as a path
39    /// to a custom-profile TOML file.
40    pub fn from_name(name: &str) -> Result<Self, ProfileLoadError> {
41        match name {
42            "schema-mirror" => Ok(Self::SchemaMirror),
43            "kind-grouped" => Ok(Self::KindGrouped),
44            "feature-grouped" => Ok(Self::FeatureGrouped),
45            "free-form" => Ok(Self::FreeForm),
46            other => {
47                let path = Path::new(other);
48                let content = std::fs::read_to_string(path)
49                    .map_err(|e| ProfileLoadError::Io(path.to_path_buf(), e.to_string()))?;
50                let custom: CustomProfile = toml::from_str(&content)
51                    .map_err(|e| ProfileLoadError::Parse(path.to_path_buf(), e.to_string()))?;
52                Ok(Self::Custom(custom))
53            }
54        }
55    }
56}
57
58/// Failure mode for [`Profile::from_name`].
59#[derive(Debug, thiserror::Error)]
60pub enum ProfileLoadError {
61    /// I/O reading the custom profile TOML.
62    #[error("reading custom profile {0}: {1}")]
63    Io(std::path::PathBuf, String),
64    /// Custom profile TOML didn't parse.
65    #[error("parsing custom profile {0}: {1}")]
66    Parse(std::path::PathBuf, String),
67}
68
69/// Dispatch to the chosen profile's rules.
70pub fn check_profile(profile: &Profile, tree: &SourceTree, schema_dir: &Path) -> Vec<Finding> {
71    match profile {
72        Profile::SchemaMirror => schema_mirror::check(tree, schema_dir),
73        Profile::KindGrouped => kind_grouped::check(tree, schema_dir),
74        Profile::FeatureGrouped => feature_grouped::check(tree, schema_dir),
75        Profile::FreeForm => free_form::check(tree, schema_dir),
76        Profile::Custom(c) => custom::check(c, tree, schema_dir),
77    }
78}