Skip to main content

syncweb_core/folder/
share.rs

1//! Shared folder-sharing helper.
2//!
3//! The CLI embedded path, the daemon IPC share handler, and `create` all build
4//! a folder ticket, optionally pin the folder's blobs, optionally persist the
5//! share record, and emit a `syncweb://folder/...` URL. This helper unifies that
6//! flow so the call sites share one implementation.
7
8use iroh_docs::{DocTicket, NamespaceId, api::protocol::AddrInfoOptions};
9
10use crate::error::Result;
11
12use super::SyncwebFolder;
13
14/// Options controlling how a folder is shared.
15#[derive(Clone, Copy, Debug)]
16#[non_exhaustive]
17pub struct ShareOptions {
18    /// Grant write access on the ticket (default: read-only).
19    pub writable: bool,
20    /// Pin the folder's blobs so shared content is retained.
21    pub pin: bool,
22    /// Persist the share record so it can be listed and unshared later.
23    pub persist: bool,
24    /// Which address information to embed in the ticket. Defaults to
25    /// [`AddrInfoOptions::RelayAndAddresses`]; use [`AddrInfoOptions::Id`] to
26    /// mint a node-id-only ticket that forces DHT-based address discovery.
27    pub addr_info: AddrInfoOptions,
28}
29
30impl ShareOptions {
31    #[must_use]
32    pub const fn new(writable: bool) -> Self {
33        Self {
34            writable,
35            pin: false,
36            persist: false,
37            addr_info: AddrInfoOptions::RelayAndAddresses,
38        }
39    }
40    #[must_use]
41    pub const fn with_pin(mut self, pin: bool) -> Self {
42        self.pin = pin;
43        self
44    }
45
46    #[must_use]
47    pub const fn with_persist(mut self, persist: bool) -> Self {
48        self.persist = persist;
49        self
50    }
51
52    #[must_use]
53    pub const fn with_addr_info(mut self, addr_info: AddrInfoOptions) -> Self {
54        self.addr_info = addr_info;
55        self
56    }
57}
58
59/// The outcome of sharing a folder.
60#[derive(Clone, Debug)]
61#[non_exhaustive]
62pub struct ShareResult {
63    pub namespace: NamespaceId,
64    pub writable: bool,
65    pub ticket: DocTicket,
66    pub url: String,
67    pub pinned: usize,
68}
69
70impl ShareResult {
71    #[must_use]
72    pub const fn access(&self) -> &'static str {
73        if self.writable { "write" } else { "read" }
74    }
75}
76
77/// Build a folder share ticket, optionally pinning and persisting it.
78///
79/// `persist` is invoked with the namespace, access label, and ticket when
80/// `options.persist` is set, letting the caller record the share in its own
81/// database without coupling this helper to a specific store.
82///
83/// # Errors
84///
85/// Returns an error if pinning or ticket creation fails, or the persist
86/// callback fails.
87pub async fn share_folder<F>(folder: &SyncwebFolder, options: ShareOptions, persist: F) -> Result<ShareResult>
88where
89    F: FnOnce(NamespaceId, &str, &DocTicket) -> Result<()>,
90{
91    let pinned = if options.pin {
92        folder.pin_all_content().await?
93    } else {
94        0
95    };
96    let ticket = folder.ticket_with_options(options.writable, options.addr_info).await?;
97    let result = ShareResult {
98        namespace: folder.namespace_id(),
99        writable: options.writable,
100        ticket,
101        url: String::new(),
102        pinned,
103    };
104    if options.persist {
105        persist(result.namespace, result.access(), &result.ticket)?;
106    }
107    let url = crate::uri::folder_url(result.namespace, &result.ticket);
108    Ok(ShareResult { url, ..result })
109}