signer-daemon 0.3.2

Signer daemon package.
Documentation
use std::{collections::HashMap, fs, path::Path, sync::Arc};

use migration::MigratorTrait;
use sea_orm::{Database, DatabaseConnection};

use signer_core::{SignerFsStore, SignerUser};
use tokio::sync::RwLock;

use crate::{
    ChatStore, CrdtAdapterController, CrdtEventController, DeltaBoxNotifier, MessageStore,
    RemoteController, ServerStore, UserStore, signer_remote::SignerRemote,
};

pub struct SignerDaemon {
    pub core: SignerDaemonCore,
    pub state: SignerDaemonState,
    pub option: SignerDaemonOption,
    pub controller: SignerDaemonController,
    pub store: SignerDaemonStore,
}

#[derive(Debug, Clone)]
pub struct SignerDaemonCore {
    pub db: DatabaseConnection,
    pub peer: String,
    pub prv_key: String,
    pub pub_key: String,
    pub change_notifier: DeltaBoxNotifier,
}

pub struct SignerDaemonState {
    pub user: SignerUser,
    pub remotes: HashMap<String, SignerRemote>,
    pub daemon: Option<Arc<RwLock<SignerDaemon>>>,
}

#[derive(Debug, Clone)]
pub struct SignerDaemonOption {
    pub(crate) user_file_path: Option<String>,
}

pub struct SignerDaemonController {
    pub crdt_event: CrdtEventController,
    pub crdt_adapter: CrdtAdapterController,
    pub remote: RemoteController,
}

pub struct SignerDaemonStore {
    pub chat: ChatStore,
    pub message: MessageStore,
    pub user: UserStore,
    pub server: ServerStore,
}

impl SignerDaemon {
    pub async fn from_memory(
        user: &SignerUser,
        peer: &str,
    ) -> crate::DaemonResult<Arc<RwLock<Self>>> {
        let db = Database::connect("sqlite::memory:").await?;

        migration::Migrator::up(&db, None).await.map_err(|e| {
            crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                "running migration failed: {}",
                e
            )))
        })?;

        let core = SignerDaemonCore {
            db,
            peer: peer.to_string(),
            prv_key: user.prv_key.clone(),
            pub_key: user.public.pub_key.clone(),
            change_notifier: DeltaBoxNotifier::new(),
        };
        let option = SignerDaemonOption {
            user_file_path: None,
        };
        let daemon = Self {
            core: core.clone(),
            state: SignerDaemonState {
                user: user.clone(),
                remotes: HashMap::new(),
                daemon: None,
            },
            option: option.clone(),
            controller: SignerDaemonController {
                crdt_event: CrdtEventController::new(core.clone()),
                crdt_adapter: CrdtAdapterController::new(core.clone()),
                remote: RemoteController::new(core.clone()),
            },
            store: SignerDaemonStore {
                chat: ChatStore::new(core.clone()),
                message: MessageStore::new(core.clone()),
                user: UserStore::new(core.clone(), option.clone()),
                server: ServerStore::new(core.clone()),
            },
        };

        daemon.start().await
    }

    pub async fn from_path(
        user: &SignerUser,
        path: &str,
        user_file_name: &str,
    ) -> crate::DaemonResult<Arc<RwLock<Self>>> {
        let db_url = format!("sqlite://{}/signer.sqlite3?mode=rwc", path);
        let db = Database::connect(&db_url).await?;

        migration::Migrator::up(&db, None).await.map_err(|e| {
            crate::DaemonError::Signer(crate::SignerError::Msg(format!(
                "running migration failed: {}",
                e
            )))
        })?;

        let peer = match fs::read_to_string(Path::new(path).join("peer")) {
            Ok(val) => val,
            Err(_) => {
                let new_peer = uuid::Uuid::new_v4().to_string();
                fs::write(Path::new(path).join("peer"), &new_peer)?;

                new_peer
            }
        };

        let core = SignerDaemonCore {
            db,
            peer: peer.clone(),
            prv_key: user.prv_key.clone(),
            pub_key: user.public.pub_key.clone(),
            change_notifier: DeltaBoxNotifier::new(),
        };
        let option = SignerDaemonOption {
            user_file_path: Some(
                Path::new(path)
                    .join(user_file_name)
                    .to_str()
                    .unwrap()
                    .to_string(),
            ),
        };

        let daemon = Self {
            core: core.clone(),
            state: SignerDaemonState {
                user: user.clone(),
                daemon: None,
                remotes: HashMap::new(),
            },
            option: option.clone(),
            controller: SignerDaemonController {
                crdt_event: CrdtEventController::new(core.clone()),
                crdt_adapter: CrdtAdapterController::new(core.clone()),
                remote: RemoteController::new(core.clone()),
            },
            store: SignerDaemonStore {
                chat: ChatStore::new(core.clone()),
                message: MessageStore::new(core.clone()),
                server: ServerStore::new(core.clone()),
                user: UserStore::new(core.clone(), option.clone()),
            },
        };
        daemon.start().await
    }

    pub async fn from_sfs(
        user: &SignerUser,
        sfs: &SignerFsStore,
    ) -> crate::DaemonResult<Arc<RwLock<Self>>> {
        SignerDaemon::from_path(
            user,
            sfs.root.join(&user.public.pub_key).to_str().unwrap(),
            "user.json",
        )
        .await
    }

    pub(crate) async fn start(self) -> crate::DaemonResult<Arc<RwLock<Self>>> {
        let daemon = Arc::new(RwLock::new(self));

        {
            let daemon_lock = daemon.clone();
            let daemon = &mut *daemon.write().await;
            daemon.state.daemon = Some(daemon_lock);
            daemon.controller.crdt_adapter.apply_all().await?;
            daemon
                .controller
                .remote
                .reconcile(&mut daemon.state)
                .await?;
            daemon.sync_user_public().await?;
        }

        Ok(daemon)
    }

    pub async fn finalize(&mut self) -> crate::DaemonResult<()> {
        tracing::info!("closing database {}", self.state.user.public.pub_key);

        for i in self.state.remotes.values_mut() {
            i.close().await;
        }
        self.core.db.close_by_ref().await?;

        Ok(())
    }
}