use std::{path::PathBuf, str::FromStr};
use bytes::Bytes;
use futures::{StreamExt, TryStreamExt};
use napi::bindgen_prelude::*;
use napi::threadsafe_function::ThreadsafeFunction;
use napi_derive::napi;
use quic_rpc::transport::flume::FlumeConnector;
use tracing::warn;
use crate::{AddrInfoOptions, AuthorId, DocTicket, DocsClient, Hash, Iroh, NodeAddr};
#[derive(Debug, Clone)]
#[napi(string_enum)]
pub enum CapabilityKind {
Write,
Read,
}
impl From<iroh_docs::CapabilityKind> for CapabilityKind {
fn from(value: iroh_docs::CapabilityKind) -> Self {
match value {
iroh_docs::CapabilityKind::Write => Self::Write,
iroh_docs::CapabilityKind::Read => Self::Read,
}
}
}
#[napi]
pub struct Docs {
docs: DocsClient,
}
type MemConnector = FlumeConnector<iroh_docs::rpc::proto::Response, iroh_docs::rpc::proto::Request>;
#[napi]
impl Iroh {
#[napi(getter)]
pub fn docs(&self) -> Docs {
Docs {
docs: self.docs_client.clone().expect("missing docs"),
}
}
}
#[napi]
impl Docs {
#[napi]
pub async fn create(&self) -> Result<Doc> {
let doc = self.docs.create().await?;
Ok(Doc { inner: doc })
}
#[napi]
pub async fn join(&self, ticket: &DocTicket) -> Result<Doc> {
let ticket = ticket.try_into()?;
let doc = self.docs.import(ticket).await?;
Ok(Doc { inner: doc })
}
#[napi]
pub async fn join_and_subscribe(
&self,
ticket: &DocTicket,
cb: ThreadsafeFunction<LiveEvent, ()>,
) -> Result<Doc> {
let ticket = ticket.try_into()?;
let (doc, mut stream) = self.docs.import_and_subscribe(ticket).await?;
tokio::spawn(async move {
while let Some(event) = stream.next().await {
let message: Result<LiveEvent> = event.map(Into::into).map_err(Into::into);
if let Err(err) = cb.call_async(message).await {
warn!("cb error: {:?}", err);
}
}
});
Ok(Doc { inner: doc })
}
#[napi]
pub async fn list(&self) -> Result<Vec<NamespaceAndCapability>> {
let docs = self
.docs
.list()
.await?
.map_ok(|(namespace, capability)| NamespaceAndCapability {
namespace: namespace.to_string(),
capability: capability.into(),
})
.try_collect::<Vec<_>>()
.await?;
Ok(docs)
}
#[napi]
pub async fn open(&self, id: String) -> Result<Option<Doc>> {
let namespace_id = iroh_docs::NamespaceId::from_str(&id)?;
let doc = self.docs.open(namespace_id).await?;
Ok(doc.map(|d| Doc { inner: d }))
}
#[napi]
pub async fn drop_doc(&self, doc_id: String) -> Result<()> {
let doc_id = iroh_docs::NamespaceId::from_str(&doc_id)?;
self.docs.drop_doc(doc_id).await?;
Ok(())
}
}
#[derive(Debug)]
#[napi(object)]
pub struct NamespaceAndCapability {
pub namespace: String,
pub capability: CapabilityKind,
}
#[derive(Clone)]
#[napi]
pub struct Doc {
pub(crate) inner: iroh_docs::rpc::client::docs::Doc<MemConnector>,
}
#[napi]
impl Doc {
#[napi]
pub fn id(&self) -> String {
self.inner.id().to_string()
}
#[napi]
pub async fn close_me(&self) -> Result<()> {
self.inner.close().await?;
Ok(())
}
#[napi]
pub async fn set_bytes(
&self,
author_id: &AuthorId,
key: Vec<u8>,
value: Vec<u8>,
) -> Result<Hash> {
let hash = self.inner.set_bytes(author_id.0, key, value).await?;
Ok(hash.into())
}
#[napi]
pub async fn set_hash(
&self,
author_id: &AuthorId,
key: Vec<u8>,
hash: String,
size: BigInt,
) -> Result<()> {
self.inner
.set_hash(
author_id.0,
key,
hash.parse().map_err(anyhow::Error::from)?,
size.get_u64().1,
)
.await?;
Ok(())
}
#[napi]
pub async fn import_file(
&self,
author: &AuthorId,
key: Vec<u8>,
path: String,
in_place: bool,
cb: Option<ThreadsafeFunction<DocImportProgress, ()>>,
) -> Result<()> {
let mut stream = self
.inner
.import_file(author.0, Bytes::from(key), PathBuf::from(path), in_place)
.await?;
while let Some(event) = stream.next().await {
if let Some(ref cb) = cb {
let message = DocImportProgress::convert(event);
cb.call_async(message).await?;
}
}
Ok(())
}
#[napi]
pub async fn export_file(
&self,
entry: Entry,
path: String,
cb: Option<ThreadsafeFunction<DocExportProgress, ()>>,
) -> Result<()> {
let mut stream = self
.inner
.export_file(
entry.try_into()?,
std::path::PathBuf::from(path),
iroh_blobs::store::ExportMode::Copy,
)
.await?;
while let Some(event) = stream.next().await {
if let Some(ref cb) = cb {
let message = DocExportProgress::convert(event);
cb.call_async(message).await?;
}
}
Ok(())
}
#[napi]
pub async fn delete(&self, author_id: &AuthorId, prefix: Vec<u8>) -> Result<u64> {
let num_del = self.inner.del(author_id.0, prefix).await?;
u64::try_from(num_del).map_err(|e| anyhow::Error::from(e).into())
}
#[napi]
pub async fn get_exact(
&self,
author: &AuthorId,
key: Vec<u8>,
include_empty: bool,
) -> Result<Option<Entry>> {
let res = self
.inner
.get_exact(author.0, key, include_empty)
.await
.map(|e| e.map(|e| e.into()))?;
Ok(res)
}
#[napi]
pub async fn get_many(&self, query: &Query) -> Result<Vec<Entry>> {
let entries = self
.inner
.get_many(query.0.clone())
.await?
.map_ok(|e| e.into())
.try_collect::<Vec<_>>()
.await?;
Ok(entries)
}
#[napi]
pub async fn get_one(&self, query: &Query) -> Result<Option<Entry>> {
let res = self
.inner
.get_one(query.0.clone())
.await
.map(|e| e.map(|e| e.into()))?;
Ok(res)
}
#[napi]
pub async fn share(&self, mode: ShareMode, addr_options: AddrInfoOptions) -> Result<DocTicket> {
let res = self
.inner
.share(mode.into(), addr_options.into())
.await
.map(|ticket| ticket.into())?;
Ok(res)
}
#[napi]
pub async fn start_sync(&self, peers: Vec<NodeAddr>) -> Result<()> {
let peers = peers
.into_iter()
.map(|p| p.try_into())
.collect::<anyhow::Result<Vec<_>>>()?;
self.inner.start_sync(peers).await?;
Ok(())
}
#[napi]
pub async fn leave(&self) -> Result<()> {
self.inner.leave().await?;
Ok(())
}
#[napi]
pub async fn subscribe(&self, cb: ThreadsafeFunction<LiveEvent, ()>) -> Result<()> {
let client = self.inner.clone();
tokio::task::spawn(async move {
let mut sub = client.subscribe().await.unwrap();
while let Some(event) = sub.next().await {
let message: Result<LiveEvent> = event.map(Into::into).map_err(Into::into);
if let Err(err) = cb.call_async(message).await {
warn!("cb error: {:?}", err);
}
}
});
Ok(())
}
#[napi]
pub async fn status(&self) -> Result<OpenState> {
let res = self.inner.status().await.map(|o| o.into())?;
Ok(res)
}
#[napi]
pub async fn set_download_policy(&self, policy: &DownloadPolicy) -> Result<()> {
self.inner.set_download_policy(policy.into()).await?;
Ok(())
}
#[napi]
pub async fn get_download_policy(&self) -> Result<DownloadPolicy> {
let res = self
.inner
.get_download_policy()
.await
.map(|policy| policy.into())?;
Ok(res)
}
#[napi]
pub async fn get_sync_peers(&self) -> Result<Option<Vec<Vec<u8>>>> {
let list = self.inner.get_sync_peers().await?;
let list = list.map(|l| l.into_iter().map(|p| p.to_vec()).collect());
Ok(list)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[napi]
pub struct DownloadPolicy {
nothing_except: Option<Vec<FilterKind>>,
everything_except: Option<Vec<FilterKind>>,
}
#[napi]
impl DownloadPolicy {
#[napi(factory)]
pub fn everything() -> Self {
DownloadPolicy {
everything_except: Some(Vec::new()),
nothing_except: None,
}
}
#[napi(factory)]
pub fn nothing() -> Self {
DownloadPolicy {
everything_except: None,
nothing_except: Some(Vec::new()),
}
}
#[napi(factory)]
pub fn nothing_except(filters: Vec<&FilterKind>) -> Self {
DownloadPolicy {
everything_except: None,
nothing_except: Some(filters.into_iter().cloned().collect()),
}
}
#[napi(factory)]
pub fn everything_except(filters: Vec<&FilterKind>) -> Self {
DownloadPolicy {
everything_except: Some(filters.into_iter().cloned().collect()),
nothing_except: None,
}
}
}
impl From<iroh_docs::store::DownloadPolicy> for DownloadPolicy {
fn from(value: iroh_docs::store::DownloadPolicy) -> Self {
match value {
iroh_docs::store::DownloadPolicy::NothingExcept(filters) => DownloadPolicy {
nothing_except: Some(filters.into_iter().map(|f| f.into()).collect()),
everything_except: None,
},
iroh_docs::store::DownloadPolicy::EverythingExcept(filters) => DownloadPolicy {
everything_except: Some(filters.into_iter().map(|f| f.into()).collect()),
nothing_except: None,
},
}
}
}
impl From<&DownloadPolicy> for iroh_docs::store::DownloadPolicy {
fn from(value: &DownloadPolicy) -> Self {
if let Some(ref filters) = value.nothing_except {
return iroh_docs::store::DownloadPolicy::NothingExcept(
filters.iter().map(|f| f.0.clone()).collect(),
);
}
if let Some(ref filters) = value.everything_except {
return iroh_docs::store::DownloadPolicy::EverythingExcept(
filters.iter().map(|f| f.0.clone()).collect(),
);
}
panic!("invalid internal state");
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[napi]
pub struct FilterKind(pub(crate) iroh_docs::store::FilterKind);
#[napi]
impl FilterKind {
#[napi]
pub fn matches(&self, key: Vec<u8>) -> bool {
self.0.matches(key)
}
#[napi(factory)]
pub fn prefix(prefix: Vec<u8>) -> FilterKind {
FilterKind(iroh_docs::store::FilterKind::Prefix(Bytes::from(prefix)))
}
#[napi(factory)]
pub fn exact(key: Vec<u8>) -> FilterKind {
FilterKind(iroh_docs::store::FilterKind::Exact(Bytes::from(key)))
}
}
impl From<iroh_docs::store::FilterKind> for FilterKind {
fn from(value: iroh_docs::store::FilterKind) -> Self {
FilterKind(value)
}
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct OpenState {
pub sync: bool,
pub subscribers: BigInt,
pub handles: BigInt,
}
impl From<iroh_docs::actor::OpenState> for OpenState {
fn from(value: iroh_docs::actor::OpenState) -> Self {
OpenState {
sync: value.sync,
subscribers: (value.subscribers as u64).into(),
handles: (value.handles as u64).into(),
}
}
}
#[derive(Debug)]
#[napi(string_enum)]
pub enum ShareMode {
Read,
Write,
}
impl From<ShareMode> for iroh_docs::rpc::client::docs::ShareMode {
fn from(mode: ShareMode) -> Self {
match mode {
ShareMode::Read => iroh_docs::rpc::client::docs::ShareMode::Read,
ShareMode::Write => iroh_docs::rpc::client::docs::ShareMode::Write,
}
}
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct Entry {
pub namespace: String,
pub author: String,
pub key: Vec<u8>,
pub len: BigInt,
pub hash: String,
pub timestamp: BigInt,
}
impl From<iroh_docs::rpc::client::docs::Entry> for Entry {
fn from(e: iroh_docs::rpc::client::docs::Entry) -> Self {
Self {
namespace: e.id().namespace().to_string(),
author: e.author().to_string(),
key: e.key().to_vec(),
len: e.content_len().into(),
hash: e.content_hash().to_string(),
timestamp: e.timestamp().into(),
}
}
}
impl TryFrom<Entry> for iroh_docs::rpc::client::docs::Entry {
type Error = anyhow::Error;
fn try_from(value: Entry) -> std::prelude::v1::Result<Self, Self::Error> {
let author: iroh_docs::AuthorId = value.author.parse()?;
let namespace: iroh_docs::NamespaceId = value.namespace.parse()?;
let id = iroh_docs::RecordIdentifier::new(namespace, author, value.key);
let hash: iroh_blobs::Hash = value.hash.parse()?;
let len = value.len.get_u64().1;
let timestamp = value.timestamp.get_u64().1;
let record = iroh_docs::Record::new(hash, len, timestamp);
let entry = iroh_docs::Entry::new(id, record);
Ok(entry)
}
}
#[derive(Debug, Default, Clone)]
#[napi(string_enum)]
pub enum SortBy {
KeyAuthor,
#[default]
AuthorKey,
}
impl From<iroh_docs::store::SortBy> for SortBy {
fn from(value: iroh_docs::store::SortBy) -> Self {
match value {
iroh_docs::store::SortBy::AuthorKey => SortBy::AuthorKey,
iroh_docs::store::SortBy::KeyAuthor => SortBy::KeyAuthor,
}
}
}
impl From<SortBy> for iroh_docs::store::SortBy {
fn from(value: SortBy) -> Self {
match value {
SortBy::AuthorKey => iroh_docs::store::SortBy::AuthorKey,
SortBy::KeyAuthor => iroh_docs::store::SortBy::KeyAuthor,
}
}
}
#[derive(Debug, Default, Clone)]
#[napi(string_enum)]
pub enum SortDirection {
#[default]
Asc,
Desc,
}
impl From<iroh_docs::store::SortDirection> for SortDirection {
fn from(value: iroh_docs::store::SortDirection) -> Self {
match value {
iroh_docs::store::SortDirection::Asc => SortDirection::Asc,
iroh_docs::store::SortDirection::Desc => SortDirection::Desc,
}
}
}
impl From<SortDirection> for iroh_docs::store::SortDirection {
fn from(value: SortDirection) -> Self {
match value {
SortDirection::Asc => iroh_docs::store::SortDirection::Asc,
SortDirection::Desc => iroh_docs::store::SortDirection::Desc,
}
}
}
#[derive(Clone, Debug)]
#[napi]
pub struct Query(pub(crate) iroh_docs::store::Query);
#[derive(Clone, Debug)]
#[napi(object)]
pub struct QueryOptions {
pub sort_by: Option<SortBy>,
pub direction: Option<SortDirection>,
pub offset: Option<BigInt>,
pub limit: Option<BigInt>,
}
impl QueryOptions {
fn offset(&self) -> Option<u64> {
self.offset
.as_ref()
.map(|o| o.get_u64().1)
.and_then(|o| if o == 0 { None } else { Some(o) })
}
fn limit(&self) -> Option<u64> {
self.limit
.as_ref()
.map(|o| o.get_u64().1)
.and_then(|o| if o == 0 { None } else { Some(o) })
}
}
#[napi]
impl Query {
#[napi(factory)]
pub fn all(opts: Option<QueryOptions>) -> Self {
let builder = iroh_docs::store::Query::all();
let builder = apply_opts_with_sort(builder, opts.as_ref());
Query(builder.build())
}
#[napi(factory)]
pub fn single_latest_per_key(opts: Option<QueryOptions>) -> Self {
let builder = iroh_docs::store::Query::single_latest_per_key();
let builder = apply_opts(builder, opts.as_ref());
Query(builder.build())
}
#[napi(factory)]
pub fn single_latest_per_key_exact(key: Vec<u8>) -> Self {
let builder = iroh_docs::store::Query::single_latest_per_key()
.key_exact(key)
.build();
Query(builder)
}
#[napi(factory)]
pub fn single_latest_per_key_prefix(prefix: Vec<u8>, opts: Option<QueryOptions>) -> Self {
let builder = iroh_docs::store::Query::single_latest_per_key().key_prefix(prefix);
let builder = apply_opts(builder, opts.as_ref());
Query(builder.build())
}
#[napi(factory)]
pub fn author(author: &AuthorId, opts: Option<QueryOptions>) -> Self {
let builder = iroh_docs::store::Query::author(author.0);
let builder = apply_opts_with_sort(builder, opts.as_ref());
Query(builder.build())
}
#[napi(factory)]
pub fn key_exact(key: Vec<u8>, opts: Option<QueryOptions>) -> Self {
let builder = iroh_docs::store::Query::key_exact(key);
let builder = apply_opts_with_sort(builder, opts.as_ref());
Query(builder.build())
}
#[napi(factory)]
pub fn author_key_exact(author: &AuthorId, key: Vec<u8>) -> Self {
let builder = iroh_docs::store::Query::author(author.0).key_exact(key);
Query(builder.build())
}
#[napi(factory)]
pub fn key_prefix(prefix: Vec<u8>, opts: Option<QueryOptions>) -> Self {
let builder = iroh_docs::store::Query::key_prefix(prefix);
let builder = apply_opts_with_sort(builder, opts.as_ref());
Query(builder.build())
}
#[napi(factory)]
pub fn author_key_prefix(
author: &AuthorId,
prefix: Vec<u8>,
opts: Option<QueryOptions>,
) -> Self {
let builder = iroh_docs::store::Query::author(author.0).key_prefix(prefix);
let builder = apply_opts_with_sort(builder, opts.as_ref());
Query(builder.build())
}
#[napi]
pub fn limit(&self) -> Option<BigInt> {
self.0.limit().map(Into::into)
}
#[napi]
pub fn offset(&self) -> BigInt {
self.0.offset().into()
}
}
fn apply_opts_with_sort(
mut builder: iroh_docs::store::QueryBuilder<iroh_docs::store::FlatQuery>,
opts: Option<&QueryOptions>,
) -> iroh_docs::store::QueryBuilder<iroh_docs::store::FlatQuery> {
builder = apply_opts(builder, opts);
if let Some(opts) = opts {
if let Some(ref sort_by) = opts.sort_by {
let direction = opts.direction.clone().unwrap_or_default();
builder = builder.sort_by(sort_by.clone().into(), direction.into());
}
}
builder
}
fn apply_opts<K>(
mut builder: iroh_docs::store::QueryBuilder<K>,
opts: Option<&QueryOptions>,
) -> iroh_docs::store::QueryBuilder<K> {
if let Some(opts) = opts {
if let Some(offset) = opts.offset() {
builder = builder.offset(offset);
}
if let Some(limit) = opts.limit() {
builder = builder.limit(limit);
}
}
builder
}
#[derive(Debug, Default)]
#[napi(object)]
pub struct LiveEvent {
pub insert_local: Option<LiveEventInsertLocal>,
pub insert_remote: Option<LiveEventInsertRemote>,
pub content_ready: Option<LiveEventContentReady>,
pub neighbor_up: Option<LiveEventNeighborUp>,
pub neighbor_down: Option<LiveEventNeighborDown>,
pub sync_finished: Option<SyncEvent>,
pub pending_content_ready: bool,
}
#[derive(Debug)]
#[napi(object)]
pub struct LiveEventInsertLocal {
pub entry: Entry,
}
#[derive(Debug)]
#[napi(object)]
pub struct LiveEventInsertRemote {
pub from: String,
pub entry: Entry,
pub content_status: ContentStatus,
}
#[derive(Debug)]
#[napi(object)]
pub struct LiveEventContentReady {
pub hash: String,
}
#[derive(Debug)]
#[napi(object)]
pub struct LiveEventNeighborUp {
pub neighbor: String,
}
#[derive(Debug)]
#[napi(object)]
pub struct LiveEventNeighborDown {
pub neighbor: String,
}
impl From<iroh_docs::rpc::client::docs::LiveEvent> for LiveEvent {
fn from(value: iroh_docs::rpc::client::docs::LiveEvent) -> Self {
match value {
iroh_docs::rpc::client::docs::LiveEvent::InsertLocal { entry } => LiveEvent {
insert_local: Some(LiveEventInsertLocal {
entry: entry.into(),
}),
..Default::default()
},
iroh_docs::rpc::client::docs::LiveEvent::InsertRemote {
from,
entry,
content_status,
} => LiveEvent {
insert_remote: Some(LiveEventInsertRemote {
from: from.to_string(),
entry: entry.into(),
content_status: content_status.into(),
}),
..Default::default()
},
iroh_docs::rpc::client::docs::LiveEvent::ContentReady { hash } => LiveEvent {
content_ready: Some(LiveEventContentReady {
hash: hash.to_string(),
}),
..Default::default()
},
iroh_docs::rpc::client::docs::LiveEvent::NeighborUp(key) => LiveEvent {
neighbor_up: Some(LiveEventNeighborUp {
neighbor: key.to_string(),
}),
..Default::default()
},
iroh_docs::rpc::client::docs::LiveEvent::NeighborDown(key) => LiveEvent {
neighbor_down: Some(LiveEventNeighborDown {
neighbor: key.to_string(),
}),
..Default::default()
},
iroh_docs::rpc::client::docs::LiveEvent::SyncFinished(e) => LiveEvent {
sync_finished: Some(e.into()),
..Default::default()
},
iroh_docs::rpc::client::docs::LiveEvent::PendingContentReady => LiveEvent {
pending_content_ready: true,
..Default::default()
},
}
}
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct SyncEvent {
pub peer: String,
pub origin: Origin,
pub finished: chrono::DateTime<chrono::Utc>,
pub started: chrono::DateTime<chrono::Utc>,
pub result: Option<String>,
}
impl From<iroh_docs::rpc::client::docs::SyncEvent> for SyncEvent {
fn from(value: iroh_docs::rpc::client::docs::SyncEvent) -> Self {
SyncEvent {
peer: value.peer.to_string(),
origin: value.origin.into(),
finished: value.finished.into(),
started: value.started.into(),
result: match value.result {
Ok(_) => None,
Err(err) => Some(err),
},
}
}
}
#[derive(Debug, Eq, PartialEq)]
#[napi(string_enum)]
pub enum SyncReason {
DirectJoin,
NewNeighbor,
SyncReport,
Resync,
}
impl From<iroh_docs::rpc::client::docs::SyncReason> for SyncReason {
fn from(value: iroh_docs::rpc::client::docs::SyncReason) -> Self {
match value {
iroh_docs::rpc::client::docs::SyncReason::DirectJoin => Self::DirectJoin,
iroh_docs::rpc::client::docs::SyncReason::NewNeighbor => Self::NewNeighbor,
iroh_docs::rpc::client::docs::SyncReason::SyncReport => Self::SyncReport,
iroh_docs::rpc::client::docs::SyncReason::Resync => Self::Resync,
}
}
}
#[derive(Debug, Clone)]
#[napi(string_enum)]
pub enum Origin {
ConnectDirectJoin,
ConnectNewNeighbor,
ConnectSyncReport,
ConnectResync,
Accept,
}
impl From<iroh_docs::rpc::client::docs::Origin> for Origin {
fn from(value: iroh_docs::rpc::client::docs::Origin) -> Self {
match value {
iroh_docs::rpc::client::docs::Origin::Connect(reason) => match reason {
iroh_docs::rpc::client::docs::SyncReason::DirectJoin => Self::ConnectDirectJoin,
iroh_docs::rpc::client::docs::SyncReason::NewNeighbor => Self::ConnectNewNeighbor,
iroh_docs::rpc::client::docs::SyncReason::SyncReport => Self::ConnectSyncReport,
iroh_docs::rpc::client::docs::SyncReason::Resync => Self::ConnectResync,
},
iroh_docs::rpc::client::docs::Origin::Accept => Self::Accept,
}
}
}
#[derive(Debug)]
#[napi(object)]
pub struct InsertRemoteEvent {
pub from: String,
pub entry: Entry,
pub content_status: ContentStatus,
}
#[derive(Debug)]
#[napi(string_enum)]
pub enum ContentStatus {
Complete,
Incomplete,
Missing,
}
impl From<iroh_docs::ContentStatus> for ContentStatus {
fn from(value: iroh_docs::ContentStatus) -> Self {
match value {
iroh_docs::ContentStatus::Complete => Self::Complete,
iroh_docs::ContentStatus::Incomplete => Self::Incomplete,
iroh_docs::ContentStatus::Missing => Self::Missing,
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[napi(string_enum)]
pub enum DocImportProgressType {
Found,
Progress,
IngestDone,
AllDone,
Abort,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocImportProgressFound {
pub id: BigInt,
pub name: String,
pub size: BigInt,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocImportProgressProgress {
pub id: BigInt,
pub offset: BigInt,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocImportProgressIngestDone {
pub id: BigInt,
pub hash: String,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocImportProgressAllDone {
pub key: Vec<u8>,
}
#[derive(Debug, Default)]
#[napi(object)]
pub struct DocImportProgress {
pub found: Option<DocImportProgressFound>,
pub progress: Option<DocImportProgressProgress>,
pub ingest_done: Option<DocImportProgressIngestDone>,
pub all_done: Option<DocImportProgressAllDone>,
}
impl DocImportProgress {
fn convert(
value: anyhow::Result<iroh_docs::rpc::client::docs::ImportProgress>,
) -> Result<Self> {
match value {
Ok(iroh_docs::rpc::client::docs::ImportProgress::Found { id, name, size }) => {
Ok(DocImportProgress {
found: Some(DocImportProgressFound {
id: id.into(),
name,
size: size.into(),
}),
..Default::default()
})
}
Ok(iroh_docs::rpc::client::docs::ImportProgress::Progress { id, offset }) => {
Ok(DocImportProgress {
progress: Some(DocImportProgressProgress {
id: id.into(),
offset: offset.into(),
}),
..Default::default()
})
}
Ok(iroh_docs::rpc::client::docs::ImportProgress::IngestDone { id, hash }) => {
Ok(DocImportProgress {
ingest_done: Some(DocImportProgressIngestDone {
id: id.into(),
hash: hash.to_string(),
}),
..Default::default()
})
}
Ok(iroh_docs::rpc::client::docs::ImportProgress::AllDone { key }) => {
Ok(DocImportProgress {
all_done: Some(DocImportProgressAllDone { key: key.into() }),
..Default::default()
})
}
Ok(iroh_docs::rpc::client::docs::ImportProgress::Abort(err)) => {
Err(anyhow::Error::from(err).into())
}
Err(err) => Err(err.into()),
}
}
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocExportProgressFound {
pub id: BigInt,
pub hash: String,
pub size: BigInt,
pub outpath: String,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocExportProgressProgress {
pub id: BigInt,
pub offset: BigInt,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocExportProgressDone {
pub id: BigInt,
}
#[derive(Debug, Clone)]
#[napi(object)]
pub struct DocExportProgressAbort {
pub error: String,
}
#[derive(Debug, Default)]
#[napi(object)]
pub struct DocExportProgress {
pub found: Option<DocExportProgressFound>,
pub progress: Option<DocExportProgressProgress>,
pub done: Option<DocExportProgressDone>,
pub all_done: bool,
}
impl DocExportProgress {
fn convert(value: anyhow::Result<iroh_blobs::export::ExportProgress>) -> Result<Self> {
match value {
Ok(value) => match value {
iroh_blobs::export::ExportProgress::Found {
id,
hash,
size,
outpath,
..
} => Ok(DocExportProgress {
found: Some(DocExportProgressFound {
id: id.into(),
hash: hash.to_string(),
size: size.value().into(),
outpath: outpath.to_string_lossy().to_string(),
}),
..Default::default()
}),
iroh_blobs::export::ExportProgress::Progress { id, offset } => {
Ok(DocExportProgress {
progress: Some(DocExportProgressProgress {
id: id.into(),
offset: offset.into(),
}),
..Default::default()
})
}
iroh_blobs::export::ExportProgress::Done { id } => Ok(DocExportProgress {
done: Some(DocExportProgressDone { id: id.into() }),
..Default::default()
}),
iroh_blobs::export::ExportProgress::AllDone => Ok(DocExportProgress {
all_done: true,
..Default::default()
}),
iroh_blobs::export::ExportProgress::Abort(err) => {
Err(anyhow::Error::from(err).into())
}
},
Err(err) => Err(err.into()),
}
}
}