use std::{sync::Arc, time::Duration};
use failure::{format_err, Error};
use itertools::Itertools;
use crate::{
claim::Claim,
id::{ContractId, FileId},
proto::{
CryptoService_grpc::CryptoServiceClient, FileService_grpc::FileServiceClient,
SmartContractService_grpc::SmartContractServiceClient,
},
query::{
Query, QueryCryptoGetAccountBalance, QueryCryptoGetClaim, QueryCryptoGetInfo,
QueryFileGetContents, QueryFileGetInfo, QueryGetTransactionReceipt,
QueryTransactionGetRecord,
},
transaction::{
Transaction, TransactionContractCall, TransactionContractCreate, TransactionContractUpdate,
TransactionCryptoCreate, TransactionCryptoDelete, TransactionCryptoDeleteClaim,
TransactionCryptoUpdate, TransactionFileAppend, TransactionFileCreate,
TransactionFileDelete,
},
AccountId, AccountInfo, FileInfo, TransactionId, TransactionReceipt, TransactionRecord,
};
use grpc::ClientStub;
pub struct Client {
pub(crate) crypto: Arc<CryptoServiceClient>,
pub(crate) file: Arc<FileServiceClient>,
pub(crate) contract: Arc<SmartContractServiceClient>,
}
impl Client {
pub fn new(address: impl AsRef<str>) -> Result<Self, Error> {
let address = address.as_ref();
let (host, port) = address.split(':').next_tuple().ok_or_else(|| {
format_err!("failed to parse 'host:port' from address: {:?}", address)
})?;
let port = port.parse()?;
let inner = Arc::new(grpc::Client::new_plain(
&host,
port,
grpc::ClientConf {
http: httpbis::ClientConf {
no_delay: Some(true),
connection_timeout: Some(Duration::from_secs(5)),
..httpbis::ClientConf::default()
},
},
)?);
let crypto = Arc::new(CryptoServiceClient::with_client(inner.clone()));
let file = Arc::new(FileServiceClient::with_client(inner.clone()));
let contract = Arc::new(SmartContractServiceClient::with_client(inner));
Ok(Self {
crypto,
file,
contract,
})
}
#[inline]
pub fn create_account(&self) -> Transaction<TransactionCryptoCreate> {
TransactionCryptoCreate::new(self)
}
#[inline]
pub fn account(&self, id: AccountId) -> PartialAccountMessage<'_> {
PartialAccountMessage(self, id)
}
#[inline]
pub fn create_contract(&self) -> Transaction<TransactionContractCreate> {
TransactionContractCreate::new(self)
}
#[inline]
pub fn contract(&self, id: ContractId) -> PartialContractMessage<'_> {
PartialContractMessage(self, id)
}
#[inline]
pub fn create_file(&self) -> Transaction<TransactionFileCreate> {
TransactionFileCreate::new(self)
}
#[inline]
pub fn file(&self, id: FileId) -> PartialFileMessage<'_> {
PartialFileMessage(self, id)
}
#[inline]
pub fn transaction(&self, id: TransactionId) -> PartialTransactionMessage {
PartialTransactionMessage(self, id)
}
}
pub struct PartialAccountMessage<'a>(&'a Client, AccountId);
impl<'a> PartialAccountMessage<'a> {
#[inline]
pub fn balance(self) -> Query<u64> {
QueryCryptoGetAccountBalance::new(self.0, self.1)
}
#[inline]
pub fn info(self) -> Query<AccountInfo> {
QueryCryptoGetInfo::new(self.0, self.1)
}
#[inline]
pub fn update(self) -> Transaction<TransactionCryptoUpdate> {
TransactionCryptoUpdate::new(self.0, self.1)
}
#[inline]
pub fn delete(self) -> Transaction<TransactionCryptoDelete> {
TransactionCryptoDelete::new(self.0, self.1)
}
#[inline]
pub fn claim(self, hash: impl Into<Vec<u8>>) -> PartialAccountClaimMessage<'a> {
PartialAccountClaimMessage(self, hash.into())
}
}
pub struct PartialAccountClaimMessage<'a>(PartialAccountMessage<'a>, Vec<u8>);
impl<'a> PartialAccountClaimMessage<'a> {
#[inline]
pub fn delete(self) -> Transaction<TransactionCryptoDeleteClaim> {
TransactionCryptoDeleteClaim::new((self.0).0, (self.0).1, self.1)
}
#[inline]
pub fn get(self) -> Query<Claim> {
QueryCryptoGetClaim::new((self.0).0, (self.0).1, self.1)
}
}
pub struct PartialFileMessage<'a>(&'a Client, FileId);
impl<'a> PartialFileMessage<'a> {
#[inline]
pub fn append(self, contents: Vec<u8>) -> Transaction<TransactionFileAppend> {
TransactionFileAppend::new(self.0, self.1, contents)
}
#[inline]
pub fn delete(self) -> Transaction<TransactionFileDelete> {
TransactionFileDelete::new(self.0, self.1)
}
#[inline]
pub fn info(self) -> Query<FileInfo> {
QueryFileGetInfo::new(self.0, self.1)
}
#[inline]
pub fn contents(self) -> Query<Vec<u8>> {
QueryFileGetContents::new(self.0, self.1)
}
}
pub struct PartialContractMessage<'a>(&'a Client, ContractId);
impl<'a> PartialContractMessage<'a> {
#[inline]
pub fn call(self) -> Transaction<TransactionContractCall> {
TransactionContractCall::new(self.0, self.1)
}
#[inline]
pub fn update(self) -> Transaction<TransactionContractUpdate> {
TransactionContractUpdate::new(self.0, self.1)
}
}
pub struct PartialTransactionMessage<'a>(&'a Client, TransactionId);
impl<'a> PartialTransactionMessage<'a> {
#[inline]
pub fn receipt(self) -> Query<TransactionReceipt> {
QueryGetTransactionReceipt::new(self.0, self.1)
}
#[inline]
pub fn record(self) -> Query<TransactionRecord> {
QueryTransactionGetRecord::new(self.0, self.1)
}
}