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.

210 lines
5.5 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>);
/// 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(uuid::Uuid),
/// 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(uuid::Uuid),
/// 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);
}
}