use serde_json::Value;
use super::types::{ParsedRoster, RosterFormat};
pub trait FormatAdapter {
fn format(&self) -> RosterFormat;
fn detect(&self, decoded: &Value) -> bool;
fn parse(&self, decoded: &Value) -> Result<ParsedRoster, ParseError>;
}
#[derive(Debug)]
pub struct ParseError(pub String);
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for ParseError {}
pub fn select_adapter<'a>(
decoded: &Value,
adapters: &'a [Box<dyn FormatAdapter>],
) -> Result<&'a dyn FormatAdapter, ParseError> {
adapters
.iter()
.map(AsRef::as_ref)
.find(|a| a.detect(decoded))
.ok_or_else(|| {
let tried: Vec<String> = adapters
.iter()
.map(|a| format_id(a.format()).to_string())
.collect();
let tried = if tried.is_empty() {
"none".to_string()
} else {
tried.join(", ")
};
ParseError(format!(
"no registered import adapter recognises this payload (tried: {tried})"
))
})
}
pub fn format_id(fmt: RosterFormat) -> &'static str {
match fmt {
RosterFormat::Listforge => "listforge",
RosterFormat::NewrecruitJson => "newrecruit-json",
RosterFormat::NewrecruitWtcCompact => "newrecruit-wtc-compact",
RosterFormat::NewrecruitWtcFull => "newrecruit-wtc-full",
RosterFormat::NewrecruitSimple => "newrecruit-simple",
RosterFormat::Rosterizer => "rosterizer",
RosterFormat::Gw => "gw",
RosterFormat::ListforgeText => "listforge-text",
}
}