|
@@ -8,11 +8,13 @@ use super::{filter::TagValue, Id};
|
|
|
use chrono::{DateTime, Utc};
|
|
|
use serde::{
|
|
|
de::{self, Deserialize, Deserializer},
|
|
|
- ser::{self, SerializeSeq, Serializer},
|
|
|
+ ser::{self, Serializer},
|
|
|
};
|
|
|
+use serde_json::from_value;
|
|
|
use std::{
|
|
|
collections::VecDeque,
|
|
|
fmt::{self, Display},
|
|
|
+ ops::Deref,
|
|
|
};
|
|
|
use url::Url;
|
|
|
|
|
@@ -30,8 +32,6 @@ pub enum Marker {
|
|
|
Unknown(String),
|
|
|
}
|
|
|
|
|
|
-type ParsedUrl = (Option<Url>, String);
|
|
|
-
|
|
|
impl Display for Marker {
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
write!(
|
|
@@ -69,6 +69,7 @@ pub enum RelayAccessType {
|
|
|
Both,
|
|
|
}
|
|
|
|
|
|
+#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
/// Tags are how events relates to each other.
|
|
|
///
|
|
|
/// So far, there are two standard tags, Events and Public Key. This an event
|
|
@@ -77,14 +78,25 @@ pub enum RelayAccessType {
|
|
|
///
|
|
|
/// Any non standard tag will be parsed as Unknown with a vector of strings as
|
|
|
/// their parameters
|
|
|
+pub struct Tag(TagType, Vec<serde_json::Value>);
|
|
|
+
|
|
|
+impl Deref for Tag {
|
|
|
+ type Target = TagType;
|
|
|
+
|
|
|
+ fn deref(&self) -> &Self::Target {
|
|
|
+ &self.0
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
-pub enum Tag {
|
|
|
+/// Inner tag type
|
|
|
+pub enum TagType {
|
|
|
/// Tag another event
|
|
|
- Event(Id, Option<ParsedUrl>, Option<Marker>),
|
|
|
+ Event(Id, Option<Url>, Option<Marker>),
|
|
|
/// Tag another public key
|
|
|
- PubKey(Id, Option<ParsedUrl>, Option<String>),
|
|
|
+ PubKey(Id, Option<Url>, Option<String>),
|
|
|
/// Tag a relay
|
|
|
- Relay(ParsedUrl, RelayAccessType),
|
|
|
+ Relay(Url, RelayAccessType),
|
|
|
/// Tag a hashtag
|
|
|
Hashtag(String),
|
|
|
/// Tag with an external content id
|
|
@@ -102,12 +114,32 @@ pub enum Tag {
|
|
|
/// Image - NIP-23, NIP-52, NIP-58
|
|
|
Image(Url, Option<String>),
|
|
|
/// Zap Goal - NIP-75
|
|
|
- ZapGoal(Id, Option<ParsedUrl>),
|
|
|
+ ZapGoal(Id, Option<Url>),
|
|
|
/// Weird, supported nonetheless
|
|
|
Empty,
|
|
|
}
|
|
|
|
|
|
-impl Tag {
|
|
|
+impl ser::Serialize for Tag {
|
|
|
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
+ where
|
|
|
+ S: Serializer,
|
|
|
+ {
|
|
|
+ self.1.serialize(serializer)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'de> Deserialize<'de> for Tag {
|
|
|
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
+ where
|
|
|
+ D: Deserializer<'de>,
|
|
|
+ {
|
|
|
+ let raw_tag: Vec<serde_json::Value> = Deserialize::deserialize(deserializer)?;
|
|
|
+ let tag = from_value(raw_tag.clone().into()).map_err(de::Error::custom)?;
|
|
|
+ Ok(Self(tag, raw_tag))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl TagType {
|
|
|
/// Is the tag a public key?
|
|
|
pub fn is_pubkey(&self) -> bool {
|
|
|
matches!(self, Self::PubKey(_, _, _))
|
|
@@ -121,30 +153,32 @@ impl Tag {
|
|
|
/// Get the identifier for the tag
|
|
|
pub fn get_identifier(&self) -> &str {
|
|
|
match self {
|
|
|
- Tag::Event(_, _, _) => "e",
|
|
|
- Tag::PubKey(_, _, _) => "p",
|
|
|
- Tag::Relay(_, _) => "r",
|
|
|
- Tag::Hashtag(_) => "t",
|
|
|
- Tag::Title(_) => "title",
|
|
|
- Tag::Encrypted => "encrypted",
|
|
|
- Tag::Image(_, _) => "image",
|
|
|
- Tag::ZapGoal(_, _) => "goal",
|
|
|
- Tag::Expiration(_) => "expiration",
|
|
|
- Tag::ExternalContentId(_, _) => "i",
|
|
|
- Tag::Unknown(u, _) | Tag::UnknownJson(u, _) => u,
|
|
|
- Tag::Empty => "",
|
|
|
+ Self::Event(_, _, _) => "e",
|
|
|
+ Self::PubKey(_, _, _) => "p",
|
|
|
+ Self::Relay(_, _) => "r",
|
|
|
+ Self::Hashtag(_) => "t",
|
|
|
+ Self::Title(_) => "title",
|
|
|
+ Self::Encrypted => "encrypted",
|
|
|
+ Self::Image(_, _) => "image",
|
|
|
+ Self::ZapGoal(_, _) => "goal",
|
|
|
+ Self::Expiration(_) => "expiration",
|
|
|
+ Self::ExternalContentId(_, _) => "i",
|
|
|
+ Self::Unknown(u, _) | Self::UnknownJson(u, _) => u,
|
|
|
+ Self::Empty => "",
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Get the indexable value of the tag
|
|
|
pub fn get_indexable_value(&self) -> Option<TagValue> {
|
|
|
match self {
|
|
|
- Tag::Event(event, _, _) => Some(TagValue::Id(event.clone())),
|
|
|
- Tag::PubKey(key, _, _) => Some(TagValue::Id(key.clone())),
|
|
|
- Tag::ExternalContentId(id, _) => Some(TagValue::String(id.clone())),
|
|
|
- Tag::Hashtag(content) | Tag::Title(content) => Some(TagValue::String(content.clone())),
|
|
|
- Tag::Relay((_, url), _) => Some(TagValue::String(url.to_string())),
|
|
|
- Tag::Unknown(_, args) => {
|
|
|
+ Self::Event(event, _, _) => Some(TagValue::Id(event.clone())),
|
|
|
+ Self::PubKey(key, _, _) => Some(TagValue::Id(key.clone())),
|
|
|
+ Self::ExternalContentId(id, _) => Some(TagValue::String(id.clone())),
|
|
|
+ Self::Hashtag(content) | Self::Title(content) => {
|
|
|
+ Some(TagValue::String(content.clone()))
|
|
|
+ }
|
|
|
+ Self::Relay(url, _) => Some(TagValue::String(url.to_string())),
|
|
|
+ Self::Unknown(_, args) => {
|
|
|
let value = args.first().cloned().unwrap_or_default();
|
|
|
Some(
|
|
|
value
|
|
@@ -154,9 +188,9 @@ impl Tag {
|
|
|
.unwrap_or_else(|_| TagValue::String(value)),
|
|
|
)
|
|
|
}
|
|
|
- Tag::ZapGoal(event, _) => Some(TagValue::Id(event.clone())),
|
|
|
- Tag::Image(image_url, _) => Some(TagValue::String(image_url.to_string())),
|
|
|
- Tag::UnknownJson(_, _) | Tag::Empty | Tag::Encrypted | Tag::Expiration(_) => None,
|
|
|
+ Self::ZapGoal(event, _) => Some(TagValue::Id(event.clone())),
|
|
|
+ Self::Image(image_url, _) => Some(TagValue::String(image_url.to_string())),
|
|
|
+ Self::UnknownJson(_, _) | Self::Empty | Self::Encrypted | Self::Expiration(_) => None,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -171,86 +205,7 @@ impl Tag {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl ser::Serialize for Tag {
|
|
|
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
- where
|
|
|
- S: Serializer,
|
|
|
- {
|
|
|
- let mut seq = serializer.serialize_seq(Some(2))?;
|
|
|
- seq.serialize_element(self.get_identifier())?;
|
|
|
-
|
|
|
- match self {
|
|
|
- Tag::Encrypted => {}
|
|
|
- Tag::Expiration(date) => {
|
|
|
- seq.serialize_element(&date.timestamp().to_string())?;
|
|
|
- }
|
|
|
- Tag::Event(event, relayer_url, marker) => {
|
|
|
- seq.serialize_element(&event.to_string())?;
|
|
|
- if let Some((_, relayer)) = relayer_url {
|
|
|
- seq.serialize_element(&relayer)?;
|
|
|
- if let Some(marker) = &marker {
|
|
|
- seq.serialize_element(&marker.to_string())?;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::ZapGoal(event, relayer_url) => {
|
|
|
- seq.serialize_element(&event.to_string())?;
|
|
|
- if let Some((_, relayer)) = relayer_url {
|
|
|
- seq.serialize_element(&relayer)?;
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::ExternalContentId(content, url) => {
|
|
|
- seq.serialize_element(content)?;
|
|
|
- if let Some(url) = url {
|
|
|
- seq.serialize_element(url)?;
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::Hashtag(content) | Tag::Title(content) => {
|
|
|
- seq.serialize_element(content)?;
|
|
|
- }
|
|
|
- Tag::PubKey(key, relayer_url, pet_name) => {
|
|
|
- seq.serialize_element(&key.to_string())?;
|
|
|
- if let Some((_, relayer)) = relayer_url {
|
|
|
- seq.serialize_element(&relayer)?;
|
|
|
- if let Some(pet_name) = &pet_name {
|
|
|
- seq.serialize_element(pet_name)?;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::Relay((_, url), access) => {
|
|
|
- seq.serialize_element(url)?;
|
|
|
-
|
|
|
- if let Some(access) = match access {
|
|
|
- RelayAccessType::Read => Some("read"),
|
|
|
- RelayAccessType::Write => Some("write"),
|
|
|
- RelayAccessType::Both => None,
|
|
|
- } {
|
|
|
- seq.serialize_element(access)?;
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::UnknownJson(_, extra) => {
|
|
|
- for extra in extra.iter() {
|
|
|
- seq.serialize_element(extra)?;
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::Unknown(_, extra) => {
|
|
|
- for extra in extra.iter() {
|
|
|
- seq.serialize_element(extra)?;
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::Image(image, dimensions) => {
|
|
|
- seq.serialize_element(image)?;
|
|
|
- if let Some(dimensions) = dimensions {
|
|
|
- seq.serialize_element(dimensions)?;
|
|
|
- }
|
|
|
- }
|
|
|
- Tag::Empty => unreachable!(),
|
|
|
- }
|
|
|
- seq.end()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl<'de> Deserialize<'de> for Tag {
|
|
|
+impl<'de> Deserialize<'de> for TagType {
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
where
|
|
|
D: Deserializer<'de>,
|
|
@@ -263,7 +218,7 @@ impl<'de> Deserialize<'de> for Tag {
|
|
|
{
|
|
|
// If any of the parts is not a string, do not attempt to parse it
|
|
|
// and instead return as a Tag::UnknownJson
|
|
|
- return Ok(Tag::UnknownJson(
|
|
|
+ return Ok(Self::UnknownJson(
|
|
|
parts.pop_front().unwrap_or_default().to_string(),
|
|
|
parts.into_iter().collect(),
|
|
|
));
|
|
@@ -275,35 +230,44 @@ impl<'de> Deserialize<'de> for Tag {
|
|
|
.collect();
|
|
|
|
|
|
match parts.len() {
|
|
|
- 0 => return Ok(Tag::Empty),
|
|
|
+ 0 => return Ok(Self::Empty),
|
|
|
1 => {
|
|
|
let tag_name = parts.pop_front().unwrap_or_default();
|
|
|
if tag_name.as_str() == "encrypted" {
|
|
|
- return Ok(Tag::Encrypted);
|
|
|
+ return Ok(Self::Encrypted);
|
|
|
}
|
|
|
- return Ok(Tag::Unknown(tag_name, vec![]));
|
|
|
+ return Ok(Self::Unknown(tag_name, vec![]));
|
|
|
}
|
|
|
_ => {}
|
|
|
}
|
|
|
|
|
|
let tag_type = parts.pop_front().unwrap_or_default();
|
|
|
- let default = Tag::Unknown(tag_type.clone(), parts.clone().into());
|
|
|
+ let default = Self::Unknown(tag_type.clone(), parts.clone().into());
|
|
|
|
|
|
let tag: Result<_, D::Error> = match tag_type.as_str() {
|
|
|
"e" | "goal" | "p" => parts
|
|
|
.pop_front()
|
|
|
.ok_or_else::<D::Error, _>(|| de::Error::custom("missing argument"))
|
|
|
- .and_then(|id| id.parse().map_err(de::Error::custom))
|
|
|
- .map(|id| {
|
|
|
- let relayer_url = parts.pop_front().map(|value| (value.parse().ok(), value));
|
|
|
+ .and_then(|input| {
|
|
|
+ let mut id = [0u8; 32];
|
|
|
+ hex::decode_to_slice(input, &mut id)
|
|
|
+ .map_err(de::Error::custom)
|
|
|
+ .map(|_| Id(id))
|
|
|
+ })
|
|
|
+ .and_then(|id| {
|
|
|
+ let relayer_url = parts
|
|
|
+ .pop_front()
|
|
|
+ .map(|value| value.parse::<Url>())
|
|
|
+ .transpose()
|
|
|
+ .map_err(de::Error::custom);
|
|
|
|
|
|
let extra = parts.pop_front();
|
|
|
- match tag_type.as_str() {
|
|
|
- "e" => Tag::Event(id, relayer_url, extra.map(|x| x.as_str().into())),
|
|
|
- "goal" => Tag::ZapGoal(id, relayer_url),
|
|
|
- "p" => Tag::PubKey(id, relayer_url, extra),
|
|
|
+ relayer_url.map(|relayer_url| match tag_type.as_str() {
|
|
|
+ "e" => Self::Event(id, relayer_url, extra.map(|x| x.as_str().into())),
|
|
|
+ "goal" => Self::ZapGoal(id, relayer_url),
|
|
|
+ "p" => Self::PubKey(id, relayer_url, extra),
|
|
|
_ => unreachable!(),
|
|
|
- }
|
|
|
+ })
|
|
|
}),
|
|
|
"expiration" => {
|
|
|
let timestamp = parts
|
|
@@ -313,16 +277,16 @@ impl<'de> Deserialize<'de> for Tag {
|
|
|
.map_err(|_| de::Error::custom("invalid timestamp"))?;
|
|
|
|
|
|
DateTime::<Utc>::from_timestamp(timestamp, 0)
|
|
|
- .map(Tag::Expiration)
|
|
|
+ .map(Self::Expiration)
|
|
|
.ok_or_else(|| de::Error::custom("invalid timestamp"))
|
|
|
}
|
|
|
"title" => parts
|
|
|
.pop_front()
|
|
|
- .map(Tag::Title)
|
|
|
+ .map(Self::Title)
|
|
|
.ok_or_else(|| de::Error::custom("missing argument")),
|
|
|
"t" => parts
|
|
|
.pop_front()
|
|
|
- .map(Tag::Hashtag)
|
|
|
+ .map(Self::Hashtag)
|
|
|
.ok_or_else(|| de::Error::custom("missing argument")),
|
|
|
"i" => {
|
|
|
let external_id = parts
|
|
@@ -334,7 +298,7 @@ impl<'de> Deserialize<'de> for Tag {
|
|
|
.transpose();
|
|
|
|
|
|
if let Ok(external_id) = external_id {
|
|
|
- url.map(|url| Tag::ExternalContentId(external_id, url))
|
|
|
+ url.map(|url| Self::ExternalContentId(external_id, url))
|
|
|
} else {
|
|
|
Err(de::Error::custom("invalid external id"))
|
|
|
}
|
|
@@ -343,11 +307,7 @@ impl<'de> Deserialize<'de> for Tag {
|
|
|
let url = parts
|
|
|
.pop_front()
|
|
|
.ok_or_else::<D::Error, _>(|| de::Error::custom("missing url"))
|
|
|
- .and_then(|url| {
|
|
|
- url.parse::<Url>()
|
|
|
- .map_err(de::Error::custom)
|
|
|
- .map(|parsed_url| (Some(parsed_url), url))
|
|
|
- });
|
|
|
+ .and_then(|url| url.parse::<Url>().map_err(de::Error::custom));
|
|
|
|
|
|
let access = parts
|
|
|
.pop_front()
|
|
@@ -359,7 +319,7 @@ impl<'de> Deserialize<'de> for Tag {
|
|
|
.unwrap_or(Ok(RelayAccessType::Both));
|
|
|
|
|
|
if let Ok(url) = url {
|
|
|
- access.map(|access| Tag::Relay(url, access))
|
|
|
+ access.map(|access| TagType::Relay(url, access))
|
|
|
} else {
|
|
|
Err(de::Error::custom("invalid url"))
|
|
|
}
|
|
@@ -421,32 +381,33 @@ mod test {
|
|
|
]
|
|
|
.into_iter()
|
|
|
.map(|x| serde_json::from_value(x).unwrap())
|
|
|
- .collect::<Vec<Tag>>();
|
|
|
+ .collect::<Vec<TagType>>();
|
|
|
|
|
|
let expected = vec![
|
|
|
- Tag::Unknown("p".to_owned(), vec![String::from("d45a98f8988")]),
|
|
|
- Tag::PubKey(
|
|
|
+ TagType::Unknown("p".to_owned(), vec![String::from("d45a98f8988")]),
|
|
|
+ TagType::PubKey(
|
|
|
"d45a98f898820258a3313f5cb14f5fe8a9263437931ac6309f23ae0324833f39"
|
|
|
.parse()
|
|
|
.unwrap(),
|
|
|
None,
|
|
|
None,
|
|
|
),
|
|
|
- Tag::PubKey(
|
|
|
- "8fe53b37518e3dbe9bab26d912292001d8b882de9456b7b08b615f912dc8bf4a"
|
|
|
- .parse()
|
|
|
- .unwrap(),
|
|
|
- Some((None, "".to_owned())),
|
|
|
- Some("mention".to_owned()),
|
|
|
+ TagType::Unknown(
|
|
|
+ "p".to_owned(),
|
|
|
+ vec![
|
|
|
+ "8fe53b37518e3dbe9bab26d912292001d8b882de9456b7b08b615f912dc8bf4a".to_owned(),
|
|
|
+ "".to_owned(),
|
|
|
+ "mention".to_owned(),
|
|
|
+ ],
|
|
|
),
|
|
|
- Tag::Event(
|
|
|
+ TagType::Event(
|
|
|
"eb278e983fcedbb0d143c4250c879d078d037586c5dca8e1cf1a104f9846a460"
|
|
|
.parse()
|
|
|
.unwrap(),
|
|
|
None,
|
|
|
None,
|
|
|
),
|
|
|
- Tag::PubKey(
|
|
|
+ TagType::PubKey(
|
|
|
"2bda4f03446bc1c6ff00594e350a1e7ec57fb9cdc4a7b52694223d56ce0599e9"
|
|
|
.parse()
|
|
|
.unwrap(),
|
|
@@ -462,11 +423,8 @@ mod test {
|
|
|
fn test_relay() {
|
|
|
let json = json!(["r", "https://example.com/", "read"]);
|
|
|
assert_eq!(
|
|
|
- Tag::Relay(
|
|
|
- (
|
|
|
- Some("https://example.com".parse().expect("valid url")),
|
|
|
- "https://example.com/".to_owned()
|
|
|
- ),
|
|
|
+ TagType::Relay(
|
|
|
+ "https://example.com".parse().expect("valid url"),
|
|
|
RelayAccessType::Read
|
|
|
),
|
|
|
serde_json::from_value(json).expect("valid json"),
|
|
@@ -474,11 +432,8 @@ mod test {
|
|
|
|
|
|
let json = json!(["r", "https://example.com", "write"]);
|
|
|
assert_eq!(
|
|
|
- Tag::Relay(
|
|
|
- (
|
|
|
- Some("https://example.com".parse().expect("valid url")),
|
|
|
- "https://example.com".to_owned()
|
|
|
- ),
|
|
|
+ TagType::Relay(
|
|
|
+ "https://example.com".parse().expect("valid url"),
|
|
|
RelayAccessType::Write
|
|
|
),
|
|
|
serde_json::from_value(json).expect("valid json"),
|
|
@@ -486,11 +441,8 @@ mod test {
|
|
|
|
|
|
let json = json!(["r", "https://example.com"]);
|
|
|
assert_eq!(
|
|
|
- Tag::Relay(
|
|
|
- (
|
|
|
- Some("https://example.com".parse().expect("valid url")),
|
|
|
- "https://example.com".to_owned()
|
|
|
- ),
|
|
|
+ TagType::Relay(
|
|
|
+ "https://example.com".parse().expect("valid url"),
|
|
|
RelayAccessType::Both,
|
|
|
),
|
|
|
serde_json::from_value(json).expect("valid json"),
|
|
@@ -501,7 +453,7 @@ mod test {
|
|
|
fn test_relay_invalid_url() {
|
|
|
let json = json!(["r", "example.com", "read"]);
|
|
|
assert_eq!(
|
|
|
- Tag::Unknown(
|
|
|
+ TagType::Unknown(
|
|
|
"r".to_string(),
|
|
|
vec!["example.com".to_string(), "read".to_string()],
|
|
|
),
|
|
@@ -521,8 +473,8 @@ mod test {
|
|
|
];
|
|
|
|
|
|
for json in json {
|
|
|
- let tag: Tag = serde_json::from_value(json).expect("valid");
|
|
|
- assert!(matches!(tag, Tag::Unknown(_, _)));
|
|
|
+ let tag: TagType = serde_json::from_value(json).expect("valid");
|
|
|
+ assert!(matches!(tag, TagType::Unknown(_, _)));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -530,7 +482,7 @@ mod test {
|
|
|
fn test_relay_invalid_access() {
|
|
|
let json = json!(["r", "https://example.com", "invalid"]);
|
|
|
assert_eq!(
|
|
|
- Tag::Unknown(
|
|
|
+ TagType::Unknown(
|
|
|
"r".to_string(),
|
|
|
vec!["https://example.com".to_string(), "invalid".to_string()],
|
|
|
),
|
|
@@ -542,7 +494,7 @@ mod test {
|
|
|
fn hashtag() {
|
|
|
let json = json!(["t", "rust"]);
|
|
|
assert_eq!(
|
|
|
- Tag::Hashtag("rust".to_string()),
|
|
|
+ TagType::Hashtag("rust".to_string()),
|
|
|
serde_json::from_value(json).expect("valid json"),
|
|
|
);
|
|
|
}
|
|
@@ -551,7 +503,7 @@ mod test {
|
|
|
fn title() {
|
|
|
let json = json!(["title", "Rust"]);
|
|
|
assert_eq!(
|
|
|
- Tag::Title("Rust".to_string()),
|
|
|
+ TagType::Title("Rust".to_string()),
|
|
|
serde_json::from_value(json).expect("valid json"),
|
|
|
);
|
|
|
}
|
|
@@ -560,13 +512,13 @@ mod test {
|
|
|
fn external_content_id() {
|
|
|
let json = json!(["i", "123"]);
|
|
|
assert_eq!(
|
|
|
- serde_json::from_value::<Tag>(json).expect("valid"),
|
|
|
- Tag::ExternalContentId("123".to_string(), None,),
|
|
|
+ serde_json::from_value::<TagType>(json).expect("valid"),
|
|
|
+ TagType::ExternalContentId("123".to_string(), None,),
|
|
|
);
|
|
|
let json = json!(["i", "123", "https://example.com"]);
|
|
|
assert_eq!(
|
|
|
- serde_json::from_value::<Tag>(json).expect("valid"),
|
|
|
- Tag::ExternalContentId(
|
|
|
+ serde_json::from_value::<TagType>(json).expect("valid"),
|
|
|
+ TagType::ExternalContentId(
|
|
|
"123".to_string(),
|
|
|
Some("https://example.com".parse().expect("valid url"))
|
|
|
),
|
|
@@ -577,8 +529,8 @@ mod test {
|
|
|
fn external_content_id_invalid_args() {
|
|
|
let json = json!(["i", "123", "https://example.com", "unexpected"]);
|
|
|
assert_eq!(
|
|
|
- serde_json::from_value::<Tag>(json).expect("valid"),
|
|
|
- Tag::Unknown(
|
|
|
+ serde_json::from_value::<TagType>(json).expect("valid"),
|
|
|
+ TagType::Unknown(
|
|
|
"i".to_owned(),
|
|
|
vec![
|
|
|
"123".to_owned(),
|
|
@@ -592,15 +544,18 @@ mod test {
|
|
|
#[test]
|
|
|
fn encrypted_tag() {
|
|
|
let json = json!(["encrypted"]);
|
|
|
- assert_eq!(Tag::Encrypted, serde_json::from_value(json).expect("valid"));
|
|
|
+ assert_eq!(
|
|
|
+ TagType::Encrypted,
|
|
|
+ serde_json::from_value(json).expect("valid")
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn external_content_id_invalid_url() {
|
|
|
let json = json!(["i", "123", "facebook.com"]);
|
|
|
assert_eq!(
|
|
|
- serde_json::from_value::<Tag>(json).expect("valid"),
|
|
|
- Tag::Unknown(
|
|
|
+ serde_json::from_value::<TagType>(json).expect("valid"),
|
|
|
+ TagType::Unknown(
|
|
|
"i".to_owned(),
|
|
|
vec!["123".to_owned(), "facebook.com".to_owned()]
|
|
|
),
|
|
@@ -614,8 +569,8 @@ mod test {
|
|
|
"d45a98f898820258a3313f5cb14f5fe8a9263437931ac6309f23ae0324833f39"
|
|
|
]);
|
|
|
assert_eq!(
|
|
|
- serde_json::from_value::<Tag>(json).expect("valid"),
|
|
|
- Tag::ZapGoal(
|
|
|
+ serde_json::from_value::<TagType>(json).expect("valid"),
|
|
|
+ TagType::ZapGoal(
|
|
|
"d45a98f898820258a3313f5cb14f5fe8a9263437931ac6309f23ae0324833f39"
|
|
|
.parse()
|
|
|
.unwrap(),
|
|
@@ -628,8 +583,8 @@ mod test {
|
|
|
fn timestamp() {
|
|
|
let json = json!(["expiration", "1678682368"]);
|
|
|
assert_eq!(
|
|
|
- serde_json::from_value::<Tag>(json).expect("valid"),
|
|
|
- Tag::Expiration(DateTime::<Utc>::from_timestamp(1678682368, 0).unwrap()),
|
|
|
+ serde_json::from_value::<TagType>(json).expect("valid"),
|
|
|
+ TagType::Expiration(DateTime::<Utc>::from_timestamp(1678682368, 0).unwrap()),
|
|
|
);
|
|
|
}
|
|
|
}
|