Skip to main content

syncweb_core/
uri.rs

1//! The `syncweb://` URI and ticket codec.
2//!
3//! Folder shares are encoded as `syncweb://folder/<namespace>?ticket=<ticket>`.
4//! This module is the single writer, reader, and validator for that format so
5//! share URLs round-trip through the CLI, the daemon, and the core library.
6
7use iroh_docs::{DocTicket, NamespaceId};
8
9use crate::constants::LINK_SCHEME;
10use crate::error::{Result, SyncwebError};
11
12/// The `syncweb://folder/<namespace>?ticket=<ticket>` share URL.
13#[must_use]
14pub fn folder_url(namespace: NamespaceId, ticket: &DocTicket) -> String {
15    format!("{LINK_SCHEME}folder/{namespace}?ticket={ticket}")
16}
17
18/// Whether a value is a `syncweb://folder/` share URL.
19#[must_use]
20pub fn is_folder_url(value: &str) -> bool {
21    value.starts_with("syncweb://folder/")
22}
23
24/// Parse a folder share URL or a bare iroh-docs ticket into a [`DocTicket`].
25///
26/// Both `syncweb://folder/<namespace>?ticket=<ticket>` and a plain
27/// `<ticket>` are accepted.
28///
29/// # Errors
30///
31/// Returns an error if neither a query `ticket=` parameter nor a valid ticket
32/// is present.
33pub fn parse_folder_ticket(value: &str) -> Result<DocTicket> {
34    let mut ticket_raw = value;
35    if let Some(rest) = value.strip_prefix(LINK_SCHEME)
36        && let Some((_, query)) = rest.split_once('?')
37    {
38        for param in query.split('&') {
39            if let Some(ticket) = param.strip_prefix("ticket=") {
40                ticket_raw = ticket;
41                break;
42            }
43        }
44    }
45    ticket_raw
46        .parse::<DocTicket>()
47        .map_err(|error| SyncwebError::InvalidTicket(error.to_string()))
48}