diff --git a/src/blobs.rs b/src/blobs.rs new file mode 100644 index 0000000..a4fb125 --- /dev/null +++ b/src/blobs.rs @@ -0,0 +1,56 @@ +//! Blob utilities which do not require RPC calls. +//! +//! Offers the following functions: +//! +//! - [`get_blob_path()`] + +use crate::error::GolgiError; + +/// Lookup the filepath for a blob. +/// +/// # Example +/// +/// ```rust +/// use golgi::blobs; +/// +/// fn lookup_blob_path() -> Result<(), GolgiError> { +/// let blob_ref = "&JlJHc9yeG1EpZA9fIPGLzUKDH0FeR39Ai57euhKT1G8=.sha256"; +/// +/// let blob_path = blobs::get_blob_path(blob_ref)?; +/// +/// println!("{}", blob_path); +/// +/// // 26/524773dc9e1b5129640f5f20f18bcd42831f415e477f408b9edeba1293d46f +/// +/// Ok(()) +/// } +/// ``` +pub fn get_blob_path(blob_id: &str) -> Result { + let dot_index = blob_id.rfind('.').ok_or_else(|| { + GolgiError::SigilLink(format!( + "Invalid blob ID; no dot index was found: {}", + blob_id + )) + })?; + + // obtain the base64 portion (substring) of the blob id + let base64_str = &blob_id[1..dot_index]; + + // decode blob substring from base64 (to bytes) + let blob_bytes = base64::decode_config(base64_str, base64::STANDARD)?; + + // represent the blob bytes as hex, removing all unnecessary characters + let blob_hex = format!("{:02x?}", blob_bytes) + .replace('[', "") + .replace(']', "") + .replace(',', "") + .replace(' ', ""); + + // split the hex representation of the decoded base64 + // this is how paths are formatted for the blobstore + // e.g. 26/524773dc9e1b5129640f5f20f18bcd42831f415e477f408b9edeba1293d46f + // full path would be: `/home/user/.ssb-go/blobs/sha256/26/524773dc...` + let blob_path = format!("{}/{}", &blob_hex[..2], &blob_hex[2..]); + + Ok(blob_path) +} diff --git a/src/error.rs b/src/error.rs index 010c834..c954bac 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,9 +34,11 @@ pub enum GolgiError { Rpc(RpcError), /// Go-sbot error. Sbot(String), + /// SSB sigil-link error. + SigilLink(String), /// JSON serialization or deserialization error. SerdeJson(JsonError), - /// Error decoding typed ssb message from content. + /// Error decoding typed SSB message from content. ContentType(String), /// Error decoding UTF8 string from bytes Utf8Parse { @@ -55,6 +57,7 @@ impl std::error::Error for GolgiError { GolgiError::Feed(ref err) => Some(err), GolgiError::Rpc(ref err) => Some(err), GolgiError::Sbot(_) => None, + GolgiError::SigilLink(_) => None, GolgiError::SerdeJson(ref err) => Some(err), GolgiError::ContentType(_) => None, GolgiError::Utf8Parse { ref source } => Some(source), @@ -75,11 +78,12 @@ impl std::fmt::Display for GolgiError { // then have the core display msg be: "SSB RPC error: {}", context GolgiError::Rpc(ref err) => write!(f, "SSB RPC failure: {}", err), GolgiError::Sbot(ref err) => write!(f, "Sbot returned an error response: {}", err), + GolgiError::SigilLink(ref context) => write!(f, "SSB sigil-link error: {}", context), GolgiError::SerdeJson(_) => write!(f, "Failed to serialize JSON slice"), //GolgiError::WhoAmI(ref err) => write!(f, "{}", err), GolgiError::ContentType(ref err) => write!( f, - "Failed to decode typed message from ssb message content: {}", + "Failed to decode typed message from SSB message content: {}", err ), GolgiError::Utf8Parse { source } => { diff --git a/src/lib.rs b/src/lib.rs index 1869e24..4b36222 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,6 +69,7 @@ //! ``` pub mod api; +pub mod blobs; pub mod error; pub mod messages; pub mod sbot;