use serde::{Deserialize, Deserializer, Serialize, Serializer};
use url::Url;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OneOrMany<T>(Vec<T>);
impl<T> OneOrMany<T> {
#[must_use]
pub const fn new() -> Self {
Self(Vec::new())
}
pub fn one(value: T) -> Self {
Self(vec![value])
}
#[must_use]
pub const fn many(values: Vec<T>) -> Self {
Self(values)
}
#[must_use]
pub fn as_slice(&self) -> &[T] {
&self.0
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.0
}
#[must_use]
pub fn into_vec(self) -> Vec<T> {
self.0
}
pub fn push(&mut self, value: T) {
self.0.push(value);
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.0.iter()
}
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
self.0.iter_mut()
}
#[must_use]
pub fn first(&self) -> Option<&T> {
self.0.first()
}
#[must_use]
pub fn last(&self) -> Option<&T> {
self.0.last()
}
}
impl<T> Default for OneOrMany<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> IntoIterator for OneOrMany<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a, T> IntoIterator for &'a OneOrMany<T> {
type Item = &'a T;
type IntoIter = core::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a, T> IntoIterator for &'a mut OneOrMany<T> {
type Item = &'a mut T;
type IntoIter = core::slice::IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter_mut()
}
}
impl<T> From<T> for OneOrMany<T> {
fn from(value: T) -> Self {
Self::one(value)
}
}
impl<T> From<Vec<T>> for OneOrMany<T> {
fn from(values: Vec<T>) -> Self {
Self::many(values)
}
}
impl<T> FromIterator<T> for OneOrMany<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self(iter.into_iter().collect())
}
}
impl<T: Serialize> Serialize for OneOrMany<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self.0.as_slice() {
[only] => only.serialize(serializer),
many => many.serialize(serializer),
}
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for OneOrMany<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(untagged)]
enum Either<T> {
Many(Vec<T>),
One(T),
}
match Option::<Either<T>>::deserialize(deserializer)? {
None => Ok(Self::new()),
Some(Either::One(value)) => Ok(Self::one(value)),
Some(Either::Many(values)) => Ok(Self::many(values)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UrlOr<T> {
Url(Url),
Object(T),
}
impl<T> UrlOr<T> {
pub fn url(&self) -> Option<&Url>
where
T: HasId,
{
match self {
Self::Url(u) => Some(u),
Self::Object(o) => o.id(),
}
}
#[must_use]
pub const fn as_object(&self) -> Option<&T> {
match self {
Self::Object(o) => Some(o),
Self::Url(_) => None,
}
}
}
pub trait HasId {
fn id(&self) -> Option<&Url>;
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Public;
impl Public {
pub const URI: &'static str = "https://www.w3.org/ns/activitystreams#Public";
pub const CURIE: &'static str = "as:Public";
pub const BARE: &'static str = "Public";
#[must_use]
pub fn is_public(value: &str) -> bool {
matches!(value, Self::URI | Self::CURIE | Self::BARE)
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use serde_json::json;
use super::*;
#[test]
fn one_or_many_single_value_serialises_as_bare_value() {
let value = OneOrMany::one("hello".to_owned());
let json = serde_json::to_value(&value).expect("serialise");
assert_eq!(json, json!("hello"));
let back: OneOrMany<String> = serde_json::from_value(json).expect("deserialise");
assert_eq!(back, value);
}
#[test]
fn one_or_many_multi_value_serialises_as_array() {
let value = OneOrMany::many(vec![1_i32, 2, 3]);
let json = serde_json::to_value(&value).expect("serialise");
assert_eq!(json, json!([1, 2, 3]));
let back: OneOrMany<i32> = serde_json::from_value(json).expect("deserialise");
assert_eq!(back, value);
}
#[test]
fn one_or_many_empty_serialises_as_empty_array() {
let value: OneOrMany<String> = OneOrMany::new();
let json = serde_json::to_value(&value).expect("serialise");
assert_eq!(json, json!([]));
}
#[test]
fn one_or_many_deserialises_null_as_empty() {
let back: OneOrMany<String> =
serde_json::from_value(json!(null)).expect("null must deserialise");
assert!(back.is_empty(), "null must yield an empty collection");
}
#[test]
fn one_or_many_collects_from_iterator() {
let value: OneOrMany<i32> = [1, 2, 3].into_iter().collect();
assert_eq!(value.as_slice(), &[1, 2, 3]);
}
#[test]
fn url_or_deserializes_bare_url() {
#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct Dummy {
id: String,
}
let value: UrlOr<Dummy> = serde_json::from_value(json!("https://example/1")).unwrap();
assert!(matches!(value, UrlOr::Url(_)));
}
#[test]
fn url_or_deserializes_object() {
#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct Dummy {
id: String,
}
let value: UrlOr<Dummy> = serde_json::from_value(json!({ "id": "abc" })).unwrap();
assert!(matches!(value, UrlOr::Object(Dummy { .. })));
}
#[test]
fn public_is_recognised_in_all_spellings() {
assert!(Public::is_public(Public::URI));
assert!(Public::is_public(Public::CURIE));
assert!(Public::is_public(Public::BARE));
assert!(!Public::is_public("https://example/actor"));
}
}