1use bytes::Bytes;
2use iroh_docs::DocTicket;
3use miette::IntoDiagnostic;
4use path_clean::PathClean;
5use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
6use std::ffi::CString;
7use std::path::PathBuf;
8
9pub(super) fn normalise_path(path: &PathBuf) -> PathBuf {
10 PathBuf::from("/").join(path).clean()
11}
12
13pub fn path_to_entry_key(path: &PathBuf) -> Bytes {
23 let path = normalise_path(path);
24 let mut path_bytes = path.into_os_string().into_encoded_bytes();
25 path_bytes.push(b'\0');
26 path_bytes.into()
27}
28
29pub fn entry_key_to_path(key: &[u8]) -> miette::Result<PathBuf> {
39 Ok(PathBuf::from(
40 CString::from_vec_with_nul(key.to_vec())
41 .into_diagnostic()?
42 .into_string()
43 .into_diagnostic()?,
44 ))
45}
46
47pub fn path_to_entry_prefix(path: &PathBuf) -> Bytes {
57 let path = normalise_path(path);
58 let path_bytes = path.into_os_string().into_encoded_bytes();
59 path_bytes.into()
60}
61
62pub fn fmt(bytes: impl AsRef<[u8]>) -> String {
72 let mut text = data_encoding::BASE32_NOPAD.encode(bytes.as_ref());
73 text.make_ascii_lowercase();
74 text
75}
76
77pub fn fmt_short(bytes: impl AsRef<[u8]>) -> String {
87 let len = bytes.as_ref().len().min(10);
88 let mut text = data_encoding::BASE32_NOPAD.encode(&bytes.as_ref()[..len]);
89 text.make_ascii_lowercase();
90 text
91}
92
93pub fn parse_array<const N: usize>(input: &str) -> miette::Result<[u8; N]> {
103 data_encoding::BASE32_NOPAD
104 .decode(input.to_ascii_uppercase().as_bytes())
105 .into_diagnostic()?
106 .try_into()
107 .map_err(|_| {
108 miette::miette!(
109 "Unable to parse {input} as a base32-encoded byte array of length {N} … "
110 )
111 })
112}
113
114pub fn parse_array_hex_or_base32<const LEN: usize>(input: &str) -> miette::Result<[u8; LEN]> {
124 let mut bytes = [0u8; LEN];
125 if input.len() == LEN * 2 {
126 hex::decode_to_slice(input, &mut bytes).into_diagnostic()?;
127 Ok(bytes)
128 } else {
129 Ok(parse_array(input)?)
130 }
131}
132
133pub fn merge_tickets(tickets: &Vec<DocTicket>) -> Option<DocTicket> {
143 let ticket_parts: Vec<_> = tickets
144 .par_iter()
145 .map(|ticket| ticket.capability.clone())
146 .zip(tickets.par_iter().map(|ticket| ticket.nodes.clone()))
147 .collect();
148 ticket_parts
149 .into_iter()
150 .reduce(|mut merged_tickets, next_ticket| {
151 let _ = merged_tickets.0.merge(next_ticket.0);
152 merged_tickets.1.extend_from_slice(&next_ticket.1);
153 merged_tickets
154 })
155 .map(|mut merged_tickets| {
156 merged_tickets.1.sort_unstable();
157 merged_tickets.1.dedup();
158 DocTicket {
159 capability: merged_tickets.0,
160 nodes: merged_tickets.1,
161 }
162 })
163}