forked from NextGraph/nextgraph-rs
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.
174 lines
4.9 KiB
174 lines
4.9 KiB
11 months ago
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||
|
// All rights reserved.
|
||
|
// Licensed under the Apache License, Version 2.0
|
||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||
|
// at your option. All files in the project carrying such
|
||
|
// notice may not be copied, modified, or distributed except
|
||
|
// according to those terms.
|
||
|
|
||
|
//! Branch storage on disk
|
||
|
|
||
|
use std::collections::hash_map::DefaultHasher;
|
||
|
use std::collections::HashMap;
|
||
|
use std::hash::Hash;
|
||
|
use std::hash::Hasher;
|
||
|
use std::time::SystemTime;
|
||
|
|
||
|
use ng_net::errors::ProtocolError;
|
||
|
use ng_net::types::*;
|
||
|
use ng_repo::block_storage::BlockStorage;
|
||
|
use ng_repo::errors::StorageError;
|
||
|
use ng_repo::kcv_storage::KCVStorage;
|
||
|
use ng_repo::repo::BranchInfo;
|
||
|
use ng_repo::repo::Repo;
|
||
|
use ng_repo::store::Store;
|
||
|
use ng_repo::types::BranchId;
|
||
|
use ng_repo::types::BranchType;
|
||
|
use ng_repo::types::BranchWriteCapSecret;
|
||
|
use ng_repo::types::ObjectId;
|
||
|
use ng_repo::types::ReadCap;
|
||
|
use ng_repo::types::RepoId;
|
||
|
use ng_repo::types::SymKey;
|
||
|
use ng_repo::types::Timestamp;
|
||
|
use ng_repo::types::TopicId;
|
||
|
use serde_bare::to_vec;
|
||
|
|
||
|
use super::prop;
|
||
|
|
||
|
pub struct BranchStorage<'a> {
|
||
|
storage: &'a dyn KCVStorage,
|
||
|
id: BranchId,
|
||
|
}
|
||
|
|
||
|
impl<'a> BranchStorage<'a> {
|
||
|
const PREFIX: u8 = b'c';
|
||
|
|
||
|
// branch properties suffixes
|
||
|
const TYPE: u8 = b'b';
|
||
|
const PUBLISHER: u8 = b'p';
|
||
|
const READ_CAP: u8 = b'r';
|
||
|
const TOPIC: u8 = b't';
|
||
|
|
||
|
const ALL_PROPERTIES: [u8; 4] = [Self::TYPE, Self::PUBLISHER, Self::READ_CAP, Self::TOPIC];
|
||
|
|
||
|
const PREFIX_HEADS: u8 = b'h';
|
||
|
|
||
|
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::TYPE;
|
||
|
|
||
|
pub fn open(
|
||
|
id: &BranchId,
|
||
|
storage: &'a dyn KCVStorage,
|
||
|
) -> Result<BranchStorage<'a>, StorageError> {
|
||
|
let opening = BranchStorage {
|
||
|
id: id.clone(),
|
||
|
storage,
|
||
|
};
|
||
|
if !opening.exists() {
|
||
|
return Err(StorageError::NotFound);
|
||
|
}
|
||
|
Ok(opening)
|
||
|
}
|
||
|
|
||
|
pub fn create_from_info(
|
||
|
info: &BranchInfo,
|
||
|
storage: &'a dyn KCVStorage,
|
||
|
) -> Result<BranchStorage<'a>, StorageError> {
|
||
|
Self::create(
|
||
|
&info.id,
|
||
|
&info.read_cap,
|
||
|
&info.branch_type,
|
||
|
&info.topic,
|
||
|
info.topic_priv_key.as_ref(),
|
||
|
storage,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
pub fn create(
|
||
|
id: &BranchId,
|
||
|
read_cap: &ReadCap,
|
||
|
branch_type: &BranchType,
|
||
|
topic: &TopicId,
|
||
|
publisher: Option<&BranchWriteCapSecret>,
|
||
|
storage: &'a dyn KCVStorage,
|
||
|
) -> Result<BranchStorage<'a>, StorageError> {
|
||
|
let bs = BranchStorage {
|
||
|
id: id.clone(),
|
||
|
storage,
|
||
|
};
|
||
|
if bs.exists() {
|
||
|
return Err(StorageError::AlreadyExists);
|
||
|
}
|
||
|
|
||
|
storage.write_transaction(&mut |tx| {
|
||
|
let id_ser = to_vec(&id)?;
|
||
|
let value = to_vec(read_cap)?;
|
||
|
tx.put(Self::PREFIX, &id_ser, Some(Self::READ_CAP), &value, &None)?;
|
||
|
let value = to_vec(branch_type)?;
|
||
|
tx.put(Self::PREFIX, &id_ser, Some(Self::TYPE), &value, &None)?;
|
||
|
let value = to_vec(topic)?;
|
||
|
tx.put(Self::PREFIX, &id_ser, Some(Self::TOPIC), &value, &None)?;
|
||
|
if let Some(privkey) = publisher {
|
||
|
let value = to_vec(privkey)?;
|
||
|
tx.put(Self::PREFIX, &id_ser, Some(Self::PUBLISHER), &value, &None)?;
|
||
|
}
|
||
|
Ok(())
|
||
|
})?;
|
||
|
Ok(bs)
|
||
|
}
|
||
|
|
||
|
pub fn load(id: &BranchId, storage: &'a dyn KCVStorage) -> Result<BranchInfo, StorageError> {
|
||
|
let props = storage.get_all_properties_of_key(
|
||
|
Self::PREFIX,
|
||
|
to_vec(id).unwrap(),
|
||
|
Self::ALL_PROPERTIES.to_vec(),
|
||
|
&None,
|
||
|
)?;
|
||
|
|
||
|
let bs = BranchInfo {
|
||
|
id: id.clone(),
|
||
|
branch_type: prop(Self::TYPE, &props)?,
|
||
|
read_cap: prop(Self::READ_CAP, &props)?,
|
||
|
topic: prop(Self::TOPIC, &props)?,
|
||
|
topic_priv_key: prop(Self::PUBLISHER, &props).ok(),
|
||
|
};
|
||
|
Ok(bs)
|
||
|
}
|
||
|
|
||
|
pub fn exists(&self) -> bool {
|
||
|
self.storage
|
||
|
.get(
|
||
|
Self::PREFIX,
|
||
|
&to_vec(&self.id).unwrap(),
|
||
|
Some(Self::SUFFIX_FOR_EXIST_CHECK),
|
||
|
&None,
|
||
|
)
|
||
|
.is_ok()
|
||
|
}
|
||
|
pub fn id(&self) -> &RepoId {
|
||
|
&self.id
|
||
|
}
|
||
|
|
||
|
pub fn del(&self) -> Result<(), StorageError> {
|
||
|
self.storage.write_transaction(&mut |tx| {
|
||
|
let key = &to_vec(&self.id)?;
|
||
|
tx.del_all(Self::PREFIX, key, &Self::ALL_PROPERTIES, &None)?;
|
||
|
let size = to_vec(&ObjectId::nil())?.len();
|
||
|
tx.del_all_values(Self::PREFIX_HEADS, key, size, None, &None)?;
|
||
|
Ok(())
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod test {
|
||
|
|
||
|
use ng_repo::errors::StorageError;
|
||
|
use ng_repo::types::*;
|
||
|
use ng_repo::utils::*;
|
||
|
use std::fs;
|
||
|
|
||
|
#[test]
|
||
|
pub fn test_repo() {}
|
||
|
}
|