use crate::error::{ResolveError, Result};
use digstore_core::{Urn, DEFAULT_RESOURCE_KEY};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedUrn {
pub urn: Urn,
pub salt: Option<String>,
}
impl ParsedUrn {
pub fn parse(input: &str) -> Result<ParsedUrn> {
let trimmed = input.trim();
let (core_part, salt) = match trimmed.rsplit_once("?salt=") {
Some((head, salt_hex)) => {
let salt_hex = salt_hex.trim();
if salt_hex.is_empty() || !salt_hex.bytes().all(|b| b.is_ascii_hexdigit()) {
return Err(ResolveError::Parse(
"?salt= must be non-empty lowercase hex".into(),
));
}
(head, Some(salt_hex.to_ascii_lowercase()))
}
None => (trimmed, None),
};
let urn = Urn::parse(core_part).map_err(|e| ResolveError::Parse(format!("{e:?}")))?;
Ok(ParsedUrn { urn, salt })
}
pub fn store_id_hex(&self) -> String {
self.urn.store_id.to_hex()
}
pub fn root_hex(&self) -> Option<String> {
self.urn.root_hash.map(|r| r.to_hex())
}
pub fn resource_key(&self) -> &str {
match self.urn.resource_key.as_deref() {
Some(k) if !k.is_empty() => k,
_ => DEFAULT_RESOURCE_KEY,
}
}
pub fn canonical_rootless(&self) -> Urn {
Urn {
chain: self.urn.chain.clone(),
store_id: self.urn.store_id,
root_hash: None,
resource_key: Some(self.resource_key().to_string()),
}
}
pub fn retrieval_key_hex(&self) -> String {
self.canonical_rootless().retrieval_key().to_hex()
}
}