use std::fs;
use radicle::cob;
use radicle::git;
use radicle::identity;
use radicle::node::Handle;
use radicle::storage::ReadRepository;
use radicle::storage::ReadStorage;
use radicle::Node;
use crate::cobs;
use crate::error::Error;
use crate::traits::Profile;
pub const MAX_EMBED_SIZE: usize = 10_485_760;
pub trait Thread: Profile {
fn get_embed(
&self,
rid: identity::RepoId,
name: Option<String>,
oid: git::Oid,
) -> Result<cobs::EmbedWithMimeType, Error> {
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
let blob = repo.blob(oid)?;
let content = blob.content();
let mime_type = match infer::get(content).map(|i| i.mime_type().to_string()) {
Some(mime_type) => Some(mime_type),
None if name.is_some() => {
let filename = name.unwrap();
mime_infer::from_path(&filename)
.first()
.map(|m| m.as_ref().to_string())
}
_ => None,
};
Ok::<_, Error>(cobs::EmbedWithMimeType {
content: content.to_vec(),
mime_type,
})
}
fn save_embed_to_disk(
&self,
rid: identity::RepoId,
oid: git::Oid,
path: std::path::PathBuf,
) -> Result<(), Error> {
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
let blob = repo.blob(oid)?;
fs::write(path, blob.content())?;
Ok::<_, Error>(())
}
fn save_embed_by_path(
&self,
rid: identity::RepoId,
path: std::path::PathBuf,
) -> Result<git::Oid, Error> {
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
let bytes = fs::read(path.clone())?;
let file_size = bytes.len();
if file_size > MAX_EMBED_SIZE {
return Err(Error::FileTooLarge(file_size));
}
let name = path.file_name().and_then(|s| s.to_str()).unwrap_or("embed");
let embed = radicle::cob::Embed::<git::Oid>::store(name, &bytes, &repo.backend)?;
Ok(embed.oid())
}
fn save_embed_by_bytes(
&self,
rid: identity::RepoId,
name: String,
bytes: Vec<u8>,
) -> Result<git::Oid, Error> {
let file_size = bytes.len();
if file_size > MAX_EMBED_SIZE {
return Err(Error::FileTooLarge(file_size));
}
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
let embed = radicle::cob::Embed::<git::Oid>::store(&name, &bytes, &repo.backend)?;
Ok(embed.oid())
}
fn create_issue_comment(
&self,
rid: identity::RepoId,
new: cobs::thread::NewIssueComment,
opts: cobs::CobOptions,
) -> Result<cobs::thread::Comment<cobs::Never>, Error> {
let profile = self.profile();
let aliases = &profile.aliases();
let mut node = Node::new(profile.socket());
let signer = profile.signer()?;
let repo = profile.storage.repository(rid)?;
let mut issues = profile.issues_mut(&repo)?;
let mut issue = issues.get_mut(&new.id.into())?;
let id = new.reply_to.unwrap_or_else(|| {
let (root_id, _) = issue.root();
*root_id
});
let n = new.clone();
let oid = issue.comment(
n.body,
id,
n.embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
&signer,
)?;
if opts.announce() {
if let Err(e) = node.announce_refs_for(rid, [profile.public_key]) {
log::error!("Not able to announce changes: {}", e)
}
}
Ok(cobs::thread::Comment::<cobs::Never>::new(
oid,
cob::thread::Comment::new(
*signer.public_key(),
new.body,
id.into(),
None,
new.embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
cob::Timestamp::from_secs(radicle_localtime::LocalTime::now().as_secs()),
),
aliases,
))
}
fn create_patch_comment(
&self,
rid: identity::RepoId,
new: cobs::thread::NewPatchComment,
opts: cobs::CobOptions,
) -> Result<cobs::thread::Comment<cobs::thread::CodeLocation>, Error> {
let profile = self.profile();
let aliases = &profile.aliases();
let mut node = Node::new(profile.socket());
let signer = profile.signer()?;
let repo = profile.storage.repository(rid)?;
let mut patches = profile.patches_mut(&repo)?;
let mut patch = patches.get_mut(&new.id.into())?;
let n = new.clone();
let oid = patch.comment(
new.revision.into(),
n.body,
n.reply_to,
n.location.map(|l| l.into()),
n.embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
&signer,
)?;
if opts.announce() {
if let Err(e) = node.announce_refs_for(rid, [profile.public_key]) {
log::error!("Not able to announce changes: {}", e)
}
}
Ok(cobs::thread::Comment::<cobs::thread::CodeLocation>::new(
oid,
cob::thread::Comment::new(
*signer.public_key(),
new.body,
new.reply_to,
new.location.map(|l| l.into()),
new.embeds.into_iter().map(Into::into).collect::<Vec<_>>(),
cob::Timestamp::from_secs(radicle_localtime::LocalTime::now().as_secs()),
),
aliases,
))
}
}