this repo has no description
1use std::collections::HashMap;
2use std::sync::LazyLock;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum ScopeCategory {
6 Core,
7 Transition,
8 Repo,
9 Blob,
10 Rpc,
11 Account,
12}
13
14impl ScopeCategory {
15 pub fn display_name(&self) -> &'static str {
16 match self {
17 ScopeCategory::Core => "Core Access",
18 ScopeCategory::Transition => "Transition",
19 ScopeCategory::Repo => "Repository",
20 ScopeCategory::Blob => "Media",
21 ScopeCategory::Rpc => "API Access",
22 ScopeCategory::Account => "Account",
23 }
24 }
25}
26
27#[derive(Debug, Clone)]
28pub struct ScopeDefinition {
29 pub scope: &'static str,
30 pub category: ScopeCategory,
31 pub required: bool,
32 pub description: &'static str,
33 pub display_name: &'static str,
34}
35
36pub static SCOPE_DEFINITIONS: LazyLock<HashMap<&'static str, ScopeDefinition>> =
37 LazyLock::new(|| {
38 let definitions = vec![
39 ScopeDefinition {
40 scope: "atproto",
41 category: ScopeCategory::Core,
42 required: true,
43 description: "Full access to read, write, and manage this account",
44 display_name: "Full Account Access",
45 },
46 ScopeDefinition {
47 scope: "transition:generic",
48 category: ScopeCategory::Transition,
49 required: false,
50 description: "Generic transition scope for compatibility",
51 display_name: "Transition Access",
52 },
53 ScopeDefinition {
54 scope: "transition:chat.bsky",
55 category: ScopeCategory::Transition,
56 required: false,
57 description: "Access to Bluesky chat features",
58 display_name: "Chat Access",
59 },
60 ScopeDefinition {
61 scope: "transition:email",
62 category: ScopeCategory::Account,
63 required: false,
64 description: "Read your account email address",
65 display_name: "Email Access",
66 },
67 ScopeDefinition {
68 scope: "repo:*?action=create",
69 category: ScopeCategory::Repo,
70 required: false,
71 description: "Create new records in your repository",
72 display_name: "Create Records",
73 },
74 ScopeDefinition {
75 scope: "repo:*?action=update",
76 category: ScopeCategory::Repo,
77 required: false,
78 description: "Update existing records in your repository",
79 display_name: "Update Records",
80 },
81 ScopeDefinition {
82 scope: "repo:*?action=delete",
83 category: ScopeCategory::Repo,
84 required: false,
85 description: "Delete records from your repository",
86 display_name: "Delete Records",
87 },
88 ScopeDefinition {
89 scope: "blob:*/*",
90 category: ScopeCategory::Blob,
91 required: false,
92 description: "Upload images, videos, and other media files",
93 display_name: "Upload Media",
94 },
95 ScopeDefinition {
96 scope: "repo:*",
97 category: ScopeCategory::Repo,
98 required: false,
99 description: "Full read and write access to all repository records",
100 display_name: "Full Repository Access",
101 },
102 ScopeDefinition {
103 scope: "account:*?action=manage",
104 category: ScopeCategory::Account,
105 required: false,
106 description: "Manage account settings and preferences",
107 display_name: "Manage Account",
108 },
109 ];
110
111 definitions.into_iter().map(|d| (d.scope, d)).collect()
112 });
113
114#[allow(dead_code)]
115pub fn get_scope_definition(scope: &str) -> Option<&'static ScopeDefinition> {
116 SCOPE_DEFINITIONS.get(scope)
117}
118
119#[allow(dead_code)]
120pub fn is_valid_scope(scope: &str) -> bool {
121 if SCOPE_DEFINITIONS.contains_key(scope) {
122 return true;
123 }
124 if scope.starts_with("ref:") {
125 return true;
126 }
127 false
128}
129
130#[allow(dead_code)]
131pub fn get_required_scopes() -> Vec<&'static str> {
132 SCOPE_DEFINITIONS
133 .values()
134 .filter(|d| d.required)
135 .map(|d| d.scope)
136 .collect()
137}
138
139#[allow(dead_code)]
140pub fn format_scope_for_display(scope: &str) -> String {
141 if let Some(def) = get_scope_definition(scope) {
142 def.description.to_string()
143 } else if scope.starts_with("ref:") {
144 "Referenced scope".to_string()
145 } else {
146 format!("Access to {}", scope)
147 }
148}