use crate::node::Node;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
pub enum Disabled {
Flag(bool),
Expr(String),
}
impl Default for Disabled {
fn default() -> Self {
Self::Flag(false)
}
}
impl Disabled {
pub fn is_disabled(&self) -> bool {
matches!(self, Self::Flag(true))
}
pub fn as_expr(&self) -> Option<&str> {
match self {
Self::Expr(source) => Some(source),
_ => None,
}
}
}
fn disabled_is_default(value: &Disabled) -> bool {
matches!(value, Disabled::Flag(false))
}
impl Serialize for Disabled {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Flag(flag) => serializer.serialize_bool(*flag),
Self::Expr(source) => serializer.serialize_str(source),
}
}
}
impl<'de> Deserialize<'de> for Disabled {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct DisabledVisitor;
impl serde::de::Visitor<'_> for DisabledVisitor {
type Value = Disabled;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a boolean or a `!!js` expression string")
}
fn visit_bool<E: serde::de::Error>(self, value: bool) -> Result<Disabled, E> {
Ok(Disabled::Flag(value))
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Disabled, E> {
Ok(Disabled::Expr(value.to_owned()))
}
fn visit_string<E: serde::de::Error>(self, value: String) -> Result<Disabled, E> {
Ok(Disabled::Expr(value))
}
}
deserializer.deserialize_any(DisabledVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn disabled_serde_json_round_trips() {
let options: EntryOptions =
serde_json::from_str(r#"{"name":"n","disabled":true}"#).unwrap();
assert_eq!(options.disabled, Disabled::Flag(true));
let options: EntryOptions =
serde_json::from_str(r#"{"name":"n","disabled":"process.platform"}"#).unwrap();
assert_eq!(
options.disabled,
Disabled::Expr("process.platform".to_owned())
);
let text = serde_json::to_string(&options).unwrap();
assert_eq!(text, r#"{"name":"n","disabled":"process.platform"}"#);
let text = serde_json::to_string(&EntryOptions::new("n")).unwrap();
assert_eq!(text, r#"{"name":"n"}"#);
}
}
pub const IMPORT_NAME: &str = "import";
pub const GROUP_NAME: &str = "group";
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct EntryOptions {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default)]
pub name: String,
#[serde(default, skip_serializing_if = "disabled_is_default")]
pub disabled: Disabled,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub inject: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub group: Vec<EntryOptions>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config: Option<Node>,
}
impl EntryOptions {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
..Self::default()
}
}
pub fn with_id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn with_config(mut self, config: Node) -> Self {
self.config = Some(config);
self
}
pub fn with_group(mut self, group: Vec<EntryOptions>) -> Self {
self.group = group;
self
}
pub fn with_disabled(mut self, disabled: bool) -> Self {
self.disabled = Disabled::Flag(disabled);
self
}
pub fn import_url(&self) -> Option<&str> {
if self.name != IMPORT_NAME {
return None;
}
self.config.as_ref()?.as_object()?.get("url")?.as_str()
}
pub fn with_inject<I, S>(mut self, inject: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.inject = inject.into_iter().map(Into::into).collect();
self
}
}