Skip to main content

systemprompt_models/brain/
scope.rs

1//! Who a fact is shared with: one user, a project, a group or the whole
2//! organisation.
3//!
4//! Copyright (c) systemprompt.io — Business Source License 1.1.
5//! See <https://systemprompt.io> for licensing details.
6
7use std::fmt;
8use std::str::FromStr;
9
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12use systemprompt_identifiers::UserId;
13
14/// The level of a [`FactScope`], as profiles and recall filters name it.
15#[derive(
16    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
17)]
18#[serde(rename_all = "snake_case")]
19pub enum ScopeLevel {
20    User,
21    Project,
22    Group,
23    Org,
24}
25
26/// A string that is not a [`ScopeLevel`].
27#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
28#[error("unknown fact scope {0:?}")]
29pub struct UnknownScopeLevel(pub String);
30
31impl ScopeLevel {
32    pub const ALL: [Self; 4] = [Self::User, Self::Project, Self::Group, Self::Org];
33
34    #[must_use]
35    pub const fn as_str(self) -> &'static str {
36        match self {
37            Self::User => "user",
38            Self::Project => "project",
39            Self::Group => "group",
40            Self::Org => "org",
41        }
42    }
43}
44
45impl fmt::Display for ScopeLevel {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.write_str(self.as_str())
48    }
49}
50
51impl FromStr for ScopeLevel {
52    type Err = UnknownScopeLevel;
53
54    fn from_str(s: &str) -> Result<Self, Self::Err> {
55        Self::ALL
56            .into_iter()
57            .find(|level| level.as_str() == s)
58            .ok_or_else(|| UnknownScopeLevel(s.to_owned()))
59    }
60}
61
62/// The audience a fact is scoped to, with the key that names it.
63#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
64#[serde(tag = "level", content = "key", rename_all = "snake_case")]
65pub enum FactScope {
66    User(UserId),
67    Project(String),
68    Group(String),
69    Org,
70}
71
72impl FactScope {
73    #[must_use]
74    pub const fn level(&self) -> ScopeLevel {
75        match self {
76            Self::User(_) => ScopeLevel::User,
77            Self::Project(_) => ScopeLevel::Project,
78            Self::Group(_) => ScopeLevel::Group,
79            Self::Org => ScopeLevel::Org,
80        }
81    }
82
83    #[must_use]
84    pub fn key(&self) -> Option<&str> {
85        match self {
86            Self::User(user) => Some(user.as_str()),
87            Self::Project(key) | Self::Group(key) => Some(key),
88            Self::Org => None,
89        }
90    }
91}