diff --git a/Cargo.lock b/Cargo.lock index a954236a..64b63ccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2354,6 +2354,7 @@ name = "radicle" version = "0.2.0" dependencies = [ "amplify", + "base64 0.21.3", "chrono", "colored", "crossbeam-channel", diff --git a/radicle-httpd/src/api.rs b/radicle-httpd/src/api.rs index ef8586f5..8c45aa30 100644 --- a/radicle-httpd/src/api.rs +++ b/radicle-httpd/src/api.rs @@ -9,19 +9,18 @@ use axum::http::Method; use axum::response::{IntoResponse, Json}; use axum::routing::get; use axum::Router; -use base64::prelude::{Engine, BASE64_STANDARD}; use serde::{Deserialize, Serialize}; use serde_json::json; use tokio::sync::RwLock; use tower_http::cors::{self, CorsLayer}; -use radicle::cob::{issue, Uri}; -use radicle::cob::{patch, Embed}; +use radicle::cob::issue; +use radicle::cob::patch; use radicle::identity::{DocAt, Id}; use radicle::node::policy::Scope; use radicle::node::routing::Store; use radicle::node::{Handle, NodeId}; -use radicle::storage::{Oid, ReadRepository, ReadStorage}; +use radicle::storage::{ReadRepository, ReadStorage}; use radicle::{Node, Profile}; mod error; @@ -217,32 +216,6 @@ mod project { } } -/// A `data:` URI. -#[derive(Debug, Clone)] -pub struct DataUri(Vec); - -impl From for Vec { - fn from(value: DataUri) -> Self { - value.0 - } -} - -impl TryFrom<&Uri> for DataUri { - type Error = Uri; - - fn try_from(value: &Uri) -> Result { - if let Some(data_uri) = value.as_str().strip_prefix("data:") { - let (_, uri_data) = data_uri.split_once(',').ok_or(value.clone())?; - let uri_data = BASE64_STANDARD - .decode(uri_data) - .map_err(|_| value.clone())?; - - return Ok(DataUri(uri_data)); - } - Err(value.clone()) - } -} - /// Announce refs to the network for the given RID. pub fn announce_refs(mut node: Node, rid: Id) -> Result<(), Error> { match node.announce_refs(rid) { @@ -251,21 +224,3 @@ pub fn announce_refs(mut node: Node, rid: Id) -> Result<(), Error> { Err(e) => Err(e.into()), } } - -/// Resolve an embed with a URI to one with actual data. -pub fn resolve_embed(repo: &impl ReadRepository, embed: Embed) -> Option>> { - DataUri::try_from(&embed.content) - .ok() - .map(|content| Embed { - name: embed.name.clone(), - content: content.into(), - }) - .or_else(|| { - Oid::try_from(&embed.content).ok().and_then(|oid| { - repo.blob(oid).ok().map(|blob| Embed { - name: embed.name, - content: blob.content().to_vec(), - }) - }) - }) -} diff --git a/radicle/Cargo.toml b/radicle/Cargo.toml index 02558be5..16e01724 100644 --- a/radicle/Cargo.toml +++ b/radicle/Cargo.toml @@ -15,6 +15,7 @@ logger = ["colored", "chrono"] [dependencies] amplify = { version = "4.0.0", default-features = false, features = ["std"] } +base64 = { version = "0.21.3" } crossbeam-channel = { version = "0.5.6" } cyphernet = { version = "0.4.1", features = ["tor", "dns", "p2p-ed25519"] } fastrand = { version = "2.0.0" } diff --git a/radicle/src/cob/common.rs b/radicle/src/cob/common.rs index 1518cf4d..c9607182 100644 --- a/radicle/src/cob/common.rs +++ b/radicle/src/cob/common.rs @@ -4,11 +4,14 @@ use std::ops::{Deref, Range}; use std::path::PathBuf; use std::str::FromStr; +use base64::prelude::{Engine, BASE64_STANDARD}; use localtime::LocalTime; use serde::{Deserialize, Serialize}; +use crate::cob::Embed; use crate::git::Oid; use crate::prelude::{Did, PublicKey}; +use crate::storage::ReadRepository; /// Timestamp used for COB operations. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -340,6 +343,50 @@ impl std::str::FromStr for Uri { } } +/// A `data:` URI. +#[derive(Debug, Clone)] +pub struct DataUri(Vec); + +impl From for Vec { + fn from(value: DataUri) -> Self { + value.0 + } +} + +impl TryFrom<&Uri> for DataUri { + type Error = Uri; + + fn try_from(value: &Uri) -> Result { + if let Some(data_uri) = value.as_str().strip_prefix("data:") { + let (_, uri_data) = data_uri.split_once(',').ok_or(value.clone())?; + let uri_data = BASE64_STANDARD + .decode(uri_data) + .map_err(|_| value.clone())?; + + return Ok(DataUri(uri_data)); + } + Err(value.clone()) + } +} + +/// Resolve an embed with a URI to one with actual data. +pub fn resolve_embed(repo: &impl ReadRepository, embed: Embed) -> Option>> { + DataUri::try_from(&embed.content) + .ok() + .map(|content| Embed { + name: embed.name.clone(), + content: content.into(), + }) + .or_else(|| { + Oid::try_from(&embed.content).ok().and_then(|oid| { + repo.blob(oid).ok().map(|blob| Embed { + name: embed.name, + content: blob.content().to_vec(), + }) + }) + }) +} + /// The result of an authorization check on an COB action. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Authorization {