Skip to main content

khive_runtime/operations/
resolve_uuid_or_prefix.rs

1//! Shared UUID-or-prefix resolution for pack handlers.
2
3use uuid::Uuid;
4
5use crate::error::{RuntimeError, RuntimeResult};
6use crate::runtime::{KhiveRuntime, NamespaceToken};
7
8impl KhiveRuntime {
9    /// Parse a full UUID or resolve a compact hexadecimal prefix in the caller's
10    /// primary namespace, using the same lookup policy as [`Self::resolve_prefix`].
11    ///
12    /// A parseable full UUID is returned without a lookup or an existence,
13    /// liveness or authorization check. Otherwise input must contain at least
14    /// eight ASCII hexadecimal characters with no separators or whitespace.
15    /// Missing prefixes and invalid shapes return distinct `InvalidInput` errors;
16    /// lookup storage and ambiguity errors propagate unchanged.
17    pub async fn resolve_uuid_or_prefix(
18        &self,
19        token: &NamespaceToken,
20        s: &str,
21    ) -> RuntimeResult<Uuid> {
22        self.resolve_uuid_or_prefix_inner(token, s, None).await
23    }
24
25    /// Resolve as [`Self::resolve_uuid_or_prefix`], with the comm/schedule
26    /// validation messages prefixed by `verb`. Lookup errors, including
27    /// ambiguous prefixes and storage failures, propagate unchanged.
28    pub async fn resolve_uuid_or_prefix_for_verb(
29        &self,
30        token: &NamespaceToken,
31        s: &str,
32        verb: &str,
33    ) -> RuntimeResult<Uuid> {
34        self.resolve_uuid_or_prefix_inner(token, s, Some(verb))
35            .await
36    }
37
38    async fn resolve_uuid_or_prefix_inner(
39        &self,
40        token: &NamespaceToken,
41        s: &str,
42        verb: Option<&str>,
43    ) -> RuntimeResult<Uuid> {
44        if let Ok(uuid) = s.parse::<Uuid>() {
45            return Ok(uuid);
46        }
47        if s.len() >= 8 && s.chars().all(|c| c.is_ascii_hexdigit()) {
48            return match self.resolve_prefix(token, s).await? {
49                Some(uuid) => Ok(uuid),
50                None => Err(RuntimeError::InvalidInput(match verb {
51                    Some(verb) => format!("{verb}: no record matches prefix: {s:?}"),
52                    None => format!("no record matches prefix: {s:?}"),
53                })),
54            };
55        }
56        Err(RuntimeError::InvalidInput(match verb {
57            Some(verb) => {
58                format!("{verb}: invalid id {s:?}; expected full UUID or 8-char hex prefix")
59            }
60            None => format!("invalid UUID (expected full UUID or 8+ hex prefix): {s:?}"),
61        }))
62    }
63}