use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::String;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default, rename_all = "camelCase")]
pub struct LibConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ui_dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub composable_dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lockfile: Option<String>,
#[serde(
skip_serializing_if = "BTreeMap::is_empty",
deserialize_with = "null_as_empty"
)]
pub registries: BTreeMap<String, LibRegistryConfig>,
}
fn null_as_empty<'de, D>(deserializer: D) -> Result<BTreeMap<String, LibRegistryConfig>, D::Error>
where
D: serde::Deserializer<'de>,
{
Option::<BTreeMap<String, LibRegistryConfig>>::deserialize(deserializer)
.map(Option::unwrap_or_default)
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LibRegistryConfig {
Source(String),
Detailed {
source: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
dir: Option<String>,
},
}
impl LibRegistryConfig {
pub fn source(&self) -> &str {
match self {
Self::Source(source) | Self::Detailed { source, .. } => source,
}
}
pub fn dir(&self) -> Option<&str> {
match self {
Self::Source(_) => None,
Self::Detailed { dir, .. } => dir.as_deref(),
}
}
}
impl LibConfig {
pub fn dir_for_kind(&self, kind: &str) -> Option<&str> {
let specific = match kind {
"ui" => self.ui_dir.as_deref(),
"composable" => self.composable_dir.as_deref(),
_ => None,
};
specific.or(self.dir.as_deref())
}
}
#[cfg(test)]
mod tests {
use super::{LibConfig, LibRegistryConfig};
#[test]
fn kind_specific_directory_wins_over_shared_directory() {
let config: LibConfig =
serde_json::from_str(r#"{ "dir": "src/vendor", "uiDir": "src/ui" }"#).unwrap();
assert_eq!(config.dir_for_kind("ui"), Some("src/ui"));
assert_eq!(config.dir_for_kind("composable"), Some("src/vendor"));
assert_eq!(LibConfig::default().dir_for_kind("ui"), None);
}
#[test]
fn registries_accept_shorthand_and_detailed_entries() {
let config: LibConfig = serde_json::from_str(
r#"{ "registries": {
"@acme": "npm:@acme/ui",
"@local": { "source": "./registry", "dir": "src/local" }
} }"#,
)
.unwrap();
let acme = &config.registries["@acme"];
assert_eq!(acme, &LibRegistryConfig::Source("npm:@acme/ui".into()));
assert_eq!(acme.dir(), None);
let local = &config.registries["@local"];
let null: LibConfig = serde_json::from_str(r#"{ "registries": null }"#).unwrap();
assert!(null.registries.is_empty());
assert_eq!(local.source(), "./registry");
assert_eq!(local.dir(), Some("src/local"));
}
}