Skip to main content

systemprompt_cli/
descriptor.rs

1//! Per-command bootstrap descriptor: which of profile/secrets/paths a command
2//! needs.
3//!
4//! Copyright (c) systemprompt.io — Business Source License 1.1.
5//! See <https://systemprompt.io> for licensing details.
6
7#[derive(Debug, Clone, Copy, Default)]
8pub struct CommandDescriptor {
9    flags: u8,
10}
11
12impl CommandDescriptor {
13    const FLAG_PROFILE: u8 = 0b0000_0001;
14    const FLAG_SECRETS: u8 = 0b0000_0010;
15    const FLAG_PATHS: u8 = 0b0000_0100;
16    const FLAG_DATABASE: u8 = 0b0000_1000;
17    const FLAG_REMOTE_ELIGIBLE: u8 = 0b0001_0000;
18    const FLAG_SKIP_VALIDATION: u8 = 0b0010_0000;
19    const FLAG_READ_ONLY: u8 = 0b0100_0000;
20
21    pub const NONE: Self = Self { flags: 0 };
22
23    pub const PROFILE_ONLY: Self = Self {
24        flags: Self::FLAG_PROFILE,
25    };
26
27    pub const PROFILE_AND_SECRETS: Self = Self {
28        flags: Self::FLAG_PROFILE | Self::FLAG_SECRETS,
29    };
30
31    pub const PROFILE_SECRETS_AND_PATHS: Self = Self {
32        flags: Self::FLAG_PROFILE | Self::FLAG_SECRETS | Self::FLAG_PATHS,
33    };
34
35    pub const FULL: Self = Self {
36        flags: Self::FLAG_PROFILE
37            | Self::FLAG_SECRETS
38            | Self::FLAG_PATHS
39            | Self::FLAG_DATABASE
40            | Self::FLAG_REMOTE_ELIGIBLE,
41    };
42
43    pub const fn profile(&self) -> bool {
44        self.flags & Self::FLAG_PROFILE != 0
45    }
46
47    pub const fn secrets(&self) -> bool {
48        self.flags & Self::FLAG_SECRETS != 0
49    }
50
51    pub const fn paths(&self) -> bool {
52        self.flags & Self::FLAG_PATHS != 0
53    }
54
55    pub const fn database(&self) -> bool {
56        self.flags & Self::FLAG_DATABASE != 0
57    }
58
59    pub const fn routing_class(&self) -> RoutingClass {
60        if self.flags & Self::FLAG_REMOTE_ELIGIBLE == 0 {
61            RoutingClass::LocalOnly
62        } else if self.flags & Self::FLAG_READ_ONLY != 0 {
63            RoutingClass::ReadOnly
64        } else {
65            RoutingClass::Mutating
66        }
67    }
68
69    pub const fn skip_validation(&self) -> bool {
70        self.flags & Self::FLAG_SKIP_VALIDATION != 0
71    }
72
73    pub const fn with_remote_eligible(self) -> Self {
74        Self {
75            flags: self.flags | Self::FLAG_REMOTE_ELIGIBLE,
76        }
77    }
78
79    pub const fn with_read_only(self) -> Self {
80        Self {
81            flags: self.flags | Self::FLAG_READ_ONLY,
82        }
83    }
84
85    pub const fn with_skip_validation(self) -> Self {
86        Self {
87            flags: self.flags | Self::FLAG_SKIP_VALIDATION,
88        }
89    }
90}
91
92/// What a command is allowed to do when the active profile is a cloud profile.
93///
94/// `LocalOnly` is never routed remotely and runs against whatever the profile
95/// resolves; `ReadOnly` prefers remote when a session is available and falls
96/// back to local with a warning; `Mutating` must route remotely or fail loudly.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum RoutingClass {
99    LocalOnly,
100    ReadOnly,
101    Mutating,
102}
103
104pub trait DescribeCommand {
105    fn descriptor(&self) -> CommandDescriptor;
106}