Add function for get_latest_about_message
This commit is contained in:
commit
be19fcbfc4
16
src/error.rs
16
src/error.rs
|
@ -1,6 +1,7 @@
|
|||
//! Custom error type for `golgi`.
|
||||
|
||||
use std::io::Error as IoError;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use base64::DecodeError;
|
||||
use kuska_handshake::async_std::Error as HandshakeError;
|
||||
|
@ -37,6 +38,11 @@ pub enum GolgiError {
|
|||
SerdeJson(JsonError),
|
||||
/// Error decoding typed ssb message from content.
|
||||
ContentTypeDecode(String),
|
||||
/// Error decoding UTF8 string from bytes
|
||||
Utf8Parse {
|
||||
/// The underlying parse error.
|
||||
source: Utf8Error,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::error::Error for GolgiError {
|
||||
|
@ -50,7 +56,8 @@ impl std::error::Error for GolgiError {
|
|||
GolgiError::Rpc(ref err) => Some(err),
|
||||
GolgiError::Sbot(_) => None,
|
||||
GolgiError::SerdeJson(ref err) => Some(err),
|
||||
GolgiError::ContentTypeDecode(ref _err) => None,
|
||||
GolgiError::ContentTypeDecode(_) => None,
|
||||
GolgiError::Utf8Parse{ ref source} => Some(source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +82,7 @@ impl std::fmt::Display for GolgiError {
|
|||
"Failed to decode typed message from ssb message content: {}",
|
||||
err
|
||||
),
|
||||
GolgiError::Utf8Parse{ source } => write!(f, "Failed to deserialize UTF8 from bytes: {}", source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,3 +122,9 @@ impl From<JsonError> for GolgiError {
|
|||
GolgiError::SerdeJson(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for GolgiError {
|
||||
fn from(err: Utf8Error) -> Self {
|
||||
GolgiError::Utf8Parse { source: err}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ impl SsbMessageValue {
|
|||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct SsbKVT {
|
||||
pub struct SsbMessageKVT {
|
||||
pub key: String,
|
||||
pub value: SsbMessageValue,
|
||||
pub timestamp: f64,
|
||||
|
|
82
src/sbot.rs
82
src/sbot.rs
|
@ -14,7 +14,7 @@ use kuska_ssb::{
|
|||
};
|
||||
|
||||
use crate::error::GolgiError;
|
||||
use crate::messages::{SsbKVT, SsbMessageContent, SsbMessageValue, SsbMessageContentType};
|
||||
use crate::messages::{SsbMessageKVT, SsbMessageContent, SsbMessageValue, SsbMessageContentType};
|
||||
use crate::utils;
|
||||
|
||||
// re-export types from kuska
|
||||
|
@ -91,10 +91,10 @@ impl Sbot {
|
|||
/// Call the `partialReplication getSubset` RPC method and return a vector
|
||||
/// of messages as KVTs (key, value, timestamp).
|
||||
// TODO: add args for `descending` and `page` (max number of msgs in response)
|
||||
pub async fn get_subset(&mut self, query: SubsetQuery) -> Result<Vec<SsbKVT>, GolgiError> {
|
||||
pub async fn get_subset(&mut self, query: SubsetQuery) -> Result<Vec<SsbMessageKVT>, GolgiError> {
|
||||
let req_id = self.client.getsubset_req_send(query).await?;
|
||||
|
||||
utils::get_async_until_eof(&mut self.rpc_reader, req_id, utils::kvt_res_parse).await
|
||||
utils::get_source_until_eof(&mut self.rpc_reader, req_id, utils::kvt_res_parse).await
|
||||
}
|
||||
|
||||
/// Call the `whoami` RPC method and return an `id`.
|
||||
|
@ -104,13 +104,6 @@ impl Sbot {
|
|||
utils::get_async(&mut self.rpc_reader, req_id, utils::string_res_parse).await
|
||||
}
|
||||
|
||||
/// Call the `createLogStream` RPC method and return a Vec<Message>
|
||||
pub async fn log(&mut self) -> Result<String, GolgiError> {
|
||||
let req_id = self.client.log_req_send().await?;
|
||||
|
||||
utils::get_async(&mut self.rpc_reader, req_id, utils::string_res_parse).await
|
||||
}
|
||||
|
||||
/// Call the `publish` RPC method and return a message reference.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -156,14 +149,32 @@ impl Sbot {
|
|||
self.publish(msg).await
|
||||
}
|
||||
|
||||
/// Wrapper for publish which constructs and publishes an about name message appropriately from a string.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - A reference to a string slice which represents the text to be published as an about name.
|
||||
pub async fn publish_name(&mut self, name: &str) -> Result<String, GolgiError> {
|
||||
let msg = SsbMessageContent::About {
|
||||
about: self.id.to_string(),
|
||||
name: Some(name.to_string()),
|
||||
title: None,
|
||||
branch: None,
|
||||
image: None,
|
||||
description: None,
|
||||
location: None,
|
||||
start_datetime: None,
|
||||
};
|
||||
self.publish(msg).await
|
||||
}
|
||||
|
||||
/// Get the about messages for a particular user in order of recency.
|
||||
pub async fn get_about_messages(&mut self, ssb_id: String) -> Result<Vec<SsbMessageValue>, GolgiError> {
|
||||
pub async fn get_about_messages(&mut self, ssb_id: &str) -> Result<Vec<SsbMessageValue>, GolgiError> {
|
||||
let query = SubsetQuery::Author{
|
||||
op: "author".to_string(),
|
||||
feed: self.id.to_string(),
|
||||
feed: ssb_id.to_string(),
|
||||
};
|
||||
let kvts: Vec<SsbKVT> = self.get_subset(query).await?;
|
||||
let kvts: Vec<SsbMessageKVT> = self.get_subset(query).await?;
|
||||
let messages: Vec<SsbMessageValue> = kvts.into_iter().map(|kvt| kvt.value).collect();
|
||||
// TODO: after fixing sbot regression,
|
||||
// change this subset query to filter by type about in addition to author
|
||||
|
@ -174,14 +185,14 @@ impl Sbot {
|
|||
}).collect();
|
||||
// TODO: use subset query to order messages instead of doing it this way
|
||||
about_messages.sort_by(|a, b| {
|
||||
a.timestamp.partial_cmp(&b.timestamp).unwrap()
|
||||
b.timestamp.partial_cmp(&a.timestamp).unwrap()
|
||||
});
|
||||
// return about messages
|
||||
Ok(about_messages)
|
||||
}
|
||||
|
||||
/// Get the latest description for a particular user from their about messages.
|
||||
pub async fn get_description(&mut self, ssb_id: String) -> Result<String, GolgiError> {
|
||||
pub async fn get_description(&mut self, ssb_id: &str) -> Result<Option<String>, GolgiError> {
|
||||
// vector of about messages with most recent at the front of the vector
|
||||
let about_messages = self.get_about_messages(ssb_id).await?;
|
||||
// iterate through the vector looking for an about message with a description
|
||||
|
@ -192,7 +203,7 @@ impl Sbot {
|
|||
SsbMessageContent::About{description, ..} => {
|
||||
match description {
|
||||
Some(description_text) => {
|
||||
return Ok(description_text)
|
||||
return Ok(Some(description_text))
|
||||
},
|
||||
None => {
|
||||
continue
|
||||
|
@ -203,7 +214,42 @@ impl Sbot {
|
|||
}
|
||||
}
|
||||
// if no about message with a description was found, then return the empty string
|
||||
Ok("".to_string())
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Get value of latest about message with given key from given user
|
||||
pub async fn get_latest_about_message(&mut self, ssb_id: &str, key: &str) -> Result<Option<String>, GolgiError> {
|
||||
// vector of about messages with most recent at the front of the vector
|
||||
let about_messages = self.get_about_messages(ssb_id).await?;
|
||||
// iterate through the vector looking for an about message with the given key
|
||||
// the first one we find is the most recent
|
||||
for msg in about_messages {
|
||||
let value = msg.content.get(key);
|
||||
match value {
|
||||
Some(val) => {
|
||||
let val_as_str_option = val.as_str();
|
||||
match val_as_str_option {
|
||||
Some(val_as_str) => {
|
||||
return Ok(Some(val_as_str.to_string()))
|
||||
},
|
||||
None => {
|
||||
// if it is an improperly formatted field (not a string)
|
||||
// then just continue
|
||||
continue
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
// if no about message with given key was found, then return None
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn get_name(&mut self, ssb_id: &str) -> Result<Option<String>, GolgiError> {
|
||||
self.get_latest_about_message(ssb_id, "name").await
|
||||
}
|
||||
|
||||
/// Call the `createHistoryStream` RPC method and return a vector
|
||||
|
@ -214,6 +260,6 @@ impl Sbot {
|
|||
) -> Result<Vec<SsbMessageValue>, GolgiError> {
|
||||
let args = CreateHistoryStreamIn::new(id);
|
||||
let req_id = self.client.create_history_stream_req_send(&args).await?;
|
||||
utils::get_async_until_eof(&mut self.rpc_reader, req_id, utils::ssb_message_res_parse).await
|
||||
utils::get_source_until_eof(&mut self.rpc_reader, req_id, utils::ssb_message_res_parse).await
|
||||
}
|
||||
}
|
||||
|
|
11
src/utils.rs
11
src/utils.rs
|
@ -6,16 +6,16 @@ use kuska_ssb::rpc::{RecvMsg, RequestNo, RpcReader};
|
|||
use serde_json::Value;
|
||||
|
||||
use crate::error::GolgiError;
|
||||
use crate::messages::{SsbKVT, SsbMessageValue};
|
||||
use crate::messages::{SsbMessageKVT, SsbMessageValue};
|
||||
|
||||
/// Function to parse an array of bytes (returned by an rpc call) into a KVT.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `body` - An array of u8 to be parsed.
|
||||
pub fn kvt_res_parse(body: &[u8]) -> Result<SsbKVT, GolgiError> {
|
||||
pub fn kvt_res_parse(body: &[u8]) -> Result<SsbMessageKVT, GolgiError> {
|
||||
let value: Value = serde_json::from_slice(body)?;
|
||||
let kvt: SsbKVT = serde_json::from_value(value)?;
|
||||
let kvt: SsbMessageKVT = serde_json::from_value(value)?;
|
||||
Ok(kvt)
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,7 @@ pub fn kvt_res_parse(body: &[u8]) -> Result<SsbKVT, GolgiError> {
|
|||
///
|
||||
/// * `body` - An array of u8 to be parsed.
|
||||
pub fn string_res_parse(body: &[u8]) -> Result<String, GolgiError> {
|
||||
// TODO: cleanup with proper error handling etc.
|
||||
Ok(std::str::from_utf8(body).unwrap().to_string())
|
||||
Ok(std::str::from_utf8(body)?.to_string())
|
||||
}
|
||||
|
||||
/// Function to parse an array of bytes (returned by an rpc call) into a serde_json::Value.
|
||||
|
@ -104,7 +103,7 @@ where
|
|||
/// * `f` - A function which takes in an array of u8 and returns a Result<T, GolgiError>.
|
||||
/// This is a function which parses the response from the RpcReader. T is a generic type,
|
||||
/// so this parse function can return multiple possible types (String, json, custom struct etc.)
|
||||
pub async fn get_async_until_eof<'a, R, T, F>(
|
||||
pub async fn get_source_until_eof<'a, R, T, F>(
|
||||
rpc_reader: &mut RpcReader<R>,
|
||||
req_no: RequestNo,
|
||||
f: F,
|
||||
|
|
Loading…
Reference in New Issue