tea_context/
provenance.rs1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4use crate::ContextProviderId;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum PromptAuthority {
10 Kernel,
12 Organization,
14 Product,
16 Workspace,
18 Tool,
20 Skill,
22 Session,
24 UserAddition,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum TrustLevel {
32 Trusted,
34 Delegated,
36 Untrusted,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum CacheScope {
44 None,
46 Run,
48 Session,
50 Profile,
52 Global,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase")]
59pub struct PromptProvenance {
60 provider_id: ContextProviderId,
61 source_kind: String,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 locator: Option<String>,
64}
65
66impl PromptProvenance {
67 pub fn new(
73 provider_id: ContextProviderId,
74 source_kind: impl Into<String>,
75 locator: Option<String>,
76 ) -> Result<Self, ProvenanceError> {
77 let source_kind = source_kind.into();
78 if !valid_source_kind(&source_kind)
79 || locator.as_ref().is_some_and(|value| {
80 value.is_empty() || value.len() > 2048 || value.chars().any(char::is_control)
81 })
82 {
83 return Err(ProvenanceError);
84 }
85 Ok(Self {
86 provider_id,
87 source_kind,
88 locator,
89 })
90 }
91
92 #[must_use]
94 pub const fn provider_id(&self) -> &ContextProviderId {
95 &self.provider_id
96 }
97 #[must_use]
99 pub fn source_kind(&self) -> &str {
100 &self.source_kind
101 }
102 #[must_use]
104 pub fn locator(&self) -> Option<&str> {
105 self.locator.as_deref()
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
111#[error("prompt provenance is invalid")]
112pub struct ProvenanceError;
113
114fn valid_source_kind(value: &str) -> bool {
115 let mut bytes = value.bytes();
116 value.len() <= 128
117 && bytes.next().is_some_and(|byte| byte.is_ascii_lowercase())
118 && bytes.all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'_')
119}