use crate::error::OkuDiscoveryError;
use futures::StreamExt;
use iroh::{
bytes::{Hash, HashAndFormat},
sync::NamespaceId,
ticket::{BlobTicket, DocTicket},
};
use iroh_mainline_content_discovery::announce_dht;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::{collections::BTreeSet, error::Error, str::FromStr, time::Duration};
pub const REPUBLISH_DELAY: Duration = Duration::from_secs(60 * 60);
pub const INITIAL_PUBLISH_DELAY: Duration = Duration::from_millis(500);
pub const DISCOVERY_PORT: u16 = 4938;
pub const ANNOUNCE_PARALLELISM: usize = 10;
pub async fn announce_replica(
namespace_id: NamespaceId,
) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut content = BTreeSet::new();
content.insert(HashAndFormat::raw(Hash::new(namespace_id)));
let dht = mainline::Dht::default();
let announce_stream = announce_dht(dht, content, DISCOVERY_PORT, ANNOUNCE_PARALLELISM);
tokio::pin!(announce_stream);
while let Some((content, res)) = announce_stream.next().await {
match res {
Ok(_) => {}
Err(e) => eprintln!(
"{}",
OkuDiscoveryError::ProblemAnnouncingContent(content.to_string(), e.to_string())
),
}
}
Ok(())
}
#[derive(Debug, Clone, derive_more::From)]
pub enum ContentRequest {
Hash(Hash),
HashAndFormat(HashAndFormat),
Ticket(BlobTicket),
}
impl ContentRequest {
pub fn hash_and_format(&self) -> HashAndFormat {
match self {
ContentRequest::Hash(hash) => HashAndFormat::raw(*hash),
ContentRequest::HashAndFormat(haf) => *haf,
ContentRequest::Ticket(ticket) => HashAndFormat {
hash: ticket.hash(),
format: ticket.format(),
},
}
}
pub fn hash(&self) -> Hash {
match self {
ContentRequest::Hash(hash) => *hash,
ContentRequest::HashAndFormat(haf) => haf.hash,
ContentRequest::Ticket(ticket) => ticket.hash(),
}
}
}
impl FromStr for ContentRequest {
type Err = Box<dyn Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Box<dyn Error + Send + Sync>> {
if let Ok(hash) = Hash::from_str(s) {
Ok(hash.into())
} else if let Ok(haf) = HashAndFormat::from_str(s) {
Ok(haf.into())
} else if let Ok(ticket) = BlobTicket::from_str(s) {
Ok(ticket.into())
} else {
Err(OkuDiscoveryError::InvalidHashAndFormat.into())
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PeerTicketResponse {
Document(DocTicket),
Entries(Vec<BlobTicket>),
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct PeerContentRequest {
pub namespace_id: NamespaceId,
pub path: Option<PathBuf>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeerContentResponse {
pub ticket_response: PeerTicketResponse,
pub content_size: u64,
}