pub mod manifest; use manifest::{ManifestError, ValuableManifest}; use serde::Deserialize; /// Sabry configuration, as it is from package.metadata.sabry #[derive(Default, Deserialize, Clone)] pub struct SabryConfig { #[serde(default = "SabryCssConfig::default")] pub css: SabryCssConfig, #[serde(default = "SabrySassConfig::default")] pub sass: SabrySassConfig, #[serde(default = "SabryHashConfig::default")] pub hash: SabryHashConfig, #[serde(default = "SabryGrassConfig::default")] pub grass: SabryGrassConfig, #[serde(default = "SabryLightCssConfig::default")] pub lightningcss: SabryLightCssConfig, } impl SabryConfig { /// Require sabry configuration for the current project from every place in the code pub fn require() -> Result { let manifest = manifest::read_manifest()?; Ok(Self::from(&manifest)) } } impl From<&ValuableManifest> for SabryConfig { fn from(value: &ValuableManifest) -> Self { value .package .as_ref() .and_then(|p| p.metadata.as_ref()) .and_then(|m| m.sabry.as_ref()) .map_or(Self::default(), |sbr| sbr.clone()) } } /// Sabry `css` configuration table #[derive(Deserialize, Clone)] pub struct SabryCssConfig { pub bundle: Option, pub scopes: Option, pub bundle_prelude: Option>, #[serde(default = "SabryCssConfig::default_minify")] pub minify: bool, } impl SabryCssConfig { fn default_minify() -> bool { true } } impl Default for SabryCssConfig { fn default() -> Self { Self { bundle: None, scopes: None, bundle_prelude: None, minify: Self::default_minify(), } } } /// Sabry `sass` configuration table /// /// (SASS/SCSS related config) #[derive(Deserialize, Clone)] pub struct SabrySassConfig { #[serde(default = "SabrySassConfig::default_scanroot")] pub scanroot: String, pub modules: Option>, #[serde(default = "SabrySassConfig::default_intermediate_dir")] pub intermediate_dir: String, #[serde(default = "SabrySassConfig::default_module_name_collision")] pub module_name_collision: BehavSassModCollision, } impl SabrySassConfig { fn default_scanroot() -> String { "src".into() } fn default_intermediate_dir() -> String { "target/.sabry/sass".into() } fn default_module_name_collision() -> BehavSassModCollision { BehavSassModCollision::default() } } impl Default for SabrySassConfig { fn default() -> Self { Self { scanroot: Self::default_scanroot(), modules: None, intermediate_dir: Self::default_intermediate_dir(), module_name_collision: Self::default_module_name_collision(), } } } /// Sabry `hash` configuration table #[derive(Deserialize, Clone)] pub struct SabryHashConfig { #[serde(default = "SabryHashConfig::default_size")] pub size: usize, #[serde(default = "SabryHashConfig::default_collision")] pub collision: BehavHashCollision, #[serde(default = "SabryHashConfig::default_use_scope_name")] pub use_scope_name: bool, #[serde(default = "SabryHashConfig::default_use_item_names")] pub use_item_names: bool, #[serde(default = "SabryHashConfig::default_use_code_size")] pub use_code_size: bool, #[serde(default = "SabryHashConfig::default_use_code_text")] pub use_code_text: bool, } impl SabryHashConfig { fn default_size() -> usize { 6 } fn default_collision() -> BehavHashCollision { BehavHashCollision::default() } fn default_use_scope_name() -> bool { true } fn default_use_item_names() -> bool { false } fn default_use_code_size() -> bool { true } fn default_use_code_text() -> bool { false } } impl Default for SabryHashConfig { fn default() -> Self { Self { size: Self::default_size(), collision: Self::default_collision(), use_scope_name: Self::default_use_scope_name(), use_code_size: Self::default_use_code_size(), use_code_text: Self::default_use_code_text(), use_item_names: Self::default_use_item_names(), } } } /// [grass] specific configuration for sabry #[derive(Default, Deserialize, Clone)] pub struct SabryGrassConfig {} /// [lightningcss] specific configuration for sabry #[derive(Default, Deserialize, Clone)] pub struct SabryLightCssConfig { pub targets: SabryLightTargets, } /// \[package.metadata.sabry.lightningcss.targets\] configuration. /// /// This is a convenience layer under [lightningcss::targets::Browsers] structure accepted /// by lightningcss. /// /// This structure is a part of [SabryConfig], which faces developer with more convenient /// `safari = "13.2"` syntax. #[derive(Default, Deserialize, Clone)] pub struct SabryLightTargets { pub android: Option, pub chrome: Option, pub edge: Option, pub firefox: Option, pub ie: Option, pub ios_saf: Option, pub opera: Option, pub safari: Option, pub samsung: Option, } impl SabryLightTargets { pub fn parse_ver(ver: &str) -> u32 { let mut vercode: u32 = 0; let vers = ver.split('.'); for (i, dig) in vers.into_iter().enumerate() { let dignum = dig .parse::() .unwrap_or_else(|_| panic!("Invalid browser specifier: '{dig}'")); let shift = 16 - 8 * i; vercode |= dignum << shift; } vercode } } impl From for lightningcss::targets::Browsers { fn from(value: SabryLightTargets) -> Self { Self { android: value.android.map(|s| SabryLightTargets::parse_ver(&s)), chrome: value.chrome.map(|s| SabryLightTargets::parse_ver(&s)), edge: value.edge.map(|s| SabryLightTargets::parse_ver(&s)), firefox: value.firefox.map(|s| SabryLightTargets::parse_ver(&s)), ie: value.ie.map(|s| SabryLightTargets::parse_ver(&s)), ios_saf: value.ios_saf.map(|s| SabryLightTargets::parse_ver(&s)), opera: value.opera.map(|s| SabryLightTargets::parse_ver(&s)), safari: value.safari.map(|s| SabryLightTargets::parse_ver(&s)), samsung: value.samsung.map(|s| SabryLightTargets::parse_ver(&s)), } } } /// \[package.metadata.sabry.sass\].module_name_collision option #[derive(Deserialize, Clone, Copy)] #[serde(rename_all = "lowercase")] pub enum BehavSassModCollision { Merge, Error, } impl Default for BehavSassModCollision { fn default() -> Self { Self::Merge } } /// \[package.metadata.sabry.hash\].collision option #[derive(Deserialize, Clone, Copy)] #[serde(rename_all = "lowercase")] pub enum BehavHashCollision { Error, Ignore, } impl Default for BehavHashCollision { fn default() -> Self { Self::Ignore } } #[cfg(test)] mod test { use super::{manifest, SabryConfig}; #[test] fn empty_manifest_still_worth_a_read() { let manifest = manifest::read_manifest().unwrap(); let _conf = SabryConfig::from(&manifest); } }