add docs and introduce TypedMessage

This commit is contained in:
glyph 2021-12-03 11:37:22 +02:00
parent 304fb3592c
commit ee2949f1b8
4 changed files with 126 additions and 58 deletions

View File

@ -1,6 +1,6 @@
use std::process;
use kuska_ssb::api::dto::content::Post;
use kuska_ssb::api::dto::content::TypedMessage;
use golgi::error::GolgiError;
use golgi::sbot::Sbot;
@ -11,14 +11,27 @@ async fn run() -> Result<(), GolgiError> {
let id = sbot_client.whoami().await?;
println!("{}", id);
let post = Post::new(
"glyph from golgi".to_string(),
// mentions
None,
);
let name = TypedMessage::About {
about: id,
name: Some("golgi".to_string()),
title: None,
branch: None,
image: None,
description: None,
location: None,
start_datetime: None,
};
let msg_ref = sbot_client.publish_post(post).await?;
println!("{}", msg_ref);
let name_msg_ref = sbot_client.publish(name).await?;
println!("{}", name_msg_ref);
let post = TypedMessage::Post {
text: "golgi go womp womp".to_string(),
mentions: None,
};
let post_msg_ref = sbot_client.publish(post).await?;
println!("{}", post_msg_ref);
Ok(())
}

View File

@ -1,37 +1,40 @@
/*
use async_std::{io::Read, net::TcpStream};
use std::fmt::Debug;
//! Custom error type for `golgi`.
use kuska_handshake::async_std::BoxStream;
use kuska_sodiumoxide::crypto::sign::ed25519;
use kuska_ssb::api::{dto::WhoAmIOut, ApiCaller};
use kuska_ssb::discovery;
use kuska_ssb::keystore;
use kuska_ssb::keystore::OwnedIdentity;
use kuska_ssb::rpc::{RecvMsg, RequestNo, RpcReader, RpcWriter};
use std::io::Error as IoError;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub fn whoami_res_parse(body: &[u8]) -> Result<WhoAmIOut> {
Ok(serde_json::from_slice(body)?)
}
*/
use base64::DecodeError;
use kuska_handshake::async_std::Error as HandshakeError;
use kuska_ssb::api::Error as ApiError;
use kuska_ssb::feed::Error as FeedError;
use kuska_ssb::rpc::Error as RpcError;
use serde_json::Error as JsonError;
/// A custom error type encapsulating all possible errors for this library.
/// `From` implementations are provided for external error types, allowing
/// the `?` operator to be used on functions which return `Result<_, GolgiError>`.
#[derive(Debug)]
pub enum GolgiError {
DecodeBase64(base64::DecodeError),
/// Failed to decode base64.
DecodeBase64(DecodeError),
/// IO error with context.
Io {
source: std::io::Error,
/// The underlying IO error.
source: IoError,
/// Description of the error context.
context: String,
},
Handshake(kuska_handshake::async_std::Error),
KuskaApi(kuska_ssb::api::Error),
KuskaFeed(kuska_ssb::feed::Error),
KuskaRpc(kuska_ssb::rpc::Error),
// error message returned from the go-sbot
/// Scuttlebutt secret handshake error.
Handshake(HandshakeError),
/// Kuska SSB API error.
Api(ApiError),
/// Kuska SSB feed error.
Feed(FeedError),
/// Kuska SSB RPC error.
Rpc(RpcError),
/// Go-sbot error.
Sbot(String),
SerdeJson(serde_json::Error),
WhoAmI(String),
/// JSON serialization or deserialization error.
SerdeJson(JsonError),
}
impl std::error::Error for GolgiError {
@ -40,12 +43,11 @@ impl std::error::Error for GolgiError {
GolgiError::DecodeBase64(ref err) => Some(err),
GolgiError::Io { ref source, .. } => Some(source),
GolgiError::Handshake(_) => None,
GolgiError::KuskaApi(ref err) => Some(err),
GolgiError::KuskaFeed(ref err) => Some(err),
GolgiError::KuskaRpc(ref err) => Some(err),
GolgiError::Api(ref err) => Some(err),
GolgiError::Feed(ref err) => Some(err),
GolgiError::Rpc(ref err) => Some(err),
GolgiError::Sbot(_) => None,
GolgiError::SerdeJson(ref err) => Some(err),
GolgiError::WhoAmI(_) => None,
}
}
}
@ -57,50 +59,50 @@ impl std::fmt::Display for GolgiError {
GolgiError::DecodeBase64(_) => write!(f, "Failed to decode base64"),
GolgiError::Io { ref context, .. } => write!(f, "IO error: {}", context),
GolgiError::Handshake(ref err) => write!(f, "{}", err),
GolgiError::KuskaApi(_) => write!(f, "SSB API failure"),
GolgiError::KuskaFeed(_) => write!(f, "SSB feed error"),
GolgiError::Api(ref err) => write!(f, "SSB API failure: {}", err),
GolgiError::Feed(ref err) => write!(f, "SSB feed error: {}", err),
// TODO: improve this variant with a context message
// then have the core display msg be: "SSB RPC error: {}", context
GolgiError::KuskaRpc(_) => write!(f, "SSB RPC failure"),
GolgiError::Rpc(ref err) => write!(f, "SSB RPC failure: {}", err),
GolgiError::Sbot(ref err) => write!(f, "Sbot returned an error response: {}", err),
GolgiError::SerdeJson(_) => write!(f, "Failed to serialize JSON slice"),
GolgiError::WhoAmI(ref err) => write!(f, "{}", err),
//GolgiError::WhoAmI(ref err) => write!(f, "{}", err),
}
}
}
impl From<base64::DecodeError> for GolgiError {
fn from(err: base64::DecodeError) -> Self {
impl From<DecodeError> for GolgiError {
fn from(err: DecodeError) -> Self {
GolgiError::DecodeBase64(err)
}
}
impl From<kuska_handshake::async_std::Error> for GolgiError {
fn from(err: kuska_handshake::async_std::Error) -> Self {
impl From<HandshakeError> for GolgiError {
fn from(err: HandshakeError) -> Self {
GolgiError::Handshake(err)
}
}
impl From<kuska_ssb::api::Error> for GolgiError {
fn from(err: kuska_ssb::api::Error) -> Self {
GolgiError::KuskaApi(err)
impl From<ApiError> for GolgiError {
fn from(err: ApiError) -> Self {
GolgiError::Api(err)
}
}
impl From<kuska_ssb::feed::Error> for GolgiError {
fn from(err: kuska_ssb::feed::Error) -> Self {
GolgiError::KuskaFeed(err)
impl From<FeedError> for GolgiError {
fn from(err: FeedError) -> Self {
GolgiError::Feed(err)
}
}
impl From<kuska_ssb::rpc::Error> for GolgiError {
fn from(err: kuska_ssb::rpc::Error) -> Self {
GolgiError::KuskaRpc(err)
impl From<RpcError> for GolgiError {
fn from(err: RpcError) -> Self {
GolgiError::Rpc(err)
}
}
impl From<serde_json::Error> for GolgiError {
fn from(err: serde_json::Error) -> Self {
impl From<JsonError> for GolgiError {
fn from(err: JsonError) -> Self {
GolgiError::SerdeJson(err)
}
}

View File

@ -1,4 +1,28 @@
// #![warn(missing_docs)]
#![warn(missing_docs)]
//! # golgi
//!
//! _The Golgi complex (aka. Golgi apparatus or Golgi body) packages proteins into membrane-bound vesicles inside the cell before the vesicles are sent to their destination._
//!
//! -----
//!
//! Golgi is an experimental Scuttlebutt client which uses the [kuska-ssb](https://github.com/Kuska-ssb) libraries and aims to provide a high-level API for interacting with an sbot instance. Development efforts are currently oriented towards [go-sbot](https://github.com/cryptoscope/ssb) interoperability.
//!
//! ## Example Usage
//!
//! ```rust
//! use golgi::GolgiError;
//! use golgi::sbot::Sbot;
//!
//! pub async fn run() -> Result<(), GolgiError> {
//! let mut sbot_client = Sbot::init(None, None).await?;
//!
//! let id = sbot_client.whoami().await?;
//! println!("{}", id);
//!
//! Ok(())
//! }
//! ```
pub mod error;
pub mod sbot;

View File

@ -1,10 +1,16 @@
//! Sbot type and associated methods.
use async_std::net::TcpStream;
use kuska_handshake::async_std::BoxStream;
use kuska_sodiumoxide::crypto::{auth, sign::ed25519};
use kuska_ssb::{
api::{
dto::{content::Post, CreateHistoryStreamIn},
dto::{
//content::{About, Post},
content::TypedMessage,
CreateHistoryStreamIn,
},
ApiCaller,
},
discovery, keystore,
@ -15,6 +21,8 @@ use kuska_ssb::{
use crate::error::GolgiError;
use crate::utils;
/// The Scuttlebutt identity, keys and configuration parameters for connecting to a local sbot
/// instance, as well as handles for calling RPC methods and receiving responses.
pub struct Sbot {
id: String,
public_key: ed25519::PublicKey,
@ -27,6 +35,9 @@ pub struct Sbot {
}
impl Sbot {
/// Initiate a connection with an sbot instance. Define the IP address, port and network key
/// for the sbot, then retrieve the public key, private key (secret) and identity from the
/// `.ssb-go/secret` file. Open a TCP stream to the sbot and perform the secret handshake. If successful, create a box stream and split it into a writer and reader. Return RPC handles to the sbot as part of the `struct` output.
pub async fn init(ip_port: Option<String>, net_id: Option<String>) -> Result<Sbot, GolgiError> {
let address;
if ip_port.is_none() {
@ -80,6 +91,7 @@ impl Sbot {
})
}
/// Call the `whoami` RPC method and return an `id`.
pub async fn whoami(&mut self) -> Result<String, GolgiError> {
let req_id = self.client.whoami_req_send().await?;
@ -88,15 +100,32 @@ impl Sbot {
.map(|whoami| whoami.id)
}
/// Call the `publish` RPC method and return a message reference.
///
/// # Arguments
///
/// * `msg` - A `TypedMessage` `enum` whose variants include `Pub`, `Post`, `Contact`, `About`,
/// `Channel` and `Vote`. See the `kuska_ssb` documentation for further details such as field
/// names and accepted values for each variant.
pub async fn publish(&mut self, msg: TypedMessage) -> Result<String, GolgiError> {
let req_id = self.client.publish_req_send(msg).await?;
utils::get_async(&mut self.rpc_reader, req_id, utils::publish_res_parse).await
}
/*
pub async fn publish_post(&mut self, post: Post) -> Result<String, GolgiError> {
let req_id = self.client.publish_req_send(post).await?;
utils::get_async(&mut self.rpc_reader, req_id, utils::publish_res_parse).await
}
*/
/// Call the `createHistoryStream` RPC method and print the output.
async fn create_history_stream(&mut self, id: String) -> Result<(), GolgiError> {
let args = CreateHistoryStreamIn::new(id);
let req_id = self.client.create_history_stream_req_send(&args).await?;
// TODO: we should return a vector of messages instead of printing them
utils::print_source_until_eof(&mut self.rpc_reader, req_id, utils::feed_res_parse).await
}
}