Parse json into SsbMessageValue

This commit is contained in:
notplants 2021-11-16 15:41:20 +01:00
parent bbb3a41662
commit c3d00837cb
3 changed files with 177 additions and 50 deletions

View File

@ -11,3 +11,7 @@ license = "LGPL-3.0-only"
[dependencies]
dirs = "4.0.0"
regex = "1.5.4"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
ssb-legacy-msg-data = "0.1.4"
ssb-multiformats = "0.4.2"

View File

@ -13,6 +13,7 @@ pub enum SbotCliError {
Blob(String),
Contact(String),
GetAboutMsgs(String),
GetRelationship(String),
Invite(String),
Publish(String),
WhoAmI(String),
@ -22,6 +23,7 @@ pub enum SbotCliError {
InvalidUtf8(Utf8Error),
// external errors
InvalidRegex(RegexError),
ParseJson(serde_json::Error),
NoHomeDir,
}
@ -38,6 +40,9 @@ impl fmt::Display for SbotCliError {
}
SbotCliError::GetAboutMsgs(ref err) => {
write!(f, "{}", err)
},
SbotCliError::GetRelationship(ref err) => {
write!(f, "{}", err)
}
SbotCliError::Invite(ref err) => {
write!(f, "{}", err)
@ -60,6 +65,9 @@ impl fmt::Display for SbotCliError {
SbotCliError::InvalidRegex(ref err) => {
write!(f, "{}", err)
}
SbotCliError::ParseJson(ref err) => {
write!(f, "{}", err)
}
SbotCliError::NoHomeDir => {
write!(
f,
@ -93,3 +101,6 @@ impl From<RegexError> for SbotCliError {
SbotCliError::InvalidRegex(err)
}
}

View File

@ -35,6 +35,10 @@
//! ## License
//!
//! LGPL-3.0.
use serde_json::Value;
use serde::{Serialize, Deserialize};
use ssb_multiformats::multihash::Multihash;
use ssb_legacy_msg_data::LegacyF64;
pub mod error;
mod utils;
@ -42,6 +46,43 @@ mod utils;
pub use crate::error::SbotCliError;
use std::{ffi::OsString, path::PathBuf, process::Command, result::Result};
/// HELPER STRUCTS FOR GETTING AND SETTING VALUES FROM SBOT
/// A struct which represents any ssb message in the log.
///
/// Modified from https://github.com/sunrise-choir/ssb-validate.
///
/// Every ssb message in the log has a content field, as a first-level key.
/// This content field is further parsed based on the specific type of message.
///
/// Data type representing the `value` of a message object (`KVT`). More information concerning the
/// data model can be found
/// in the [`Metadata` documentation](https://spec.scuttlebutt.nz/feed/messages.html#metadata).
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct SsbMessageValue {
pub previous: Option<Multihash>,
pub author: String,
pub sequence: u64,
pub timestamp: LegacyF64,
pub hash: String,
pub content: Value,
pub signature: String,
}
/// A struct which represents a relationship with another user (peer).
/// Any combination of following and blocking is possible except
/// for following=true AND blocking=true.
#[derive(Serialize, Deserialize, Debug)]
pub struct PeerRelationship {
following: bool,
blocking: bool
}
/// SBOT METHODS
/// An `sbotcli` instance with associated methods for querying a Go sbot server.
pub struct Sbot {
/// The path to the `sbotcli` binary.
@ -57,8 +98,8 @@ impl Sbot {
///
/// # Arguments
///
/// * `sbotcli_path` - an optional string slice representing a file path
/// * `sbot_working_dir` - an optional string slice representing a directory path
/// * `sbotcli_path` - an optional string slice representing the file path to the sbotcli binary
/// * `sbot_working_dir` - an optional string slice representing a directory path where sbotcli stores its data
///
pub fn init(
sbotcli_path: Option<&str>,
@ -110,7 +151,7 @@ impl Sbot {
///
/// # Arguments
///
/// * `file_path` - A string slice representing a file path
/// * `file_path` - A string slice representing a file path to the blob to be added
///
pub fn add_blob(&self, file_path: &str) -> Result<String, SbotCliError> {
let output = Command::new(&self.sbotcli_path)
@ -131,65 +172,154 @@ impl Sbot {
/* CONTACTS */
/// Follow a peer.
/// Set relationship with a peer.
///
/// Calls `sbotcli publish contact --following [id]`. On success: trims the trailing whitespace from `stdout`
/// A relationship has two properties:
/// - following (either true or false)
/// - blocking (either true or false)
///
/// This is an idempotent function which sets the relationship with a peer for following and blocking,
/// based on the arguments that are passed in.
///
/// It uses `sbotcli publish contact [id]`. Passing the --following and/or --blocking flags,
/// if following or blocking is set to true, respectively.
///
/// On success: trims the trailing whitespace from `stdout`
/// and returns the message reference. On error: returns the `stderr` output with a description.
///
/// # Arguments
///
/// * `id` - A string slice representing an id (profile reference / public key)
/// * `id` - A string slice representing an id (profile reference / public key) of the account to follow
/// * `following` - a boolean representing whether to follow this account or not
/// * `blocking` - a boolean representing whether to block this account or not
///
pub fn follow(&self, id: &str) -> Result<String, SbotCliError> {
let output = Command::new(&self.sbotcli_path)
pub fn set_relationship(&self, id: &str, following: bool, blocking: bool) -> Result<String, SbotCliError> {
let mut command = Command::new(&self.sbotcli_path);
command
.arg("publish")
.arg("contact")
.arg("--following")
.arg("contact");
if following {
command.arg("--following");
}
if blocking {
command.arg("--blocking");
}
let output = command
.arg(id)
.output()?;
if output.status.success() {
let stdout = String::from_utf8(output.stdout)?;
let msg_ref = stdout.trim_end().to_string();
Ok(msg_ref)
} else {
let stderr = std::str::from_utf8(&output.stderr)?;
Err(SbotCliError::Contact(format!(
"Error following peer: {}",
"Error setting relationship with peer: {}",
stderr
)))
}
}
/// Block a peer.
/// Get the latest value of a current relationship with a peer.
///
/// We determine the value of the relationship by looking up the most recent contact
/// message made about that peer.
///
/// If no contact message is found, then a relationship of following=false blocking=false
/// is returned.
///
/// Calls `sbotcli bytype --limit 1 --reverse contact`.
///
/// On success: parses a PeerRelationship from the ssb_message or returns one with default values.
/// On error: returns the `stderr` output with a description.
///
/// # arguments
///
/// * `id` - a string slice representing the id (profile reference / public key)
/// of the peer to look up the relationship with.
///
pub fn get_relationship(&self, _id: &str) -> Result<PeerRelationship, SbotCliError> {
// TODO: need to filter this query down to the actual user it should be for
// right now this is not a real query
let output = Command::new(&self.sbotcli_path)
.arg("bytype")
.arg("--limit")
.arg("1")
.arg("--reverse")
.arg("contact")
.output()?;
if output.status.success() {
let stdout = String::from_utf8(output.stdout)?;
println!("stdout: {:?}", stdout);
let ssb_message : SsbMessageValue = serde_json::from_str(&stdout).map_err(|err| {
SbotCliError::GetRelationship(format!("error deserializing ssb message while getting relationship to peer: {}", err))
})?;
let relationship : PeerRelationship = serde_json::from_value(ssb_message.content).map_err(|err| {
SbotCliError::GetRelationship(format!("error deserializing ssb message content while getting relationship to peer: {}", err))
})?;
Ok(relationship)
} else {
let stderr = std::str::from_utf8(&output.stderr)?;
Err(SbotCliError::GetRelationship(format!(
"error getting relationship to peer: {}",
stderr
)))
}
}
/// Follow a peer. Does not change whatever blocking relationship user already has with this peer.
///
/// On success: trims the trailing whitespace from `stdout`
/// and returns the message reference. On error: returns the `stderr` output with a description.
///
/// # Arguments
///
/// * `id` - A string slice representing an id (profile reference / public key) of the account to follow
///
pub fn follow(&self, id: &str) -> Result<String, SbotCliError> {
let current_relationship = self.get_relationship(id)?;
self.set_relationship(id, true, current_relationship.blocking)
}
/// Block a peer. Does not change whatever following relationship user already has with this peer.
///
/// Calls `sbotcli publish contact --blocking [id]`. On success: trims the trailing whitespace from `stdout` and returns the message reference. On error: returns the `stderr` output with a description.
///
/// # Arguments
///
/// * `id` - A string slice representing an id (profile reference / public key)
/// * `id` - A string slice representing an id (profile reference / public key) of the account to block
///
pub fn block(&self, id: &str) -> Result<String, SbotCliError> {
let output = Command::new(&self.sbotcli_path)
.arg("publish")
.arg("contact")
.arg("--blocking")
.arg(id)
.output()?;
if output.status.success() {
let stdout = String::from_utf8(output.stdout)?;
let msg_ref = stdout.trim_end().to_string();
Ok(msg_ref)
} else {
let stderr = std::str::from_utf8(&output.stderr)?;
Err(SbotCliError::Contact(format!(
"Error blocking peer: {}",
stderr
)))
}
let current_relationship = self.get_relationship(id)?;
self.set_relationship(id, current_relationship.following, true)
}
/// Unfollow a peer. Does not change whatever blocking relationship user already has with this peer.
///
/// On success: trims the trailing whitespace from `stdout`
/// and returns the message reference. On error: returns the `stderr` output with a description.
///
/// # Arguments
///
/// * `id` - A string slice representing an id (profile reference / public key) of the account to follow
///
pub fn unfollow(&self, id: &str) -> Result<String, SbotCliError> {
let current_relationship = self.get_relationship(id)?;
self.set_relationship(id, false, current_relationship.blocking)
}
/// Unblock a peer. Does not change whatever following relationship user already has with this peer.
///
/// Calls `sbotcli publish contact --blocking [id]`. On success: trims the trailing whitespace from `stdout` and returns the message reference. On error: returns the `stderr` output with a description.
///
/// # Arguments
///
/// * `id` - A string slice representing an id (profile reference / public key) of the account to block
///
pub fn unblock(&self, id: &str) -> Result<String, SbotCliError> {
let current_relationship = self.get_relationship(id)?;
self.set_relationship(id, current_relationship.following, false)
}
/* GET ABOUT MESSAGES */
@ -551,22 +681,4 @@ mod tests {
let result = 2 + 2;
assert_eq!(result, 4);
}
use crate::Sbot;
#[test]
fn new_test() {
let sbot = Sbot::init(Some("/Users/notplants/go/bin/sbotcli"), None).unwrap();
let self_id = sbot.whoami().unwrap();
let profile_image = "/Users/notplants/computer/docs/avi/plants.png";
let result = sbot.add_blob(profile_image);
// sbot.publish_image(profile_image);
// let result = sbot.get_about_message("image", &self_id);
// println!("get_profile_image");
match result {
Ok(res) => println!("success: {:?}", res),
Err(err) => println!("error: {:?}", err)
}
assert_eq!(2, 2);
}
}