1use std::fmt::Debug;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Serialize)]
6#[serde(untagged)]
7#[allow(dead_code)]
8pub enum NuCow<B, O> {
9 Borrowed(B),
10 Owned(O),
11}
12
13impl<B, O> PartialEq for NuCow<B, O>
14where
15 O: std::cmp::PartialEq<O>,
16 B: std::cmp::PartialEq<B>,
17 O: std::cmp::PartialEq<B>,
18{
19 fn eq(&self, other: &Self) -> bool {
20 match (&self, &other) {
21 (NuCow::Owned(o), NuCow::Borrowed(b)) | (NuCow::Borrowed(b), NuCow::Owned(o)) => o == b,
22 (NuCow::Borrowed(lhs), NuCow::Borrowed(rhs)) => lhs == rhs,
23 (NuCow::Owned(lhs), NuCow::Owned(rhs)) => lhs == rhs,
24 }
25 }
26}
27
28impl<B, O> Debug for NuCow<B, O>
29where
30 B: Debug,
31 O: Debug,
32{
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 if f.alternate() {
35 match self {
36 Self::Borrowed(b) => f.debug_tuple("Borrowed").field(b).finish(),
37 Self::Owned(o) => f.debug_tuple("Owned").field(o).finish(),
38 }
39 } else {
40 match self {
41 Self::Borrowed(b) => <B as Debug>::fmt(b, f),
42 Self::Owned(o) => <O as Debug>::fmt(o, f),
43 }
44 }
45 }
46}
47
48impl<B, O> Clone for NuCow<B, O>
49where
50 B: Clone,
51 O: Clone,
52{
53 fn clone(&self) -> Self {
54 match self {
55 Self::Borrowed(b) => Self::Borrowed(b.clone()),
56 Self::Owned(o) => Self::Owned(o.clone()),
57 }
58 }
59}
60
61impl<'de, B, O> Deserialize<'de> for NuCow<B, O>
62where
63 O: Deserialize<'de>,
64{
65 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66 where
67 D: serde::Deserializer<'de>,
68 {
69 <O as Deserialize>::deserialize(deserializer).map(Self::Owned)
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn static_to_dynamic_roundtrip() {
79 type Strings = NuCow<&'static [&'static str], Vec<String>>;
80
81 let src = ["hello", "world", "!"].as_slice();
82 let json = serde_json::to_string(&Strings::Borrowed(src)).unwrap();
83 let dst: Strings = serde_json::from_str(&json).unwrap();
84
85 let Strings::Owned(dst) = dst else {
86 panic!("Expected Owned variant");
87 };
88
89 for (&s, d) in src.iter().zip(&dst) {
90 assert_eq!(s, d.as_str())
91 }
92 }
93}