twine_lib/twine/
any_twine.rs

1use super::TwineBlock;
2use super::{Strand, Tixel, Twine};
3use crate::as_cid::AsCid;
4use crate::crypto::{assert_cid, get_hasher};
5use crate::errors::VerificationError;
6use crate::twine::Tagged;
7use crate::Cid;
8use core::str;
9use ipld_core::codec::Codec;
10use multihash_codetable::{Code, Multihash};
11use serde_ipld_dagjson::codec::DagJsonCodec;
12use std::convert::TryFrom;
13/// Structs and traits common to both Chain's and Pulses
14use std::fmt::Display;
15
16/// A type that can be either a Strand or a Tixel
17///
18/// Useful for dealing with the fundamental data structures
19/// without needing to know which one you're dealing with.
20#[derive(Debug, PartialEq, Eq, Hash, Clone)]
21pub enum AnyTwine {
22  /// A [`Strand`]
23  Strand(Strand),
24  /// A [`Tixel`]
25  Tixel(Tixel),
26}
27
28impl AnyTwine {
29  /// Get the CID
30  pub fn cid(&self) -> Cid {
31    match self {
32      Self::Strand(s) => s.cid(),
33      Self::Tixel(t) => t.cid(),
34    }
35  }
36
37  /// Get the Strand CID
38  ///
39  /// If this is a Tixel, it will return the CID of its strand property
40  /// If this is a Strand, it will return its own CID
41  pub fn strand_cid(&self) -> Cid {
42    match self {
43      Self::Strand(s) => s.cid(),
44      Self::Tixel(t) => t.strand_cid(),
45    }
46  }
47
48  /// Get the hash
49  pub fn content_hash(&self) -> Multihash {
50    match self {
51      Self::Strand(s) => s.content_hash(),
52      Self::Tixel(t) => t.content_hash(),
53    }
54  }
55
56  /// Is this twine a Strand?
57  pub fn is_strand(&self) -> bool {
58    matches!(self, Self::Strand(_))
59  }
60
61  /// Is this twine a Tixel?
62  pub fn is_tixel(&self) -> bool {
63    matches!(self, Self::Tixel(_))
64  }
65
66  /// Unwrap a Tixel or panic
67  pub fn unwrap_tixel(&self) -> Tixel {
68    match self {
69      Self::Tixel(t) => t.clone(),
70      _ => panic!("Expected Tixel, found Strand"),
71    }
72  }
73
74  /// Unwrap a Strand or panic
75  pub fn unwrap_strand(&self) -> Strand {
76    match self {
77      Self::Strand(s) => s.clone(),
78      _ => panic!("Expected Strand, found Tixel"),
79    }
80  }
81
82  fn assert_cid(&self, expected: &Cid) -> Result<(), VerificationError> {
83    assert_cid(expected, &self.cid())
84  }
85
86  /// Get a list of AnyTwines from a json string formatted as a tagged DAG-JSON array
87  pub fn from_tagged_dag_json_array<S: AsRef<str>>(
88    json: S,
89  ) -> Result<Vec<Self>, VerificationError> {
90    let arr: Vec<Tagged<Self>> = DagJsonCodec::decode_from_slice(json.as_ref().as_bytes())?;
91    Ok(arr.into_iter().map(|t| t.unpack()).collect())
92  }
93}
94
95impl PartialEq<Tixel> for AnyTwine {
96  fn eq(&self, other: &Tixel) -> bool {
97    match self {
98      Self::Tixel(t) => *t == *other,
99      _ => false,
100    }
101  }
102}
103
104impl PartialEq<AnyTwine> for Tixel {
105  fn eq(&self, other: &AnyTwine) -> bool {
106    other == self
107  }
108}
109
110impl PartialEq<Strand> for AnyTwine {
111  fn eq(&self, other: &Strand) -> bool {
112    match self {
113      Self::Strand(s) => *s == *other,
114      _ => false,
115    }
116  }
117}
118
119impl PartialEq<AnyTwine> for Strand {
120  fn eq(&self, other: &AnyTwine) -> bool {
121    other == self
122  }
123}
124
125impl TryFrom<AnyTwine> for Tixel {
126  type Error = VerificationError;
127
128  fn try_from(t: AnyTwine) -> Result<Self, Self::Error> {
129    match t {
130      AnyTwine::Tixel(t) => Ok(t),
131      _ => Err(VerificationError::WrongType {
132        expected: "Tixel".to_string(),
133        found: "Strand".to_string(),
134      }),
135    }
136  }
137}
138
139impl TryFrom<AnyTwine> for Strand {
140  type Error = VerificationError;
141
142  fn try_from(s: AnyTwine) -> Result<Self, Self::Error> {
143    match s {
144      AnyTwine::Strand(s) => Ok(s),
145      _ => Err(VerificationError::WrongType {
146        expected: "Strand".to_string(),
147        found: "Tixel".to_string(),
148      }),
149    }
150  }
151}
152
153impl From<Strand> for AnyTwine {
154  fn from(s: Strand) -> Self {
155    Self::Strand(s)
156  }
157}
158
159impl From<Twine> for AnyTwine {
160  fn from(t: Twine) -> Self {
161    Self::Tixel(t.tixel().clone())
162  }
163}
164
165impl From<Tixel> for AnyTwine {
166  fn from(t: Tixel) -> Self {
167    Self::Tixel(t)
168  }
169}
170
171impl AsCid for AnyTwine {
172  fn as_cid(&self) -> &Cid {
173    match self {
174      Self::Strand(s) => s.as_cid(),
175      Self::Tixel(t) => t.as_cid(),
176    }
177  }
178}
179
180impl From<AnyTwine> for Cid {
181  fn from(t: AnyTwine) -> Self {
182    match t {
183      AnyTwine::Strand(s) => s.cid(),
184      AnyTwine::Tixel(t) => t.cid(),
185    }
186  }
187}
188
189impl TwineBlock for AnyTwine {
190  fn cid(&self) -> &Cid {
191    self.as_cid()
192  }
193  /// Decode from DAG-JSON
194  ///
195  /// DAG-JSON is a JSON object with a CID and a data object. CID is verified.
196  fn from_tagged_dag_json<S: Display>(json: S) -> Result<Self, VerificationError> {
197    let str_json = json.to_string();
198    // assume it's a Tixel first
199    let tixel = Tixel::from_tagged_dag_json(&str_json);
200    if tixel.is_ok() {
201      return Ok(Self::Tixel(tixel.unwrap().into()));
202    }
203    // assume it's a Strand next
204    let strand = Strand::from_tagged_dag_json(&str_json);
205    if strand.is_ok() {
206      return Ok(Self::Strand(strand.unwrap().into()));
207    }
208    let msg = format!(
209      "Undecodable structure:\n{}\n{}",
210      tixel.err().unwrap(),
211      strand.err().unwrap()
212    );
213    Err(VerificationError::InvalidTwineFormat(msg))
214  }
215
216  /// Decode from raw bytes without checking CID
217  fn from_bytes_unchecked(hasher: Code, bytes: Vec<u8>) -> Result<Self, VerificationError> {
218    let tixel = Tixel::from_bytes_unchecked(hasher, bytes.clone());
219    if tixel.is_ok() {
220      return Ok(Self::Tixel(tixel.unwrap().into()));
221    }
222    let strand = Strand::from_bytes_unchecked(hasher, bytes);
223    if strand.is_ok() {
224      return Ok(Self::Strand(strand.unwrap().into()));
225    }
226    let msg = format!(
227      "Undecodable structure because:\n{}\n{}",
228      tixel.err().unwrap(),
229      strand.err().unwrap()
230    );
231    Err(VerificationError::InvalidTwineFormat(msg))
232  }
233
234  /// Decode from a Block
235  ///
236  /// A block is a cid and DAG-CBOR bytes. CID is verified.
237  fn from_block<T: AsRef<[u8]>>(cid: Cid, bytes: T) -> Result<Self, VerificationError> {
238    let hasher = get_hasher(&cid)?;
239    let twine = Self::from_bytes_unchecked(hasher, bytes.as_ref().to_vec())?;
240    twine.assert_cid(&cid)?;
241    Ok(twine)
242  }
243
244  /// Encode to DAG-JSON
245  fn tagged_dag_json(&self) -> String {
246    match self {
247      Self::Strand(s) => s.tagged_dag_json(),
248      Self::Tixel(t) => t.tagged_dag_json(),
249    }
250  }
251
252  /// Encode to raw bytes
253  fn bytes(&self) -> std::sync::Arc<[u8]> {
254    match self {
255      Self::Strand(s) => s.bytes(),
256      Self::Tixel(t) => t.bytes(),
257    }
258  }
259
260  fn content_bytes(&self) -> std::sync::Arc<[u8]> {
261    match self {
262      Self::Strand(s) => s.content_bytes(),
263      Self::Tixel(t) => t.content_bytes(),
264    }
265  }
266}
267
268impl Display for AnyTwine {
269  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270    match self {
271      Self::Strand(s) => write!(f, "{}", s),
272      Self::Tixel(t) => write!(f, "{}", t),
273    }
274  }
275}