summaryrefslogtreecommitdiff
path: root/src/chunkmeta.rs
blob: e2fa9b35dd6357699b2ce025dcd718dc319060c2 (plain)
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
//! Metadata about a chunk.

use crate::label::Label;
use serde::{Deserialize, Serialize};
use std::default::Default;
use std::str::FromStr;

/// Metadata about chunks.
///
/// We a single piece of metadata about chunks, in addition to its
/// identifier: a label assigned by the client. Currently, this is a
/// [SHA256][] checksum of the chunk content.
///
/// For HTTP, the metadata will be serialised as a JSON object, like this:
///
/// ~~~json
/// {
///     "label": "09ca7e4eaa6e8ae9c7d261167129184883644d07dfba7cbfbc4c8a2e08360d5b",
/// }
/// ~~~
///
/// This module provides functions for serializing to and from JSON.
/// The JSON doesn't have to include the fields for generations if
/// they're not needed, although when serialized, they will always be
/// there.
///
/// After chunk metadata is created, it is immutable.
///
/// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
/// [SHA256]: https://en.wikipedia.org/wiki/SHA-2
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct ChunkMeta {
    label: String,
}

impl ChunkMeta {
    /// Create a new data chunk.
    ///
    /// Data chunks are not for generations.
    pub fn new(label: &Label) -> Self {
        ChunkMeta {
            label: label.serialize(),
        }
    }

    /// The label of the content of the chunk.
    ///
    /// The caller should not interpret the label in any way. It
    /// happens to be a SHA256 of the cleartext contents of the
    /// checksum for now, but that _will_ change in the future.
    pub fn label(&self) -> &str {
        &self.label
    }

    /// Serialize from a textual JSON representation.
    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
        serde_json::from_str(json)
    }

    /// Serialize as JSON.
    pub fn to_json(&self) -> String {
        serde_json::to_string(self).unwrap()
    }

    /// Serialize as JSON, as a byte vector.
    pub fn to_json_vec(&self) -> Vec<u8> {
        self.to_json().as_bytes().to_vec()
    }
}

impl FromStr for ChunkMeta {
    type Err = serde_json::error::Error;

    /// Parse a JSON representation metadata.
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        serde_json::from_str(s)
    }
}

#[cfg(test)]
mod test {
    use super::{ChunkMeta, Label};

    #[test]
    fn new_creates_data_chunk() {
        let sum = Label::sha256(b"abcdef");
        let meta = ChunkMeta::new(&sum);
        assert_eq!(meta.label(), sum.serialize());
    }

    #[test]
    fn new_generation_creates_generation_chunk() {
        let sum = Label::sha256(b"abcdef");
        let meta = ChunkMeta::new(&sum);
        assert_eq!(meta.label(), sum.serialize());
    }

    #[test]
    fn data_chunk_from_json() {
        let meta: ChunkMeta = r#"{"label": "abcdef"}"#.parse().unwrap();
        assert_eq!(meta.label(), "abcdef");
    }

    #[test]
    fn generation_chunk_from_json() {
        let meta: ChunkMeta =
            r#"{"label": "abcdef", "generation": true, "ended": "2020-09-17T08:17:13+03:00"}"#
                .parse()
                .unwrap();

        assert_eq!(meta.label(), "abcdef");
    }

    #[test]
    fn generation_json_roundtrip() {
        let sum = Label::sha256(b"abcdef");
        let meta = ChunkMeta::new(&sum);
        let json = serde_json::to_string(&meta).unwrap();
        let meta2 = serde_json::from_str(&json).unwrap();
        assert_eq!(meta, meta2);
    }

    #[test]
    fn data_json_roundtrip() {
        let sum = Label::sha256(b"abcdef");
        let meta = ChunkMeta::new(&sum);
        let json = meta.to_json_vec();
        let meta2 = serde_json::from_slice(&json).unwrap();
        assert_eq!(meta, meta2);
        assert_eq!(meta.to_json_vec(), meta2.to_json_vec());
    }
}