2022-03-25 23:47:50 +00:00
|
|
|
use crate::config::RequiredSledRepo;
|
|
|
|
use crate::{config::Repository, details::Details, store::Identifier};
|
2022-03-24 18:16:41 +00:00
|
|
|
use futures_util::Stream;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
2022-03-25 23:47:50 +00:00
|
|
|
mod old;
|
2022-03-24 22:09:15 +00:00
|
|
|
pub(crate) mod sled;
|
|
|
|
|
2022-03-25 23:47:50 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) enum Repo {
|
|
|
|
Sled(self::sled::SledRepo),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
2022-03-24 18:16:41 +00:00
|
|
|
pub(crate) struct Alias {
|
|
|
|
id: Uuid,
|
|
|
|
extension: String,
|
|
|
|
}
|
2022-03-25 03:06:29 +00:00
|
|
|
|
2022-03-24 18:16:41 +00:00
|
|
|
pub(crate) struct DeleteToken {
|
|
|
|
id: Uuid,
|
|
|
|
}
|
2022-03-25 03:06:29 +00:00
|
|
|
|
2022-03-24 18:16:41 +00:00
|
|
|
pub(crate) struct AlreadyExists;
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
|
|
pub(crate) trait SettingsRepo {
|
2022-03-24 22:09:15 +00:00
|
|
|
type Bytes: AsRef<[u8]> + From<Vec<u8>>;
|
2022-03-24 18:16:41 +00:00
|
|
|
type Error: std::error::Error;
|
|
|
|
|
2022-03-24 22:09:15 +00:00
|
|
|
async fn set(&self, key: &'static [u8], value: Self::Bytes) -> Result<(), Self::Error>;
|
|
|
|
async fn get(&self, key: &'static [u8]) -> Result<Option<Self::Bytes>, Self::Error>;
|
2022-03-24 18:16:41 +00:00
|
|
|
async fn remove(&self, key: &'static [u8]) -> Result<(), Self::Error>;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
|
|
pub(crate) trait IdentifierRepo<I: Identifier> {
|
|
|
|
type Error: std::error::Error;
|
|
|
|
|
|
|
|
async fn relate_details(&self, identifier: I, details: Details) -> Result<(), Self::Error>;
|
|
|
|
async fn details(&self, identifier: I) -> Result<Option<Details>, Self::Error>;
|
|
|
|
|
|
|
|
async fn cleanup(&self, identifier: I) -> Result<(), Self::Error>;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
2022-03-24 22:09:15 +00:00
|
|
|
pub(crate) trait HashRepo<I: Identifier> {
|
|
|
|
type Bytes: AsRef<[u8]> + From<Vec<u8>>;
|
2022-03-24 18:16:41 +00:00
|
|
|
type Error: std::error::Error;
|
2022-03-24 22:09:15 +00:00
|
|
|
type Stream: Stream<Item = Result<Self::Bytes, Self::Error>>;
|
2022-03-24 18:16:41 +00:00
|
|
|
|
|
|
|
async fn hashes(&self) -> Self::Stream;
|
|
|
|
|
2022-03-24 22:09:15 +00:00
|
|
|
async fn create(&self, hash: Self::Bytes) -> Result<Result<(), AlreadyExists>, Self::Error>;
|
|
|
|
|
|
|
|
async fn relate_alias(&self, hash: Self::Bytes, alias: Alias) -> Result<(), Self::Error>;
|
|
|
|
async fn remove_alias(&self, hash: Self::Bytes, alias: Alias) -> Result<(), Self::Error>;
|
|
|
|
async fn aliases(&self, hash: Self::Bytes) -> Result<Vec<Alias>, Self::Error>;
|
2022-03-24 18:16:41 +00:00
|
|
|
|
2022-03-24 22:09:15 +00:00
|
|
|
async fn relate_identifier(&self, hash: Self::Bytes, identifier: I) -> Result<(), Self::Error>;
|
|
|
|
async fn identifier(&self, hash: Self::Bytes) -> Result<I, Self::Error>;
|
2022-03-24 18:16:41 +00:00
|
|
|
|
2022-03-25 23:47:50 +00:00
|
|
|
async fn relate_variant_identifier(
|
|
|
|
&self,
|
|
|
|
hash: Self::Bytes,
|
|
|
|
variant: String,
|
|
|
|
identifier: I,
|
|
|
|
) -> Result<(), Self::Error>;
|
|
|
|
async fn variant_identifier(
|
|
|
|
&self,
|
|
|
|
hash: Self::Bytes,
|
|
|
|
variant: String,
|
|
|
|
) -> Result<Option<I>, Self::Error>;
|
|
|
|
|
|
|
|
async fn relate_motion_identifier(
|
|
|
|
&self,
|
|
|
|
hash: Self::Bytes,
|
|
|
|
identifier: I,
|
|
|
|
) -> Result<(), Self::Error>;
|
|
|
|
async fn motion_identifier(&self, hash: Self::Bytes) -> Result<Option<I>, Self::Error>;
|
|
|
|
|
2022-03-24 22:09:15 +00:00
|
|
|
async fn cleanup(&self, hash: Self::Bytes) -> Result<(), Self::Error>;
|
2022-03-24 18:16:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
2022-03-24 22:09:15 +00:00
|
|
|
pub(crate) trait AliasRepo {
|
|
|
|
type Bytes: AsRef<[u8]> + From<Vec<u8>>;
|
2022-03-24 18:16:41 +00:00
|
|
|
type Error: std::error::Error;
|
|
|
|
|
|
|
|
async fn create(&self, alias: Alias) -> Result<Result<(), AlreadyExists>, Self::Error>;
|
|
|
|
|
2022-03-24 22:09:15 +00:00
|
|
|
async fn relate_delete_token(
|
2022-03-24 18:16:41 +00:00
|
|
|
&self,
|
|
|
|
alias: Alias,
|
2022-03-24 22:09:15 +00:00
|
|
|
delete_token: DeleteToken,
|
|
|
|
) -> Result<Result<(), AlreadyExists>, Self::Error>;
|
2022-03-24 18:16:41 +00:00
|
|
|
async fn delete_token(&self, alias: Alias) -> Result<DeleteToken, Self::Error>;
|
|
|
|
|
2022-03-24 22:09:15 +00:00
|
|
|
async fn relate_hash(&self, alias: Alias, hash: Self::Bytes) -> Result<(), Self::Error>;
|
|
|
|
async fn hash(&self, alias: Alias) -> Result<Self::Bytes, Self::Error>;
|
2022-03-24 18:16:41 +00:00
|
|
|
|
|
|
|
async fn cleanup(&self, alias: Alias) -> Result<(), Self::Error>;
|
|
|
|
}
|
2022-03-25 23:47:50 +00:00
|
|
|
|
|
|
|
impl Repo {
|
|
|
|
pub(crate) fn open(config: Repository) -> anyhow::Result<Self> {
|
|
|
|
match config {
|
|
|
|
Repository::Sled(RequiredSledRepo {
|
|
|
|
mut path,
|
|
|
|
cache_capacity,
|
|
|
|
}) => {
|
|
|
|
path.push("v0.4.0-alpha.1");
|
|
|
|
|
|
|
|
let db = ::sled::Config::new()
|
|
|
|
.cache_capacity(cache_capacity)
|
|
|
|
.path(path)
|
|
|
|
.open()?;
|
|
|
|
|
|
|
|
Ok(Self::Sled(self::sled::SledRepo::new(db)?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
|
|
pub(crate) async fn from_db(&self, db: ::sled::Db) -> anyhow::Result<()> {
|
|
|
|
if self.has_migrated().await? {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let old = self::old::Old::open(db)?;
|
|
|
|
|
|
|
|
for hash in old.hashes() {
|
|
|
|
match self {
|
|
|
|
Self::Sled(repo) => {
|
|
|
|
if let Err(e) = migrate_hash(repo, &old, hash).await {
|
|
|
|
tracing::error!("Failed to migrate hash: {}", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.mark_migrated().await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn has_migrated(&self) -> anyhow::Result<bool> {
|
|
|
|
match self {
|
|
|
|
Self::Sled(repo) => Ok(repo.get(REPO_MIGRATION_O1).await?.is_some()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn mark_migrated(&self) -> anyhow::Result<()> {
|
|
|
|
match self {
|
|
|
|
Self::Sled(repo) => {
|
|
|
|
repo.set(REPO_MIGRATION_O1, b"1".to_vec().into()).await?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const REPO_MIGRATION_O1: &[u8] = b"repo-migration-01";
|
|
|
|
const STORE_MIGRATION_PROGRESS: &[u8] = b"store-migration-progress";
|
|
|
|
const GENERATOR_KEY: &[u8] = b"last-path";
|
|
|
|
|
|
|
|
async fn migrate_hash<T>(repo: &T, old: &old::Old, hash: ::sled::IVec) -> anyhow::Result<()>
|
|
|
|
where
|
|
|
|
T: IdentifierRepo<::sled::IVec>,
|
|
|
|
<T as IdentifierRepo<::sled::IVec>>::Error: Send + Sync + 'static,
|
|
|
|
T: HashRepo<::sled::IVec>,
|
|
|
|
<T as HashRepo<::sled::IVec>>::Error: Send + Sync + 'static,
|
|
|
|
T: AliasRepo,
|
|
|
|
<T as AliasRepo>::Error: Send + Sync + 'static,
|
|
|
|
T: SettingsRepo,
|
|
|
|
<T as SettingsRepo>::Error: Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
HashRepo::create(repo, hash.to_vec().into()).await?;
|
|
|
|
|
|
|
|
let main_ident = old.main_identifier(&hash)?;
|
|
|
|
|
|
|
|
HashRepo::relate_identifier(repo, hash.to_vec().into(), main_ident.clone()).await?;
|
|
|
|
|
|
|
|
for alias in old.aliases(&hash) {
|
|
|
|
if let Ok(Ok(())) = AliasRepo::create(repo, alias.clone()).await {
|
|
|
|
let _ = HashRepo::relate_alias(repo, hash.to_vec().into(), alias.clone()).await;
|
|
|
|
let _ = AliasRepo::relate_hash(repo, alias.clone(), hash.to_vec().into()).await;
|
|
|
|
|
|
|
|
if let Ok(Some(delete_token)) = old.delete_token(&alias) {
|
|
|
|
let _ = AliasRepo::relate_delete_token(repo, alias, delete_token).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Ok(Some(identifier)) = old.motion_identifier(&hash) {
|
|
|
|
HashRepo::relate_motion_identifier(repo, hash.to_vec().into(), identifier).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (variant, identifier) in old.variants(&hash)? {
|
|
|
|
let _ =
|
|
|
|
HashRepo::relate_variant_identifier(repo, hash.to_vec().into(), variant, identifier)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (identifier, details) in old.details(&hash)? {
|
|
|
|
let _ = IdentifierRepo::relate_details(repo, identifier, details).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Ok(Some(value)) = old.setting(STORE_MIGRATION_PROGRESS) {
|
|
|
|
SettingsRepo::set(repo, STORE_MIGRATION_PROGRESS, value.to_vec().into()).await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Ok(Some(value)) = old.setting(GENERATOR_KEY) {
|
|
|
|
SettingsRepo::set(repo, GENERATOR_KEY, value.to_vec().into()).await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Alias {
|
|
|
|
fn to_bytes(&self) -> Vec<u8> {
|
|
|
|
let mut v = self.id.as_bytes().to_vec();
|
|
|
|
|
|
|
|
v.extend_from_slice(self.extension.as_bytes());
|
|
|
|
|
|
|
|
v
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_slice(bytes: &[u8]) -> Option<Self> {
|
|
|
|
if bytes.len() > 16 {
|
|
|
|
let id = Uuid::from_slice(&bytes[0..16]).expect("Already checked length");
|
|
|
|
let extension = String::from_utf8_lossy(&bytes[16..]).to_string();
|
|
|
|
|
|
|
|
Some(Self { id, extension })
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DeleteToken {
|
|
|
|
fn to_bytes(&self) -> Vec<u8> {
|
|
|
|
self.id.as_bytes().to_vec()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_slice(bytes: &[u8]) -> Option<Self> {
|
|
|
|
Some(DeleteToken {
|
|
|
|
id: Uuid::from_slice(bytes).ok()?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for Alias {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}{}", self.id, self.extension)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Identifier for Vec<u8> {
|
|
|
|
type Error = std::convert::Infallible;
|
|
|
|
|
|
|
|
fn from_bytes(bytes: Vec<u8>) -> Result<Self, Self::Error>
|
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
|
|
|
Ok(bytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_bytes(&self) -> Result<Vec<u8>, Self::Error> {
|
|
|
|
Ok(self.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Identifier for ::sled::IVec {
|
|
|
|
type Error = std::convert::Infallible;
|
|
|
|
|
|
|
|
fn from_bytes(bytes: Vec<u8>) -> Result<Self, Self::Error>
|
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
|
|
|
Ok(bytes.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_bytes(&self) -> Result<Vec<u8>, Self::Error> {
|
|
|
|
Ok(self.to_vec())
|
|
|
|
}
|
|
|
|
}
|