summaryrefslogtreecommitdiffstats
path: root/src/profile/state.rs
blob: a84ff23c7d039cede8dd1ef7701937e38f8ac253 (plain)
1
2
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimit
use std::path::PathBuf;
use std::convert::TryFrom;
use std::convert::TryInto;

use anyhow::Context;
use anyhow::Result;
use tokio::io::AsyncWriteExt;

#[derive(Debug)]
pub struct StateDir(PathBuf);

impl StateDir {
    pub fn ipfs(&self) -> PathBuf {
        self.0.join("ipfs")
    }

    pub fn profile_state(&self) -> PathBuf {
        self.0.join("profile_state")
    }

    pub fn display(&self) -> std::path::Display {
        self.0.display()
    }
}

impl From<PathBuf> for StateDir {
    fn from(p: PathBuf) -> Self {
        Self(p)
    }
}

#[derive(getset::Getters)]
pub struct ProfileState {
    #[getset(get = "pub")]
    profile_head: cid::Cid,

    #[getset(get = "pub")]
    profile_name: String,

    #[getset(get = "pub")]
    keypair: libp2p::identity::Keypair,
}

impl ProfileState {
    pub(super) fn new(profile_head: cid::Cid, profile_name: String, keypair: libp2p::identity::Keypair) -> Self {
        Self {
            profile_head,
            profile_name,
            keypair
        }
    }
}

impl std::fmt::Debug for ProfileState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "ProfileState {{ name = {}, head = {:?} }}", self.profile_name, self.profile_head)
    }
}

#[derive(Debug, serde::Serialize, serde::Deserialize, getset::Getters)]
pub(super) struct ProfileStateSaveable {
    profile_head: Vec<u8>,
    profile_name: String,
    keypair: Vec<u8>,
}

impl ProfileStateSaveable {
    pub(super) fn new(s: &ProfileState) -> Result<Self> {
        Ok(Self {
            profile_head: s.profile_head.to_bytes(),
            profile_name: s.profile_name.clone(),
            keypair: match s.keypair {
                libp2p::identity::Keypair::Ed25519(ref kp) => Vec::from(kp.encode()),
                _ => anyhow::bail!("Only keypair type ed25519 supported"),
            }
        })
    }

    pub async fn save_to_disk(&self, state_dir_path: &StateDir) -> Result<()> {
        let state_s = serde_json::to_string(&self).context("Serializing state")?;
        tokio::fs::OpenOptions::new()
            .create_new(false) // do not _always_ create a new file
            .create(true)
            .truncate(true)
            .write(true)
            .open(&state_dir_path.profile_state())
            .await
            .with_context(|| format!("Opening {}", state_dir_path.profile_state().display()))?
            .write_all(state_s.as_bytes())
            .await
            .map(|_| ())
            .with_context(|| format!("Writing to {}", state_dir_path.profile_state().display()))
            .map_err(anyhow::Error::from)
    }

    pub async fn load_from_disk(state_dir_path: &StateDir) -> Result<Self> {
        log::trace!("Loading from disk: {:?}", state_dir_path.profile_state().display());
        let reader = tokio::fs::OpenOptions::new()
            .read(true)
            .open(&state_dir_path.profile_state())
            .await
            .context("Opening state file")?
            .into_std()
            .await;

        log::trace!("Parsing state file");
        serde_json::from_reader(reader)
            .context("Parsing state file")
            .map_err(anyhow::Error::from)
    }

}

impl TryInto<ProfileState> for ProfileStateSaveable {
    type Error = anyhow::Error;

    fn try_into(mut self) -> Result<ProfileState> {
        Ok(ProfileState {
            profile_head: cid::Cid::try_from(self.profile_head)?,
            profile_name: self.profile_name,
            keypair: {
                let kp = libp2p::identity::ed25519::Keypair::decode(&mut self.keypair)?;
                libp2p::identity::Keypair::Ed25519(kp)
            },
        })
    }
}