You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
5.6 KiB
212 lines
5.6 KiB
//! Data structures for the in-memory map.
|
|
use super::*;
|
|
use std::{
|
|
collections::HashMap,
|
|
borrow::Cow,
|
|
error,
|
|
fmt,
|
|
path::PathBuf,
|
|
fs,
|
|
num::NonZeroI32,
|
|
};
|
|
use generational_arena::{
|
|
Arena, Index,
|
|
};
|
|
use bitflags::bitflags;
|
|
use cryptohelpers::{
|
|
rsa::{
|
|
RsaPublicKey, RsaPrivateKey,
|
|
Signature,
|
|
},
|
|
aes::{self, AesKey,},
|
|
sha256::Sha256Hash,
|
|
};
|
|
use tokio::io::{
|
|
AsyncRead, AsyncWrite,
|
|
};
|
|
|
|
/// An absolute (nested) index
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
pub struct AbsoluteIndex(Vec<Index>);
|
|
|
|
id_type!(pub CachedFileId: "The file path is this ID inside the data dir. It may be subject to caching");
|
|
|
|
/// Possible value types of the data map
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub enum Data
|
|
{
|
|
Byte(u8),
|
|
Char(char),
|
|
Bool(bool),
|
|
|
|
/// Signed integer
|
|
SI(i64),
|
|
/// Unsigned integer
|
|
UI(u64),
|
|
/// Floating-point integer
|
|
FI(f64),
|
|
|
|
/// A UTF-8 text string
|
|
Text(String),
|
|
/// A binary blob
|
|
Binary(Vec<u8>),
|
|
|
|
/// A UTF-8 text string outsourced to a file.
|
|
///
|
|
/// The file path is this ID inside the data dir. It may be subject to caching later down the line, and will require deleting when an Atom of this type is removed.
|
|
/// The files corresponding to these entries should be lazy-loaded (as open `tokio::fs::File`s) into a `DelayQueue` for caching.
|
|
TextFile(CachedFileId),
|
|
/// A binary blob outsourced to a file.
|
|
///
|
|
/// The file path is this ID inside the data dir. It may be subject to caching later down the line, and will require deleting when an Atom of this type is removed.
|
|
/// The files corresponding to these entries should be lazy-loaded (as open `tokio::fs::File`s) into a `DelayQueue` for caching.
|
|
BinaryFile(CachedFileId),
|
|
|
|
/// A reference index to an item within the same `Datamap` as this one.
|
|
RelativeRef(Index),
|
|
/// A reference to an item N deep within nested `Map` elements.
|
|
///
|
|
/// The first `Index` specifies the `Map` data item at the root `Datamap` that contains the next, et cetera. The pointed to value is the last index.
|
|
AbsoluteRef(AbsoluteIndex),
|
|
|
|
/// A list of atoms
|
|
List(Vec<Atom>),
|
|
/// Another datamap
|
|
Map(Datamap),
|
|
|
|
/// An AES key
|
|
AesKey(AesKey),
|
|
/// An RSA keypair
|
|
RsaKeypair(RsaPrivateKey, RsaPublicKey),
|
|
/// An RSA private key
|
|
RsaPrivate(RsaPrivateKey),
|
|
/// An RSA public key
|
|
RsaPublic(RsaPublicKey),
|
|
|
|
/// A SHA256 hash
|
|
Hash(Sha256Hash),
|
|
|
|
/// A unique ID
|
|
Uuid(uuid::Uuid),
|
|
|
|
/// An RSA signature
|
|
Signature(Signature),
|
|
|
|
/// Nothing
|
|
Null,
|
|
}
|
|
|
|
/// An entry that may or may not be encrypted
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub enum MaybeEncrypted
|
|
{
|
|
/// This is an encrypted serialized `Data`.
|
|
Encrypted(Vec<u8>),
|
|
/// An unencrypted `Data`
|
|
Unencrypted(Data),
|
|
}
|
|
|
|
submod!(encryption; "impl for `MaybeEncrypted`. \
|
|
Other `Data` encryption helpers");
|
|
|
|
bitflags! {
|
|
/// And additional metadata for values
|
|
#[derive(Serialize, Deserialize)]
|
|
struct Tags: u16
|
|
{
|
|
/// Default
|
|
const NONE = 0;
|
|
/// This value should be cloned on write unless specified elsewhere.
|
|
const COW = 1<<0;
|
|
/// This should not show up in searches.
|
|
const HIDDEN = 1<<1;
|
|
/// Versioning is enabled for this value.
|
|
///
|
|
/// When it is deleted, it should instead be moved away into a sperate entry.
|
|
const VERSIONED = 1<<2;
|
|
}
|
|
}
|
|
|
|
/// Information about a map entry.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct Info
|
|
{
|
|
alt_name: Option<(String, String)>,
|
|
|
|
created: u64,
|
|
modified: u64,
|
|
|
|
tags: Tags,
|
|
|
|
/// Events to be emitted to `State` when this element does something.
|
|
///
|
|
/// Can be used for monitoring or logging and such.
|
|
hooks: event::Hooks,
|
|
|
|
owner: Option<Vec<user::EntityID>>, //starts as the user that created (i.e. same as `created_by`), or `None` if ownership is disabled
|
|
signed: Option<Vec<Signature>>,
|
|
|
|
perms: user::Permissions,
|
|
|
|
created_by: user::UserID,
|
|
log: Vec<event::InBandEvent>,
|
|
}
|
|
|
|
/// The root data containing map
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Datamap
|
|
{
|
|
data: Arena<Atom>,
|
|
ident: HashMap<Identifier, Index>,
|
|
}
|
|
|
|
impl PartialEq for Datamap
|
|
{
|
|
fn eq(&self, other: &Self) -> bool
|
|
{
|
|
self.ident == other.ident && {
|
|
for (v1, v2) in self.ident.iter().map(|(_, v)| (self.data.get(*v), other.data.get(*v)))
|
|
{
|
|
if v1 != v2 {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A value in a datamap, contains the information about the value and the value itself.
|
|
///
|
|
/// May also contain previous versions of this atom.
|
|
///
|
|
/// # Note
|
|
/// `Atom` boxes most of itself. It's not needed to box `Atom` itself.
|
|
// Box these first two together, since they are hueg.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct Atom(Box<(MaybeEncrypted, Info)>, Vec<Atom>);
|
|
|
|
/// An identifier for an item in a `Datamap`, or an item nested within many `Datamap`s.
|
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
|
pub struct Identifier(String);
|
|
|
|
#[cfg(test)]
|
|
mod tests
|
|
{
|
|
#[test]
|
|
fn data_me_sync()
|
|
{
|
|
let data = super::Data::SI(-120);
|
|
let aes = super::aes::AesKey::generate().unwrap();
|
|
|
|
println!("Data: {:?}", data);
|
|
let enc = super::MaybeEncrypted::new_encrypted(data.clone(), &aes).expect("Couldn't encrypt");
|
|
println!("M/Enc: {:?}", enc);
|
|
let dec = enc.clone().into_unencrypted(Some(&aes)).expect("Couldn't decrypt");
|
|
println!("M/Dec: {:?}", dec);
|
|
let out = dec.into_data(Some(&aes)).unwrap();
|
|
|
|
assert_eq!(data, out);
|
|
}
|
|
}
|