use std::{fmt, path::PathBuf, str::FromStr};
use serde::{Deserialize, Serialize};
use web3_address::ethereum::Address;
use crate::{
constants::VAULT_EXT,
decode,
storage::AppPaths,
vault::{Header, Summary, Vault, VaultId},
vfs,
};
use crate::{Error, Result};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountInfo {
address: Address,
label: String,
}
impl AccountInfo {
pub fn new(label: String, address: Address) -> Self {
Self { label, address }
}
pub fn address(&self) -> &Address {
&self.address
}
pub fn label(&self) -> &str {
&self.label
}
pub(crate) fn set_label(&mut self, label: String) {
self.label = label;
}
}
impl From<&AccountInfo> for AccountRef {
fn from(value: &AccountInfo) -> Self {
AccountRef::Address(*value.address())
}
}
impl From<AccountInfo> for AccountRef {
fn from(value: AccountInfo) -> Self {
(&value).into()
}
}
#[derive(Debug, Clone)]
pub enum AccountRef {
Address(Address),
Name(String),
}
impl fmt::Display for AccountRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Address(address) => write!(f, "{}", address),
Self::Name(name) => write!(f, "{}", name),
}
}
}
impl FromStr for AccountRef {
type Err = Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if let Ok(address) = s.parse::<Address>() {
Ok(Self::Address(address))
} else {
Ok(Self::Name(s.to_string()))
}
}
}
#[derive(Default)]
pub struct LocalAccounts;
impl LocalAccounts {
pub async fn find_local_vault(
address: &Address,
id: &VaultId,
include_system: bool,
) -> Result<(Vault, PathBuf)> {
let vaults = Self::list_local_vaults(address, include_system).await?;
let (_summary, path) = vaults
.into_iter()
.find(|(s, _)| s.id() == id)
.ok_or_else(|| Error::NoVaultFile(id.to_string()))?;
let buffer = vfs::read(&path).await?;
let vault: Vault = decode(&buffer).await?;
Ok((vault, path))
}
pub async fn list_local_vaults(
address: &Address,
include_system: bool,
) -> Result<Vec<(Summary, PathBuf)>> {
let vaults_dir = AppPaths::local_vaults_dir(address.to_string())?;
let mut vaults = Vec::new();
let mut dir = vfs::read_dir(vaults_dir).await?;
while let Some(entry) = dir.next_entry().await? {
if let Some(extension) = entry.path().extension() {
if extension == VAULT_EXT {
let summary =
Header::read_summary_file(entry.path()).await?;
if !include_system && summary.flags().is_system() {
continue;
}
vaults.push((summary, entry.path().to_path_buf()));
}
}
}
Ok(vaults)
}
pub async fn list_accounts() -> Result<Vec<AccountInfo>> {
let mut keys = Vec::new();
let identity_dir = AppPaths::identity_dir()?;
let mut dir = vfs::read_dir(identity_dir).await?;
while let Some(entry) = dir.next_entry().await? {
if let (Some(extension), Some(file_stem)) =
(entry.path().extension(), entry.path().file_stem())
{
if extension == VAULT_EXT {
let summary =
Header::read_summary_file(entry.path()).await?;
keys.push(AccountInfo {
address: file_stem.to_string_lossy().parse()?,
label: summary.name().to_owned(),
});
}
}
}
keys.sort_by(|a, b| a.label.cmp(&b.label));
Ok(keys)
}
}