hive-rs 0.1.0

A Rust client library for the Hive blockchain with 1:1 dhive API parity
Documentation
use std::sync::Arc;

use serde::de::DeserializeOwned;
use serde_json::{json, Value};

use crate::client::ClientInner;
use crate::error::Result;
use crate::types::{
    AccountNotifsQuery, AccountPostsQuery, CommunityDetail, CommunityQuery, CommunityRole,
    Discussion, ListCommunitiesQuery, Notification, PostsQuery,
};

#[derive(Debug, Clone)]
pub struct HivemindApi {
    client: Arc<ClientInner>,
}

impl HivemindApi {
    pub(crate) fn new(client: Arc<ClientInner>) -> Self {
        Self { client }
    }

    async fn call<T: DeserializeOwned>(&self, method: &str, params: Value) -> Result<T> {
        self.client.call("bridge", method, params).await
    }

    pub async fn get_ranked_posts(&self, query: &PostsQuery) -> Result<Vec<Discussion>> {
        self.call("get_ranked_posts", json!([query])).await
    }

    pub async fn get_account_posts(&self, query: &AccountPostsQuery) -> Result<Vec<Discussion>> {
        self.call("get_account_posts", json!([query])).await
    }

    pub async fn get_community(&self, query: &CommunityQuery) -> Result<CommunityDetail> {
        self.call("get_community", json!([query])).await
    }

    pub async fn list_communities(
        &self,
        query: &ListCommunitiesQuery,
    ) -> Result<Vec<CommunityDetail>> {
        self.call("list_communities", json!([query])).await
    }

    pub async fn get_community_roles(
        &self,
        community: &str,
        last: Option<&str>,
        limit: Option<u32>,
    ) -> Result<Vec<CommunityRole>> {
        self.call("get_community_roles", json!([community, last, limit]))
            .await
    }

    pub async fn get_account_notifications(
        &self,
        query: &AccountNotifsQuery,
    ) -> Result<Vec<Notification>> {
        self.call("get_account_notifications", json!([query])).await
    }

    pub async fn get_discussion(&self, author: &str, permlink: &str) -> Result<Discussion> {
        self.call("get_discussion", json!([author, permlink])).await
    }

    pub async fn get_post(&self, author: &str, permlink: &str) -> Result<Discussion> {
        self.call("get_post", json!([author, permlink])).await
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use std::time::Duration;

    use serde_json::json;
    use wiremock::matchers::{body_partial_json, method};
    use wiremock::{Mock, MockServer, ResponseTemplate};

    use crate::api::HivemindApi;
    use crate::client::{ClientInner, ClientOptions};
    use crate::transport::{BackoffStrategy, FailoverTransport};
    use crate::types::PostsQuery;

    #[tokio::test]
    async fn get_ranked_posts_uses_bridge_namespace() {
        let server = MockServer::start().await;
        Mock::given(method("POST"))
            .and(body_partial_json(json!({
                "method": "call",
                "params": ["bridge", "get_ranked_posts"]
            })))
            .respond_with(ResponseTemplate::new(200).set_body_json(json!({
                "id": 0,
                "jsonrpc": "2.0",
                "result": []
            })))
            .mount(&server)
            .await;

        let transport = Arc::new(
            FailoverTransport::new(
                &[server.uri()],
                Duration::from_secs(2),
                1,
                BackoffStrategy::default(),
            )
            .expect("transport should initialize"),
        );
        let inner = Arc::new(ClientInner::new(transport, ClientOptions::default()));
        let api = HivemindApi::new(inner);

        let posts = api
            .get_ranked_posts(&PostsQuery::default())
            .await
            .expect("rpc should succeed");
        assert!(posts.is_empty());
    }
}