steer_tui/tui/state/
setup.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3use steer_core::config::provider::ProviderId;
4
5#[derive(Debug, Clone)]
6pub struct SetupState {
7    pub current_step: SetupStep,
8    pub auth_providers: HashMap<ProviderId, AuthStatus>,
9    pub selected_provider: Option<ProviderId>,
10    pub oauth_state: Option<OAuthFlowState>,
11    pub api_key_input: String,
12    pub oauth_callback_input: String,
13    pub error_message: Option<String>,
14    pub provider_cursor: usize,
15    pub skip_setup: bool,
16    pub registry: Arc<RemoteProviderRegistry>,
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub enum SetupStep {
21    Welcome,
22    ProviderSelection,
23    Authentication(ProviderId),
24    Completion,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum AuthStatus {
29    NotConfigured,
30    ApiKeySet,
31    OAuthConfigured,
32    InProgress,
33}
34
35#[derive(Debug, Clone)]
36pub struct OAuthFlowState {
37    pub auth_url: String,
38    pub state: String,
39    pub waiting_for_callback: bool,
40}
41
42/// Minimal provider view built from remote proto ProviderInfo
43#[derive(Debug, Clone)]
44pub struct RemoteProviderConfig {
45    pub id: String,
46    pub name: String,
47    pub auth_schemes: Vec<steer_grpc::proto::ProviderAuthScheme>,
48}
49
50#[derive(Debug, Clone)]
51pub struct RemoteProviderRegistry {
52    providers: Vec<RemoteProviderConfig>,
53}
54
55impl RemoteProviderRegistry {
56    pub fn from_proto(providers: Vec<steer_grpc::proto::ProviderInfo>) -> Self {
57        let providers = providers
58            .into_iter()
59            .map(|p| RemoteProviderConfig {
60                id: p.id,
61                name: p.name,
62                auth_schemes: p
63                    .auth_schemes
64                    .into_iter()
65                    .filter_map(|v| steer_grpc::proto::ProviderAuthScheme::try_from(v).ok())
66                    .collect(),
67            })
68            .collect();
69        Self { providers }
70    }
71
72    pub fn all(&self) -> impl Iterator<Item = &RemoteProviderConfig> {
73        self.providers.iter()
74    }
75
76    pub fn get(&self, id: &ProviderId) -> Option<&RemoteProviderConfig> {
77        self.providers.iter().find(|p| p.id == id.storage_key())
78    }
79}
80
81impl SetupState {
82    pub fn new(
83        registry: Arc<RemoteProviderRegistry>,
84        auth_providers: HashMap<ProviderId, AuthStatus>,
85    ) -> Self {
86        Self {
87            current_step: SetupStep::Welcome,
88            auth_providers,
89            selected_provider: None,
90            oauth_state: None,
91            api_key_input: String::new(),
92            oauth_callback_input: String::new(),
93            error_message: None,
94            provider_cursor: 0,
95            skip_setup: false,
96            registry,
97        }
98    }
99
100    /// Create a SetupState that skips the welcome page - for /auth command
101    pub fn new_for_auth_command(
102        registry: Arc<RemoteProviderRegistry>,
103        auth_providers: HashMap<ProviderId, AuthStatus>,
104    ) -> Self {
105        let mut state = Self::new(registry, auth_providers);
106        state.current_step = SetupStep::ProviderSelection;
107        state
108    }
109
110    pub fn next_step(&mut self) {
111        self.current_step = match &self.current_step {
112            SetupStep::Welcome => SetupStep::ProviderSelection,
113            SetupStep::ProviderSelection => {
114                if let Some(provider) = &self.selected_provider {
115                    SetupStep::Authentication(provider.clone())
116                } else {
117                    SetupStep::ProviderSelection
118                }
119            }
120            SetupStep::Authentication(_) => SetupStep::Completion,
121            SetupStep::Completion => SetupStep::Completion,
122        };
123        self.error_message = None;
124    }
125
126    pub fn previous_step(&mut self) {
127        self.current_step = match &self.current_step {
128            SetupStep::Welcome => SetupStep::Welcome,
129            SetupStep::ProviderSelection => SetupStep::Welcome,
130            SetupStep::Authentication(_) => SetupStep::ProviderSelection,
131            SetupStep::Completion => SetupStep::ProviderSelection,
132        };
133        self.error_message = None;
134    }
135
136    pub fn available_providers(&self) -> Vec<&RemoteProviderConfig> {
137        let mut providers: Vec<_> = self.registry.all().collect();
138        // Sort by name for consistent ordering
139        providers.sort_by_key(|p| p.name.clone());
140        providers
141    }
142}