nu_utils/
nu_cow.rs

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}