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: "Use AT Protocol OAuth (required for all sessions)", 44 display_name: "AT Protocol", 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 ]; 96 97 definitions.into_iter().map(|d| (d.scope, d)).collect() 98 }); 99 100#[allow(dead_code)] 101pub fn get_scope_definition(scope: &str) -> Option<&'static ScopeDefinition> { 102 SCOPE_DEFINITIONS.get(scope) 103} 104 105#[allow(dead_code)] 106pub fn is_valid_scope(scope: &str) -> bool { 107 if SCOPE_DEFINITIONS.contains_key(scope) { 108 return true; 109 } 110 if scope.starts_with("ref:") { 111 return true; 112 } 113 false 114} 115 116#[allow(dead_code)] 117pub fn get_required_scopes() -> Vec<&'static str> { 118 SCOPE_DEFINITIONS 119 .values() 120 .filter(|d| d.required) 121 .map(|d| d.scope) 122 .collect() 123} 124 125#[allow(dead_code)] 126pub fn format_scope_for_display(scope: &str) -> String { 127 if let Some(def) = get_scope_definition(scope) { 128 def.description.to_string() 129 } else if scope.starts_with("ref:") { 130 "Referenced scope".to_string() 131 } else { 132 format!("Access to {}", scope) 133 } 134}