use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use keetanetwork_block::{AccountRef, Amount, Block};
use keetanetwork_vote::{VoteQuote, VoteStaple};
use crate::error::ClientError;
use crate::sync::Once;
#[derive(Clone, Debug, Default)]
pub struct PendingAccount {
cell: Arc<Once<AccountRef>>,
}
impl PendingAccount {
pub fn get(&self) -> Result<AccountRef, ClientError> {
self.cell
.get()
.map(Arc::clone)
.ok_or(ClientError::UnresolvedIdentifier)
}
pub(crate) fn fill(&self, account: AccountRef) {
self.cell.call_once(|| account);
}
}
#[derive(Clone, Debug)]
pub enum AccountOrPending {
Resolved(AccountRef),
Pending(PendingAccount),
}
impl AccountOrPending {
pub(crate) fn resolve(&self) -> Result<AccountRef, ClientError> {
match self {
AccountOrPending::Resolved(account) => Ok(Arc::clone(account)),
AccountOrPending::Pending(pending) => pending.get(),
}
}
}
impl From<AccountRef> for AccountOrPending {
fn from(account: AccountRef) -> Self {
AccountOrPending::Resolved(account)
}
}
impl From<&AccountRef> for AccountOrPending {
fn from(account: &AccountRef) -> Self {
AccountOrPending::Resolved(Arc::clone(account))
}
}
impl From<PendingAccount> for AccountOrPending {
fn from(pending: PendingAccount) -> Self {
AccountOrPending::Pending(pending)
}
}
impl From<&PendingAccount> for AccountOrPending {
fn from(pending: &PendingAccount) -> Self {
AccountOrPending::Pending(pending.clone())
}
}
#[derive(Debug, Clone)]
pub struct TokenBalance {
pub token: String,
pub balance: Amount,
}
#[derive(Debug, Clone)]
pub struct Representative {
pub account: String,
pub weight: Amount,
pub api_url: Option<String>,
}
#[derive(Debug, Clone)]
pub struct LedgerChecksum {
pub checksum: Amount,
pub moment: Option<String>,
pub moment_range: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct HistoryEntry {
pub staple: VoteStaple,
pub id: Option<String>,
pub timestamp: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Acl {
pub principal: Option<String>,
pub entity: Option<String>,
pub target: Option<String>,
pub permissions: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct Certificate {
pub certificate: String,
pub intermediates: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct ChainQuery {
pub start: Option<String>,
pub end: Option<String>,
pub limit: Option<i64>,
}
#[derive(Debug, Clone, Default)]
pub struct ChainPage {
pub blocks: Vec<Block>,
pub next_key: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct HistoryQuery {
pub start: Option<String>,
pub limit: Option<i64>,
}
#[derive(Debug, Clone, Default)]
pub struct AccountInfo {
pub name: Option<String>,
pub description: Option<String>,
pub metadata: Option<String>,
}
#[derive(Debug, Clone)]
pub struct AccountState {
pub representative: Option<String>,
pub head: Option<String>,
pub height: Option<Amount>,
pub info: Option<AccountInfo>,
pub supply: Option<Amount>,
pub balances: Vec<TokenBalance>,
}
#[derive(Clone, Debug, Default)]
pub struct TransmitOptions {
pub fee_signer: Option<AccountRef>,
pub quotes: Vec<VoteQuote>,
pub fee_token_priority: Vec<AccountRef>,
}
#[cfg(feature = "std")]
#[derive(Clone, Debug)]
pub struct RepStatus {
pub representative: String,
pub online: bool,
pub stats: Option<serde_json::Value>,
}
#[cfg(test)]
mod tests {
use super::*;
use keetanetwork_block::testing::generate_ed25519_ref;
#[test]
fn pending_account_resolves_after_fill() -> Result<(), ClientError> {
let pending = PendingAccount::default();
assert!(matches!(pending.get(), Err(ClientError::UnresolvedIdentifier)));
let account = generate_ed25519_ref(0x01);
pending.fill(Arc::clone(&account));
assert_eq!(pending.get()?.to_string(), account.to_string());
Ok(())
}
#[test]
fn pending_account_fill_is_set_once() {
let pending = PendingAccount::default();
let first = generate_ed25519_ref(0x02);
let second = generate_ed25519_ref(0x03);
pending.fill(Arc::clone(&first));
pending.fill(Arc::clone(&second));
assert!(matches!(pending.get(), Ok(account) if account.to_string() == first.to_string()));
}
#[test]
fn account_or_pending_resolves_both_variants() -> Result<(), ClientError> {
let account = generate_ed25519_ref(0x04);
let resolved: AccountOrPending = (&account).into();
assert_eq!(resolved.resolve()?.to_string(), account.to_string());
let pending = PendingAccount::default();
let operand: AccountOrPending = pending.clone().into();
assert!(matches!(operand.resolve(), Err(ClientError::UnresolvedIdentifier)));
pending.fill(Arc::clone(&account));
assert_eq!(operand.resolve()?.to_string(), account.to_string());
Ok(())
}
}