miyabi_github/
client.rs

1//! GitHub API client wrapper
2//!
3//! Provides a high-level interface to GitHub API using octocrab
4
5use miyabi_types::error::{MiyabiError, Result};
6use octocrab::Octocrab;
7
8/// GitHub API client
9#[derive(Clone)]
10pub struct GitHubClient {
11    pub(crate) client: Octocrab,
12    pub(crate) owner: String,
13    pub(crate) repo: String,
14}
15
16impl GitHubClient {
17    /// Create a new GitHub client with personal access token
18    ///
19    /// # Arguments
20    /// * `token` - GitHub personal access token (ghp_xxx)
21    /// * `owner` - Repository owner (user or organization)
22    /// * `repo` - Repository name
23    ///
24    /// # Example
25    /// ```no_run
26    /// use miyabi_github::GitHubClient;
27    ///
28    /// let client = GitHubClient::new("ghp_xxx", "user", "repo")?;
29    /// # Ok::<(), miyabi_types::error::MiyabiError>(())
30    /// ```
31    pub fn new(
32        token: impl Into<String>,
33        owner: impl Into<String>,
34        repo: impl Into<String>,
35    ) -> Result<Self> {
36        let client = Octocrab::builder()
37            .personal_token(token.into())
38            .build()
39            .map_err(|e| MiyabiError::GitHub(format!("Failed to build Octocrab client: {}", e)))?;
40
41        Ok(Self {
42            client,
43            owner: owner.into(),
44            repo: repo.into(),
45        })
46    }
47
48    /// Get the underlying Octocrab client for advanced usage
49    pub fn octocrab(&self) -> &Octocrab {
50        &self.client
51    }
52
53    /// Get repository owner
54    pub fn owner(&self) -> &str {
55        &self.owner
56    }
57
58    /// Get repository name
59    pub fn repo(&self) -> &str {
60        &self.repo
61    }
62
63    /// Get full repository name (owner/repo)
64    pub fn full_name(&self) -> String {
65        format!("{}/{}", self.owner, self.repo)
66    }
67
68    /// Verify authentication by fetching current user
69    pub async fn verify_auth(&self) -> Result<String> {
70        let user = self
71            .client
72            .current()
73            .user()
74            .await
75            .map_err(|e| MiyabiError::GitHub(format!("Authentication failed: {}", e)))?;
76
77        Ok(user.login)
78    }
79
80    /// Get repository information
81    pub async fn get_repository(&self) -> Result<octocrab::models::Repository> {
82        self.client
83            .repos(&self.owner, &self.repo)
84            .get()
85            .await
86            .map_err(|e| {
87                MiyabiError::GitHub(format!(
88                    "Failed to get repository {}/{}: {}",
89                    self.owner, self.repo, e
90                ))
91            })
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[tokio::test]
100    async fn test_client_creation() {
101        // Test with dummy token (will fail auth, but construction should work)
102        let client = GitHubClient::new("ghp_test", "owner", "repo");
103        assert!(client.is_ok());
104
105        let client = client.unwrap();
106        assert_eq!(client.owner(), "owner");
107        assert_eq!(client.repo(), "repo");
108        assert_eq!(client.full_name(), "owner/repo");
109    }
110
111    #[tokio::test]
112    async fn test_client_cloning() {
113        let client = GitHubClient::new("ghp_test", "owner", "repo").unwrap();
114        let _cloned = client.clone();
115        assert_eq!(client.owner(), "owner");
116    }
117
118    // Note: Integration tests requiring real GitHub API calls are in tests/ directory
119}