use serde_json::Value;
use url::Url;
use crate::entry::Entry;
use crate::error::transform::{InvalidUrlError, JsonError, NothingToTransformError};
use crate::sink::{Media, Message};
#[derive(Debug)]
pub struct TextQuery {
pub string: String,
pub prepend: Option<String>,
pub append: Option<String>,
}
#[derive(Debug)]
pub struct Json {
pub itemq: Vec<String>,
pub titleq: Option<String>,
pub textq: Option<Vec<TextQuery>>, pub idq: String, pub linkq: Option<TextQuery>,
pub imgq: Option<Vec<String>>, }
impl Json {
#[tracing::instrument(skip_all)]
pub fn transform(&self, entry: &Entry) -> Result<Vec<Entry>, JsonError> {
let json: Value =
serde_json::from_str(entry.raw_contents.as_ref().ok_or(NothingToTransformError)?)?;
let items = self.itemq.iter().try_fold(&json, |acc, x| {
acc.get(x.as_str())
.ok_or_else(|| JsonError::JsonParseKeyNotFound(x.clone()))
})?;
let items_iter: Box<dyn Iterator<Item = &Value>> = if let Some(items) = items.as_array() {
Box::new(items.iter())
} else if let Some(items) = items.as_object() {
Box::new(items.iter().map(|(_, v)| v))
} else {
return Err(JsonError::JsonParseKeyWrongType {
key: self.itemq.last().unwrap().clone(),
expected_type: "iterator (array, map)",
found_type: format!("{items:?}"),
});
};
items_iter
.into_iter()
.map(|item| {
let title = self
.titleq
.as_ref()
.and_then(|s| item.get(s))
.and_then(serde_json::Value::as_str)
.map(|s| s.trim().to_owned());
let body = self
.textq
.as_ref()
.map(|v| {
let s = v
.iter()
.map(|query| {
let mut text_str = {
let text_val = item.get(&query.string).ok_or_else(|| {
JsonError::JsonParseKeyNotFound(query.string.clone())
})?;
text_val
.as_str()
.ok_or_else(|| JsonError::JsonParseKeyWrongType {
key: query.string.clone(),
expected_type: "string",
found_type: format!("{text_val:?}"),
})?
.trim()
.to_owned()
};
if query.prepend.is_some() || query.append.is_some() {
text_str = format!(
"{prepend}{original}{append}",
prepend = query.prepend.as_deref().unwrap_or_default(),
original = text_str,
append = query.append.as_deref().unwrap_or_default()
);
}
Ok(text_str)
})
.collect::<Result<Vec<String>, JsonError>>()?
.join("\n\n");
Ok::<_, JsonError>(s)
})
.transpose()?;
let id = {
let id_val = item
.get(&self.idq)
.ok_or_else(|| JsonError::JsonParseKeyNotFound(self.idq.clone()))?;
if let Some(id) = id_val.as_str() {
id.to_owned()
} else if let Some(id) = id_val.as_i64() {
id.to_string()
} else if let Some(id) = id_val.as_u64() {
id.to_string()
} else {
return Err(JsonError::JsonParseKeyWrongType {
key: self.idq.clone(),
expected_type: "string/i64/u64",
found_type: format!("{id_val:?}"),
});
}
};
let link = self
.linkq
.as_ref()
.map(|linkq| {
let link_val = item
.get(&linkq.string)
.ok_or_else(|| JsonError::JsonParseKeyNotFound(linkq.string.clone()))?;
let mut link_str = link_val
.as_str()
.ok_or_else(|| JsonError::JsonParseKeyWrongType {
key: linkq.string.clone(),
expected_type: "string",
found_type: format!("{link_val:?}"),
})?
.to_owned();
if linkq.prepend.is_some() || linkq.append.is_some() {
link_str = format!(
"{prepend}{original}{append}",
prepend = linkq.prepend.as_deref().unwrap_or_default(),
original = link_str,
append = linkq.append.as_deref().unwrap_or_default()
);
}
Url::try_from(link_str.as_str())
.map_err(|e| JsonError::from(InvalidUrlError(e, link_str)))
})
.transpose()?;
let img: Option<Url> = self
.imgq
.as_ref()
.map(|imgq| {
let first = item
.get(&imgq[0])
.ok_or_else(|| JsonError::JsonParseKeyNotFound(imgq[0].clone()))?;
let img_val = imgq.iter().skip(1).try_fold(first, |val, x| {
val.get(x)
.ok_or_else(|| JsonError::JsonParseKeyNotFound(x.clone()))
})?;
let img_str = img_val
.as_str()
.ok_or_else(|| JsonError::JsonParseKeyWrongType {
key: imgq.last().unwrap().clone(),
expected_type: "string",
found_type: format!("{img_val:?}"),
})?
.to_owned();
Url::try_from(img_str.as_str())
.map_err(|e| JsonError::from(InvalidUrlError(e, img_str)))
})
.transpose()?;
Ok(Entry {
id: Some(id),
raw_contents: body.clone(),
msg: Message {
title,
body,
link,
media: img.map(|url| vec![Media::Photo(url)]),
},
})
})
.collect::<Result<Vec<Entry>, JsonError>>()
}
}