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}