1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

mod de;
mod error;
mod path;
mod path_segment;
pub mod selector;
mod ser;

#[cfg(feature = "json")]
pub mod json;

#[macro_use]
mod macros;

pub use self::error::Error;
pub use path::Path;
pub use path_segment::PathSegment;

use cid::Cid;
use encoding::{from_slice, to_vec, Cbor};
use ser::Serializer;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::BTreeMap;

/// Represents IPLD data structure used when serializing and deserializing data.
#[derive(Debug, Clone, PartialEq)]
pub enum Ipld {
    /// Represents a null value.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!(null);
    /// ```
    Null,

    /// Represents a boolean value.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!(true);
    /// ```
    Bool(bool),

    /// Represents a signed integer value.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!(28);
    /// ```
    Integer(i128),

    /// Represents a floating point value.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!(8.5);
    /// ```
    Float(f64),

    /// Represents a String.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!("string");
    /// ```
    String(String),

    /// Represents Bytes.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!(Bytes(vec![0x98, 0x8, 0x2a, 0xff]));
    /// ```
    Bytes(Vec<u8>),

    /// Represents List of IPLD objects.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!([1, "string", null]);
    /// ```
    List(Vec<Ipld>),

    /// Represents a map of strings to Ipld objects.
    ///
    /// ```no_run
    /// # use forest_ipld::ipld;
    /// let v = ipld!({"key": "value", "bool": true});
    /// ```
    Map(BTreeMap<String, Ipld>),

    /// Represents a link to another piece of data through a content identifier (`Cid`).
    /// Using `ipld` macro, can wrap Cid with Link to be explicit of Link type, or let it resolve.
    ///
    /// ```
    /// # use forest_ipld::ipld;
    /// # use cid::Cid;
    /// let cid: Cid = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n".parse().unwrap();
    /// let v1 = ipld!(Link(cid.clone()));
    /// let v2 = ipld!(cid);
    /// assert_eq!(v1, v2);
    /// ```
    Link(Cid),
}

impl Ipld {
    pub(crate) fn lookup_segment(&self, segment: &PathSegment) -> Option<&Self> {
        match self {
            Self::Map(map) => match segment {
                PathSegment::String(s) => map.get(s),
                PathSegment::Int(i) => map.get(&i.to_string()),
            },
            Self::List(list) => list.get(segment.to_index()?),
            _ => None,
        }
    }
}

impl Cbor for Ipld {}

/// Convert any object into an IPLD object
pub fn to_ipld<T>(ipld: T) -> Result<Ipld, Error>
where
    T: Serialize,
{
    ipld.serialize(Serializer)
}

/// Convert a `Ipld` structure into a type `T`
/// Currently converts using a byte buffer with serde_cbor
pub fn from_ipld<T>(value: &Ipld) -> Result<T, String>
where
    T: DeserializeOwned,
{
    // TODO update to not go through byte buffer to convert
    // There is a good amount of overhead for this (having to implement serde::Deserializer)
    // for Ipld, but possible. The benefit isn't worth changing yet since if the value is not
    // passed by reference as needed by HAMT, then the values will have to be cloned.
    let buf = to_vec(value).map_err(|e| e.to_string())?;
    from_slice(buf.as_slice()).map_err(|e| e.to_string())
}