use std::fmt;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HalDoc {
#[serde(default, deserialize_with = "string_or_number")]
pub docid: String,
#[serde(default)]
pub label_s: Option<String>,
#[serde(default)]
pub uri_s: Option<String>,
#[serde(default)]
pub title_s: Option<Vec<String>>,
#[serde(default, rename = "authFullName_s")]
pub auth_full_name_s: Option<Vec<String>>,
#[serde(default)]
pub produced_date_s: Option<String>,
#[serde(default)]
pub doc_type_s: Option<String>,
#[serde(flatten)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
impl HalDoc {
pub fn title(&self) -> Option<&str> {
self.title_s.as_ref()?.first().map(String::as_str)
}
pub fn authors(&self) -> Option<String> {
self.auth_full_name_s.as_ref().map(|a| a.join(", "))
}
pub fn heading(&self) -> Option<&str> {
self.title().or(self.label_s.as_deref())
}
}
impl fmt::Display for HalDoc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.heading() {
Some(heading) => write!(f, "{} ({})", heading, self.docid),
None => write!(f, "{}", self.docid),
}
}
}
fn string_or_number<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrNumber;
impl<'de> Visitor<'de> for StringOrNumber {
type Value = String;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a string or an integer")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<String, E> {
Ok(v.to_owned())
}
fn visit_string<E: de::Error>(self, v: String) -> Result<String, E> {
Ok(v)
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<String, E> {
Ok(v.to_string())
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<String, E> {
Ok(v.to_string())
}
}
deserializer.deserialize_any(StringOrNumber)
}