use serde::de::{self, Deserializer};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum Unit {
File { path: String },
Section { file: String, anchor: String },
Symbol { id: String },
Directory { path: String },
Crate { id: String },
Module { id: String },
}
impl Unit {
pub fn file(path: impl Into<String>) -> Self {
Unit::File { path: path.into() }
}
pub fn is_directory_subtree(&self) -> bool {
match self {
Unit::File { path } => path.ends_with('/'),
Unit::Directory { .. } => true,
_ => false,
}
}
}
impl<'de> Deserialize<'de> for Unit {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Repr {
Bare(String),
Tagged(Tagged),
Wrapped { unit: Box<Unit> },
}
#[derive(Deserialize)]
#[serde(tag = "kind", rename_all = "kebab-case", deny_unknown_fields)]
enum Tagged {
File { path: String },
Section { file: String, anchor: String },
Symbol { id: String },
Directory { path: String },
Crate { id: String },
Module { id: String },
}
match Repr::deserialize(deserializer)? {
Repr::Bare(path) => {
if path.trim().is_empty() {
return Err(de::Error::custom("unit path must not be empty"));
}
Ok(Unit::File { path })
}
Repr::Tagged(Tagged::File { path }) => Ok(Unit::File { path }),
Repr::Tagged(Tagged::Section { file, anchor }) => Ok(Unit::Section { file, anchor }),
Repr::Tagged(Tagged::Symbol { id }) => Ok(Unit::Symbol { id }),
Repr::Tagged(Tagged::Directory { path }) => Ok(Unit::Directory { path }),
Repr::Tagged(Tagged::Crate { id }) => Ok(Unit::Crate { id }),
Repr::Tagged(Tagged::Module { id }) => Ok(Unit::Module { id }),
Repr::Wrapped { unit } => Ok(*unit),
}
}
}