Skip to content

Commit

Permalink
feat(rust): add FileCreate, FileUpdate, FileDelete, and FileAppend
Browse files Browse the repository at this point in the history
  • Loading branch information
mehcode committed May 30, 2022
1 parent f7fd9b0 commit 47360e3
Show file tree
Hide file tree
Showing 9 changed files with 419 additions and 3 deletions.
70 changes: 70 additions & 0 deletions sdk/rust/src/file/file_append_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use async_trait::async_trait;
use hedera_proto::services;
use hedera_proto::services::file_service_client::FileServiceClient;
use serde_with::skip_serializing_none;
use tonic::transport::Channel;

use crate::protobuf::ToProtobuf;
use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute};
use crate::{AccountId, FileId, Transaction, TransactionId};

/// Append the given contents to the end of the specified file.
///
pub type FileAppendTransaction = Transaction<FileAppendTransactionData>;

#[skip_serializing_none]
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileAppendTransactionData {
/// The file to which the bytes will be appended.
file_id: Option<FileId>,

/// The bytes that will be appended to the end of the specified file.
contents: Option<Vec<u8>>,
}

impl FileAppendTransaction {
/// Sets the file to which the bytes will be appended.
pub fn file_id(&mut self, id: impl Into<FileId>) -> &mut Self {
self.body.data.file_id = Some(id.into());
self
}

/// Sets the bytes that will be appended to the end of the specified file.
pub fn contents(&mut self, contents: Vec<u8>) -> &mut Self {
self.body.data.contents = Some(contents);
self
}
}

#[async_trait]
impl TransactionExecute for FileAppendTransactionData {
async fn execute(
&self,
channel: Channel,
request: services::Transaction,
) -> Result<tonic::Response<services::TransactionResponse>, tonic::Status> {
FileServiceClient::new(channel).append_content(request).await
}
}

impl ToTransactionDataProtobuf for FileAppendTransactionData {
fn to_transaction_data_protobuf(
&self,
_node_account_id: AccountId,
_transaction_id: &TransactionId,
) -> services::transaction_body::Data {
let file_id = self.file_id.as_ref().map(FileId::to_protobuf);

services::transaction_body::Data::FileAppend(services::FileAppendTransactionBody {
file_id,
contents: self.contents.clone().unwrap_or_default(),
})
}
}

impl From<FileAppendTransactionData> for AnyTransactionData {
fn from(transaction: FileAppendTransactionData) -> Self {
Self::FileAppend(transaction)
}
}
107 changes: 107 additions & 0 deletions sdk/rust/src/file/file_create_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use async_trait::async_trait;
use hedera_proto::services;
use hedera_proto::services::file_service_client::FileServiceClient;
use itertools::Itertools;
use serde_with::skip_serializing_none;
use time::OffsetDateTime;
use tonic::transport::Channel;

use crate::protobuf::ToProtobuf;
use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute};
use crate::{AccountId, Key, Transaction, TransactionId};

/// Create a new file, containing the given contents.
pub type FileCreateTransaction = Transaction<FileCreateTransactionData>;

#[skip_serializing_none]
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileCreateTransactionData {
/// The memo associated with the file.
#[serde(skip_serializing_if = "String::is_empty")]
file_memo: String,

/// All keys at the top level of a key list must sign to create or
/// modify the file. Any one of the keys at the top level key list
/// can sign to delete the file.
keys: Option<Vec<Key>>,

/// The bytes that are to be the contents of the file.
contents: Option<Vec<u8>>,

/// The time at which this file should expire.
expires_at: Option<OffsetDateTime>,
}

impl FileCreateTransaction {
/// Sets the memo associated with the file.
pub fn file_memo(&mut self, memo: impl Into<String>) -> &mut Self {
self.body.data.file_memo = memo.into();
self
}

/// Sets the bytes that are to be the contents of the file.
pub fn contents(&mut self, contents: Vec<u8>) -> &mut Self {
self.body.data.contents = Some(contents);
self
}

/// Sets the keys for this file.
///
/// All keys at the top level of a key list must sign to create or
/// modify the file. Any one of the keys at the top level key list
/// can sign to delete the file.
///
pub fn keys<K: Into<Key>>(&mut self, keys: impl IntoIterator<Item = K>) -> &mut Self {
self.body.data.keys = Some(keys.into_iter().map_into().collect());
self
}

/// Sets the time at which this file should expire.
pub fn expires_at(&mut self, at: OffsetDateTime) -> &mut Self {
self.body.data.expires_at = Some(at);
self
}
}

#[async_trait]
impl TransactionExecute for FileCreateTransactionData {
async fn execute(
&self,
channel: Channel,
request: services::Transaction,
) -> Result<tonic::Response<services::TransactionResponse>, tonic::Status> {
FileServiceClient::new(channel).create_file(request).await
}
}

impl ToTransactionDataProtobuf for FileCreateTransactionData {
fn to_transaction_data_protobuf(
&self,
_node_account_id: AccountId,
_transaction_id: &TransactionId,
) -> services::transaction_body::Data {
let expiration_time = self.expires_at.as_ref().map(OffsetDateTime::to_protobuf);

let keys =
self.keys.as_deref().unwrap_or_default().iter().map(Key::to_protobuf).collect_vec();

let keys = services::KeyList { keys };

services::transaction_body::Data::FileCreate(services::FileCreateTransactionBody {
expiration_time,
keys: Some(keys),
contents: self.contents.clone().unwrap_or_default(),
shard_id: None,
realm_id: None,
new_realm_admin_key: None,
memo: self.file_memo.clone(),
})
}
}

impl From<FileCreateTransactionData> for AnyTransactionData {
fn from(transaction: FileCreateTransactionData) -> Self {
Self::FileCreate(transaction)
}
}
64 changes: 64 additions & 0 deletions sdk/rust/src/file/file_delete_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use async_trait::async_trait;
use hedera_proto::services;
use hedera_proto::services::file_service_client::FileServiceClient;
use serde_with::skip_serializing_none;
use tonic::transport::Channel;

use crate::protobuf::ToProtobuf;
use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute};
use crate::{AccountId, FileId, Transaction, TransactionId};

/// Delete the given file.
///
/// After deletion, it will be marked as deleted and will have no contents.
/// Information about it will continue to exist until it expires.
///
pub type FileDeleteTransaction = Transaction<FileDeleteTransactionData>;

#[skip_serializing_none]
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileDeleteTransactionData {
/// The file to delete. It will be marked as deleted until it expires.
/// Then it will disappear.
file_id: Option<FileId>,
}

impl FileDeleteTransaction {
/// Set the file to delete.
pub fn file_id(&mut self, id: impl Into<FileId>) -> &mut Self {
self.body.data.file_id = Some(id.into());
self
}
}

#[async_trait]
impl TransactionExecute for FileDeleteTransactionData {
async fn execute(
&self,
channel: Channel,
request: services::Transaction,
) -> Result<tonic::Response<services::TransactionResponse>, tonic::Status> {
FileServiceClient::new(channel).delete_file(request).await
}
}

impl ToTransactionDataProtobuf for FileDeleteTransactionData {
fn to_transaction_data_protobuf(
&self,
_node_account_id: AccountId,
_transaction_id: &TransactionId,
) -> services::transaction_body::Data {
let file_id = self.file_id.as_ref().map(FileId::to_protobuf);

services::transaction_body::Data::FileDelete(services::FileDeleteTransactionBody {
file_id,
})
}
}

impl From<FileDeleteTransactionData> for AnyTransactionData {
fn from(transaction: FileDeleteTransactionData) -> Self {
Self::FileDelete(transaction)
}
}
14 changes: 13 additions & 1 deletion sdk/rust/src/file/file_id.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use hedera_proto::services;

use crate::FromProtobuf;
use crate::{FromProtobuf, ToProtobuf};

/// The unique identifier for a file on Hedera.
#[derive(Debug, serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq, Clone, Copy)]
Expand All @@ -18,3 +18,15 @@ impl FromProtobuf for FileId {
Ok(Self { num: pb.file_num as u64, shard: pb.shard_num as u64, realm: pb.realm_num as u64 })
}
}

impl ToProtobuf for FileId {
type Protobuf = services::FileId;

fn to_protobuf(&self) -> Self::Protobuf {
services::FileId {
file_num: self.num as i64,
realm_num: self.realm as i64,
shard_num: self.shard as i64,
}
}
}
118 changes: 118 additions & 0 deletions sdk/rust/src/file/file_update_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use async_trait::async_trait;
use hedera_proto::services;
use hedera_proto::services::file_service_client::FileServiceClient;
use itertools::Itertools;
use serde_with::skip_serializing_none;
use time::OffsetDateTime;
use tonic::transport::Channel;

use crate::protobuf::ToProtobuf;
use crate::transaction::{AnyTransactionData, ToTransactionDataProtobuf, TransactionExecute};
use crate::{AccountId, FileId, Key, Transaction, TransactionId};

/// Modify the metadata and/or the contents of a file.
///
/// If a field is not set in the transaction body, the
/// corresponding file attribute will be unchanged.
///
pub type FileUpdateTransaction = Transaction<FileUpdateTransactionData>;

#[skip_serializing_none]
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileUpdateTransactionData {
/// The file ID which is being updated in this transaction.
file_id: Option<FileId>,

/// The memo associated with the file.
file_memo: Option<String>,

/// All keys at the top level of a key list must sign to create or
/// modify the file. Any one of the keys at the top level key list
/// can sign to delete the file.
keys: Option<Vec<Key>>,

/// The bytes that are to be the contents of the file.
contents: Option<Vec<u8>>,

/// The time at which this file should expire.
expires_at: Option<OffsetDateTime>,
}

impl FileUpdateTransaction {
/// Set the file ID which is being updated.
pub fn file_id(&mut self, id: impl Into<FileId>) -> &mut Self {
self.body.data.file_id = Some(id.into());
self
}

/// Sets the memo associated with the file.
pub fn file_memo(&mut self, memo: impl Into<String>) -> &mut Self {
self.body.data.file_memo = Some(memo.into());
self
}

/// Sets the bytes that are to be the contents of the file.
pub fn contents(&mut self, contents: Vec<u8>) -> &mut Self {
self.body.data.contents = Some(contents);
self
}

/// Sets the keys for this file.
///
/// All keys at the top level of a key list must sign to create or
/// modify the file. Any one of the keys at the top level key list
/// can sign to delete the file.
///
pub fn keys<K: Into<Key>>(&mut self, keys: impl IntoIterator<Item = K>) -> &mut Self {
self.body.data.keys = Some(keys.into_iter().map_into().collect());
self
}

/// Sets the time at which this file should expire.
pub fn expires_at(&mut self, at: OffsetDateTime) -> &mut Self {
self.body.data.expires_at = Some(at);
self
}
}

#[async_trait]
impl TransactionExecute for FileUpdateTransactionData {
async fn execute(
&self,
channel: Channel,
request: services::Transaction,
) -> Result<tonic::Response<services::TransactionResponse>, tonic::Status> {
FileServiceClient::new(channel).update_file(request).await
}
}

impl ToTransactionDataProtobuf for FileUpdateTransactionData {
fn to_transaction_data_protobuf(
&self,
_node_account_id: AccountId,
_transaction_id: &TransactionId,
) -> services::transaction_body::Data {
let file_id = self.file_id.as_ref().map(FileId::to_protobuf);
let expiration_time = self.expires_at.as_ref().map(OffsetDateTime::to_protobuf);

let keys =
self.keys.as_deref().unwrap_or_default().iter().map(Key::to_protobuf).collect_vec();

let keys = services::KeyList { keys };

services::transaction_body::Data::FileUpdate(services::FileUpdateTransactionBody {
file_id,
expiration_time,
keys: Some(keys),
contents: self.contents.clone().unwrap_or_default(),
memo: self.file_memo.clone(),
})
}
}

impl From<FileUpdateTransactionData> for AnyTransactionData {
fn from(transaction: FileUpdateTransactionData) -> Self {
Self::FileUpdate(transaction)
}
}
Loading

0 comments on commit 47360e3

Please sign in to comment.