use serde::{
Deserialize,
Deserializer,
de::{
DeserializeOwned,
Error as DeserializerError,
},
};
use serde_json::Value;
use thiserror::Error;
#[derive(Debug, Error)]
#[error("{0}")]
pub struct DeserializationError(&'static str);
pub fn object_to_id(
object: &Value,
) -> Result<String, DeserializationError> {
let object_id = match object {
Value::String(string) => string.to_owned(),
Value::Object(_) => {
object["id"].as_str()
.ok_or(DeserializationError("missing 'id' property"))?
.to_owned()
},
_ => return Err(DeserializationError("unexpected value type")),
};
Ok(object_id)
}
pub fn deserialize_into_object_id<'de, D>(
deserializer: D,
) -> Result<String, D::Error>
where D: Deserializer<'de>
{
let value = Value::deserialize(deserializer)?;
let object_id = object_to_id(&value)
.map_err(DeserializerError::custom)?;
Ok(object_id)
}
pub fn deserialize_into_object_id_opt<'de, D>(
deserializer: D,
) -> Result<Option<String>, D::Error>
where D: Deserializer<'de>
{
let maybe_value: Option<Value> = Option::deserialize(deserializer)?;
let maybe_object_id = if let Some(value) = maybe_value {
let object_id = object_to_id(&value)
.map_err(DeserializerError::custom)?;
Some(object_id)
} else {
None
};
Ok(maybe_object_id)
}
fn parse_string_array(
value: &Value,
) -> Result<Vec<String>, DeserializationError> {
let result = match value {
Value::String(string) => vec![string.clone()],
Value::Array(array) => {
let mut items = vec![];
for value in array {
let string = value.as_str()
.ok_or(DeserializationError("unexpected array item type"))?
.to_string();
items.push(string);
};
items
},
_ => return Err(DeserializationError("unexpected value type")),
};
Ok(result)
}
pub fn deserialize_string_array<'de, D>(
deserializer: D,
) -> Result<Vec<String>, D::Error>
where D: Deserializer<'de>
{
let maybe_value: Option<Value> = Option::deserialize(deserializer)?;
let strings = if let Some(value) = maybe_value {
parse_string_array(&value).map_err(DeserializerError::custom)?
} else {
vec![]
};
Ok(strings)
}
pub fn parse_into_id_array(
value: &Value,
) -> Result<Vec<String>, DeserializationError> {
let result = match value {
Value::Null => vec![],
Value::String(_) | Value::Object(_) => {
let object_id = object_to_id(value)?;
vec![object_id]
},
Value::Array(array) => {
let mut results = vec![];
for value in array {
let object_id = object_to_id(value)?;
results.push(object_id);
};
results
},
_ => return Err(DeserializationError("unexpected value type")),
};
Ok(result)
}
pub fn deserialize_into_id_array<'de, D>(
deserializer: D,
) -> Result<Vec<String>, D::Error>
where D: Deserializer<'de>
{
let value: Value = Value::deserialize(deserializer)?;
parse_into_id_array(&value).map_err(DeserializerError::custom)
}
fn link_to_href(
link: &Value,
) -> Result<String, DeserializationError> {
let href = match link {
Value::String(string) => string.to_owned(),
Value::Object(_) => {
link["href"].as_str()
.ok_or(DeserializationError("missing href property"))?
.to_string()
},
_ => return Err(DeserializationError("unexpected value type")),
};
Ok(href)
}
pub fn parse_into_href_array(
value: &Value,
) -> Result<Vec<String>, DeserializationError> {
let result = match value {
Value::String(_) | Value::Object(_) => {
let object_id = link_to_href(value)?;
vec![object_id]
},
Value::Array(array) => {
let mut results = vec![];
for value in array {
let object_id = link_to_href(value)?;
results.push(object_id);
};
results
},
_ => return Err(DeserializationError("unexpected value type")),
};
Ok(result)
}
pub fn deserialize_into_link_href<'de, D>(
deserializer: D,
) -> Result<String, D::Error>
where D: Deserializer<'de>
{
let value = Value::deserialize(deserializer)?;
let link_href = link_to_href(&value)
.map_err(DeserializerError::custom)?;
Ok(link_href)
}
pub fn parse_into_array<T: DeserializeOwned>(
value: &Value,
) -> Result<Vec<T>, DeserializationError> {
let objects = match value {
Value::Array(array) => array.clone(),
Value::Object(_) => vec![value.clone()],
_ => return Err(DeserializationError("unexpected value type")),
};
let mut items = vec![];
for object in objects {
let item: T = serde_json::from_value(object)
.map_err(|_| DeserializationError("invalid array item"))?;
items.push(item);
};
Ok(items)
}
pub fn deserialize_object_array<'de, D, T>(
deserializer: D,
) -> Result<Vec<T>, D::Error>
where D: Deserializer<'de>, T: DeserializeOwned
{
let maybe_value: Option<Value> = Option::deserialize(deserializer)?;
let objects = if let Some(value) = maybe_value {
parse_into_array(&value).map_err(DeserializerError::custom)?
} else {
vec![]
};
Ok(objects)
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn test_object_to_id_string() {
let value = json!("test_id");
assert_eq!(object_to_id(&value).unwrap(), "test_id");
}
#[test]
fn test_object_to_id_object() {
let value = json!({"id": "test_id", "type": "Note"});
assert_eq!(object_to_id(&value).unwrap(), "test_id");
}
#[test]
fn test_object_to_id_array() {
let value = json!(["test_id"]);
assert_eq!(
object_to_id(&value).err().unwrap().to_string(),
"unexpected value type",
);
}
#[test]
fn test_deserialize_into_object_id_opt() {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct TestObject {
#[serde(default, deserialize_with = "deserialize_into_object_id_opt")]
in_reply_to: Option<String>,
}
let value = json!({});
let object: TestObject = serde_json::from_value(value).unwrap();
assert_eq!(object.in_reply_to, None);
let value = json!({"inReplyTo": "https://social.example/mypost"});
let object: TestObject = serde_json::from_value(value).unwrap();
assert_eq!(
object.in_reply_to,
Some("https://social.example/mypost".to_string()),
);
}
#[test]
fn test_deserialize_string_array() {
#[derive(Deserialize)]
struct TestObject {
#[serde(deserialize_with = "deserialize_string_array")]
rel: Vec<String>,
}
let value = json!({});
let error = serde_json::from_value::<TestObject>(value).err().unwrap();
assert_eq!(error.to_string(), "missing field `rel`");
let value = json!({"rel": "test"});
let object: TestObject = serde_json::from_value(value).unwrap();
assert_eq!(object.rel, vec!["test".to_string()]);
let value = json!({"rel": ["a", "b"]});
let object: TestObject = serde_json::from_value(value).unwrap();
assert_eq!(
object.rel,
vec!["a".to_string(), "b".to_string()],
);
}
#[test]
fn test_parse_into_id_array_with_string() {
let value = json!("test");
assert_eq!(
parse_into_id_array(&value).unwrap(),
vec!["test".to_string()],
);
}
#[test]
fn test_parse_into_id_array_with_array() {
let value = json!(["test1", "test2"]);
assert_eq!(
parse_into_id_array(&value).unwrap(),
vec!["test1".to_string(), "test2".to_string()],
);
}
#[test]
fn test_parse_into_id_array_with_array_of_objects() {
let value = json!([{"id": "test1"}, {"id": "test2"}]);
assert_eq!(
parse_into_id_array(&value).unwrap(),
vec!["test1".to_string(), "test2".to_string()],
);
}
#[test]
fn test_parse_into_id_array_with_empty() {
let object = json!({"key": 1});
let value = &object["test"];
assert_eq!(
parse_into_id_array(value).unwrap().is_empty(),
true,
);
}
#[test]
fn test_deserialize_into_id_array() {
#[derive(Deserialize)]
struct TestObject {
#[serde(default, deserialize_with = "deserialize_into_id_array")]
to: Vec<String>,
}
let value = json!({});
let object: TestObject = serde_json::from_value(value).unwrap();
assert_eq!(object.to.is_empty(), true);
let value = json!({"to": "https://social.example/actor"});
let object: TestObject = serde_json::from_value(value).unwrap();
assert_eq!(
object.to,
vec!["https://social.example/actor".to_string()],
);
}
#[test]
fn test_link_to_href() {
let link = json!({"name": "test", "href": "https://test.example"});
assert_eq!(
link_to_href(&link).unwrap(),
"https://test.example",
);
}
#[test]
fn test_parse_into_href_array() {
let value = json!([
"https://test1.example",
"https://test2.example",
]);
assert_eq!(
parse_into_href_array(&value).unwrap(),
vec![
"https://test1.example".to_string(),
"https://test2.example".to_string(),
],
);
}
#[test]
fn test_parse_into_array_tag_list() {
let value = json!({"type": "Mention"});
let value_list: Vec<Value> = parse_into_array(&value).unwrap();
assert_eq!(value_list, vec![value]);
}
}