aboutsummaryrefslogtreecommitdiff
path: root/src/record/mod.rs
blob: 1963ce1580077955ff30f54a39f2ce93868f3893 (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
//! Lockchain record handling module
//!
//! A record is a set of key-value store values with a header
//!

pub mod version;
pub use self::version::Version;

use std::collections::BTreeMap;
use chrono::{DateTime, Local};


/// A generic payload for a record
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub enum Payload {
    Text(String),
    Boolean(bool),
    Number(i64),
    BTreeMap(BTreeMap<String, Payload>),
}

/// Describes the header of a record file
///
/// This part of the record should not be considered safe as it is
/// serialised and cached multiple times.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Header {
    /// The name of this record
    pub name: String,

    /// The primary category used for sorting
    pub category: String,

    /// A dynamic collection of fields. User-configurable
    /// In most cases this is where website URLs can be stored
    pub fields: BTreeMap<String, Payload>,

    /// Dynamic tagging (like categories but not exclusive)
    pub tags: Vec<String>,

    /// Record creation date (fixed)
    pub date_created: DateTime<Local>,

    /// Date of last update
    pub date_updated: DateTime<Local>,
}

/// Represents a record inside lockchain
///
/// A record consists of a header and a body. The body has built-in
/// versioning. The different versions are then flattened to create the
/// latest stage of a record which is exposed to the outside.
#[derive(Debug, Serialize, Deserialize)]
pub struct Record {
    /// The header for this record
    pub header: Header,

    /// The versioned record body
    pub body: Vec<Version>,
}

impl Header {
    /// Create a new header with a name of a category
    pub fn new(name: String, category: String) -> Header {
        let me = Header {
            name: name,
            category: category,
            fields: BTreeMap::new(),
            tags: Vec::new(),
            date_created: Local::now(),
            date_updated: Local::now(),
        };

        return me;
    }
}

impl PartialEq for Record {
    fn eq(&self, other: &Record) -> bool {
        self.header == other.header
    }
}

impl Record {
    /// Create a new record
    pub fn new(name: &str, category: &str) -> Record {
        return Record {
            header: Header::new(String::from(name), String::from(category)),
            body: Vec::new(),
        };
    }

    /// Return a new version to work on externally
    pub fn start_version(&self) -> Version {
        let num = self.body.len(); // Versions are continually numbered
        return Version::new(num as u64);
    }

    /// Apply a version to the current record
    pub fn apply_version(&mut self, ver: Version) {
        self.body.push(ver);
    }

    /// Flatten all versions down and return a map of *current* data
    /// stored in this record.
    /// 
    /// Note: currently the data presented in this map is not sorted
    /// in the way that the developer intended (insertion-order)
    pub fn get_data(&self) -> BTreeMap<String, Payload> {
        let mut first: Version = self.body[0].clone();
        for version in &self.body[1..] {
            first.merge(version);
        }

        return first.flatten();
    }

    /// Add a new tag to this record head. Checks that tags don't already exists
    pub fn add_tag(&mut self, tag: &str) {
        self.header.tags.push(String::from(tag));
    }
}