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};
9
10use crate::error::Result;
11
12use super::SyncwebFolder;
13
14/// Options controlling how a folder is shared.
15#[derive(Clone, Copy, Debug, Default)]
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}
25
26impl ShareOptions {
27    #[must_use]
28    pub const fn new(writable: bool) -> Self {
29        Self {
30            writable,
31            pin: false,
32            persist: false,
33        }
34    }
35
36    #[must_use]
37    pub const fn with_pin(mut self, pin: bool) -> Self {
38        self.pin = pin;
39        self
40    }
41
42    #[must_use]
43    pub const fn with_persist(mut self, persist: bool) -> Self {
44        self.persist = persist;
45        self
46    }
47}
48
49/// The outcome of sharing a folder.
50#[derive(Clone, Debug)]
51#[non_exhaustive]
52pub struct ShareResult {
53    pub namespace: NamespaceId,
54    pub writable: bool,
55    pub ticket: DocTicket,
56    pub url: String,
57    pub pinned: usize,
58}
59
60impl ShareResult {
61    #[must_use]
62    pub const fn access(&self) -> &'static str {
63        if self.writable { "write" } else { "read" }
64    }
65}
66
67/// Build a folder share ticket, optionally pinning and persisting it.
68///
69/// `persist` is invoked with the namespace, access label, and ticket when
70/// `options.persist` is set, letting the caller record the share in its own
71/// database without coupling this helper to a specific store.
72///
73/// # Errors
74///
75/// Returns an error if pinning or ticket creation fails, or the persist
76/// callback fails.
77pub async fn share_folder<F>(folder: &SyncwebFolder, options: ShareOptions, persist: F) -> Result<ShareResult>
78where
79    F: FnOnce(NamespaceId, &str, &DocTicket) -> Result<()>,
80{
81    let pinned = if options.pin {
82        folder.pin_all_content().await?
83    } else {
84        0
85    };
86    let ticket = folder.ticket(options.writable).await?;
87    let result = ShareResult {
88        namespace: folder.namespace_id(),
89        writable: options.writable,
90        ticket,
91        url: String::new(),
92        pinned,
93    };
94    if options.persist {
95        persist(result.namespace, result.access(), &result.ticket)?;
96    }
97    let url = crate::uri::folder_url(result.namespace, &result.ticket);
98    Ok(ShareResult { url, ..result })
99}