use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AttributeValue {
String(String),
Int(i64),
List(Vec<String>),
Null,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrackId<'a> {
String(&'a str),
Int(i64),
}
impl AttributeValue {
pub fn as_str(&self) -> Option<&str> {
match self {
AttributeValue::String(s) => Some(s.as_str()),
_ => None,
}
}
pub fn as_int(&self) -> Option<i64> {
match self {
AttributeValue::Int(i) => Some(*i),
AttributeValue::String(s) => s.parse::<i64>().ok(),
_ => None,
}
}
pub fn as_list(&self) -> Option<&Vec<String>> {
match self {
AttributeValue::List(l) => Some(l),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Track {
pub track_type: String,
#[serde(flatten)]
attributes: IndexMap<String, AttributeValue>,
}
impl Track {
pub fn new(track_type: String) -> Self {
Track {
track_type,
attributes: IndexMap::new(),
}
}
pub fn track_type(&self) -> &str {
&self.track_type
}
pub fn track_id(&self) -> Option<&str> {
match self.track_id_any() {
Some(TrackId::String(value)) => Some(value),
_ => None,
}
}
pub fn track_id_int(&self) -> Option<i64> {
match self.track_id_any() {
Some(TrackId::Int(value)) => Some(value),
Some(TrackId::String(value)) => value.parse::<i64>().ok(),
None => None,
}
}
pub fn track_id_value(&self) -> Option<&AttributeValue> {
self.get("track_id")
}
pub fn track_id_any(&self) -> Option<TrackId<'_>> {
match self.attributes.get("track_id") {
Some(AttributeValue::String(value)) => Some(TrackId::String(value.as_str())),
Some(AttributeValue::Int(value)) => Some(TrackId::Int(*value)),
Some(AttributeValue::List(values)) => {
values.first().map(|value| TrackId::String(value.as_str()))
}
Some(AttributeValue::Null) | None => None,
}
}
pub fn get(&self, key: &str) -> Option<&AttributeValue> {
match self.attributes.get(key) {
Some(AttributeValue::Null) => None,
other => other,
}
}
pub fn get_int(&self, key: &str) -> Option<i64> {
self.get(key).and_then(|v| v.as_int())
}
pub fn get_string(&self, key: &str) -> Option<&str> {
self.get(key).and_then(|v| v.as_str())
}
pub fn get_list(&self, key: &str) -> Option<&Vec<String>> {
self.get(key).and_then(|v| v.as_list())
}
pub fn set(&mut self, key: String, value: AttributeValue) {
self.attributes.insert(key, value);
}
pub fn set_string(&mut self, key: String, value: String) {
self.attributes.insert(key, AttributeValue::String(value));
}
pub fn set_int(&mut self, key: String, value: i64) {
self.attributes.insert(key, AttributeValue::Int(value));
}
pub fn set_list(&mut self, key: String, value: Vec<String>) {
self.attributes.insert(key, AttributeValue::List(value));
}
pub fn set_null(&mut self, key: String) {
self.attributes.insert(key, AttributeValue::Null);
}
pub fn attributes(&self) -> &IndexMap<String, AttributeValue> {
&self.attributes
}
pub fn attributes_mut(&mut self) -> &mut IndexMap<String, AttributeValue> {
&mut self.attributes
}
pub fn to_data(&self) -> serde_json::Map<String, serde_json::Value> {
let mut data = serde_json::Map::new();
data.insert(
"track_type".to_string(),
serde_json::Value::String(self.track_type.clone()),
);
for (key, value) in &self.attributes {
let json_value = match value {
AttributeValue::String(s) => serde_json::Value::String(s.clone()),
AttributeValue::Int(i) => serde_json::Value::Number((*i).into()),
AttributeValue::List(l) => serde_json::Value::Array(
l.iter()
.map(|s| serde_json::Value::String(s.clone()))
.collect(),
),
AttributeValue::Null => serde_json::Value::Null,
};
data.insert(key.clone(), json_value);
}
data
}
}
impl std::fmt::Display for Track {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let track_id_display = match self.attributes.get("track_id") {
Some(AttributeValue::String(s)) => s.clone(),
Some(AttributeValue::Int(i)) => i.to_string(),
Some(AttributeValue::List(list)) => {
list.first().cloned().unwrap_or_else(|| "None".to_string())
}
Some(AttributeValue::Null) | None => "None".to_string(),
};
write!(
f,
"<Track track_id='{}', track_type='{}'>",
track_id_display, self.track_type
)
}
}