Skip to main content

claude_wrapper/command/
auth.rs

1use crate::Claude;
2use crate::command::ClaudeCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5
6/// Check authentication status.
7///
8/// # Example
9///
10/// ```no_run
11/// use claude_wrapper::{Claude, ClaudeCommand, AuthStatusCommand};
12///
13/// # async fn example() -> claude_wrapper::Result<()> {
14/// let claude = Claude::builder().build()?;
15/// let status = AuthStatusCommand::new().execute_json(&claude).await?;
16/// println!("logged in: {}", status.logged_in);
17/// # Ok(())
18/// # }
19/// ```
20#[derive(Debug, Clone, Default)]
21pub struct AuthStatusCommand {
22    json: bool,
23}
24
25impl AuthStatusCommand {
26    /// Create a new auth status command.
27    #[must_use]
28    pub fn new() -> Self {
29        Self { json: true }
30    }
31
32    /// Request text output instead of JSON.
33    #[must_use]
34    pub fn text(mut self) -> Self {
35        self.json = false;
36        self
37    }
38
39    /// Execute and parse the JSON result into an [`AuthStatus`](crate::types::AuthStatus).
40    #[cfg(feature = "json")]
41    pub async fn execute_json(&self, claude: &Claude) -> Result<crate::types::AuthStatus> {
42        let mut cmd = self.clone();
43        cmd.json = true;
44
45        let output = exec::run_claude(claude, cmd.args()).await?;
46
47        serde_json::from_str(&output.stdout).map_err(|e| crate::error::Error::Json {
48            message: format!("failed to parse auth status: {e}"),
49            source: e,
50        })
51    }
52}
53
54impl ClaudeCommand for AuthStatusCommand {
55    type Output = CommandOutput;
56
57    fn args(&self) -> Vec<String> {
58        let mut args = vec!["auth".to_string(), "status".to_string()];
59        if self.json {
60            args.push("--json".to_string());
61        } else {
62            args.push("--text".to_string());
63        }
64        args
65    }
66
67    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
68        exec::run_claude(claude, self.args()).await
69    }
70}
71
72/// Authenticate with Claude.
73///
74/// # Example
75///
76/// ```no_run
77/// use claude_wrapper::{Claude, ClaudeCommand, AuthLoginCommand};
78///
79/// # async fn example() -> claude_wrapper::Result<()> {
80/// let claude = Claude::builder().build()?;
81/// AuthLoginCommand::new()
82///     .email("user@example.com")
83///     .execute(&claude)
84///     .await?;
85/// # Ok(())
86/// # }
87/// ```
88#[derive(Debug, Clone, Default)]
89pub struct AuthLoginCommand {
90    email: Option<String>,
91    sso: Option<String>,
92}
93
94impl AuthLoginCommand {
95    /// Create a new auth login command.
96    #[must_use]
97    pub fn new() -> Self {
98        Self::default()
99    }
100
101    /// Set the email address for authentication.
102    #[must_use]
103    pub fn email(mut self, email: impl Into<String>) -> Self {
104        self.email = Some(email.into());
105        self
106    }
107
108    /// Set the SSO provider for authentication.
109    #[must_use]
110    pub fn sso(mut self, provider: impl Into<String>) -> Self {
111        self.sso = Some(provider.into());
112        self
113    }
114}
115
116impl ClaudeCommand for AuthLoginCommand {
117    type Output = CommandOutput;
118
119    fn args(&self) -> Vec<String> {
120        let mut args = vec!["auth".to_string(), "login".to_string()];
121        if let Some(ref email) = self.email {
122            args.push("--email".to_string());
123            args.push(email.clone());
124        }
125        if let Some(ref sso) = self.sso {
126            args.push("--sso".to_string());
127            args.push(sso.clone());
128        }
129        args
130    }
131
132    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
133        exec::run_claude(claude, self.args()).await
134    }
135}
136
137/// Deauthenticate from Claude.
138///
139/// # Example
140///
141/// ```no_run
142/// use claude_wrapper::{Claude, ClaudeCommand, AuthLogoutCommand};
143///
144/// # async fn example() -> claude_wrapper::Result<()> {
145/// let claude = Claude::builder().build()?;
146/// AuthLogoutCommand::new().execute(&claude).await?;
147/// # Ok(())
148/// # }
149/// ```
150#[derive(Debug, Clone, Default)]
151pub struct AuthLogoutCommand;
152
153impl AuthLogoutCommand {
154    /// Create a new auth logout command.
155    #[must_use]
156    pub fn new() -> Self {
157        Self
158    }
159}
160
161impl ClaudeCommand for AuthLogoutCommand {
162    type Output = CommandOutput;
163
164    fn args(&self) -> Vec<String> {
165        vec!["auth".to_string(), "logout".to_string()]
166    }
167
168    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
169        exec::run_claude(claude, self.args()).await
170    }
171}
172
173/// Set up a long-lived authentication token.
174///
175/// # Example
176///
177/// ```no_run
178/// use claude_wrapper::{Claude, ClaudeCommand, SetupTokenCommand};
179///
180/// # async fn example() -> claude_wrapper::Result<()> {
181/// let claude = Claude::builder().build()?;
182/// SetupTokenCommand::new().execute(&claude).await?;
183/// # Ok(())
184/// # }
185/// ```
186#[derive(Debug, Clone, Default)]
187pub struct SetupTokenCommand;
188
189impl SetupTokenCommand {
190    /// Create a new setup-token command.
191    #[must_use]
192    pub fn new() -> Self {
193        Self
194    }
195}
196
197impl ClaudeCommand for SetupTokenCommand {
198    type Output = CommandOutput;
199
200    fn args(&self) -> Vec<String> {
201        vec!["setup-token".to_string()]
202    }
203
204    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
205        exec::run_claude(claude, self.args()).await
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    #[test]
214    fn test_auth_status_args() {
215        let cmd = AuthStatusCommand::new();
216        assert_eq!(cmd.args(), vec!["auth", "status", "--json"]);
217    }
218
219    #[test]
220    fn test_auth_status_text() {
221        let cmd = AuthStatusCommand::new().text();
222        assert_eq!(cmd.args(), vec!["auth", "status", "--text"]);
223    }
224
225    #[test]
226    fn test_auth_login_default() {
227        let cmd = AuthLoginCommand::new();
228        assert_eq!(cmd.args(), vec!["auth", "login"]);
229    }
230
231    #[test]
232    fn test_auth_login_with_email() {
233        let cmd = AuthLoginCommand::new().email("user@example.com");
234        assert_eq!(
235            cmd.args(),
236            vec!["auth", "login", "--email", "user@example.com"]
237        );
238    }
239
240    #[test]
241    fn test_auth_login_with_sso() {
242        let cmd = AuthLoginCommand::new().sso("okta");
243        assert_eq!(cmd.args(), vec!["auth", "login", "--sso", "okta"]);
244    }
245
246    #[test]
247    fn test_auth_logout() {
248        let cmd = AuthLogoutCommand::new();
249        assert_eq!(cmd.args(), vec!["auth", "logout"]);
250    }
251
252    #[test]
253    fn test_setup_token() {
254        let cmd = SetupTokenCommand::new();
255        assert_eq!(cmd.args(), vec!["setup-token"]);
256    }
257}