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));
}
}
|