11//! GitHub API client implementation
2+ //!
3+ //! This module provides the main `GitHubClient` struct which serves as the entry point
4+ //! for all GitHub API operations. The client encapsulates authentication and HTTP client
5+ //! state, making it easy to perform various GitHub operations.
6+ //!
7+ //! ## Architecture
8+ //!
9+ //! The `GitHubClient` follows a modular design where different API endpoints are organized
10+ //! into separate modules:
11+ //! - `pull_requests.rs` - Pull request operations
12+ //! - `repositories.rs` - Repository information and releases
13+ //!
14+ //! Each module extends the `GitHubClient` with `impl` blocks containing related methods.
215
316use super :: auth:: GitHubAuth ;
4- use super :: types:: { PullRequestParams , constants:: * } ;
517use anyhow:: Result ;
618use reqwest:: Client ;
7- use serde_json:: { Value , json} ;
819
9- /// GitHub API client
20+ /// GitHub API client for interacting with GitHub's REST API
21+ ///
22+ /// This client provides a unified interface for GitHub API operations, managing
23+ /// authentication and HTTP client state. Different API endpoints are organized
24+ /// into separate modules that extend this client with specific functionality.
25+ ///
26+ /// ## Features
27+ ///
28+ /// - **Authentication Management**: Handles GitHub token authentication
29+ /// - **URL Parsing**: Supports both GitHub.com and GitHub Enterprise URLs
30+ /// - **Modular Design**: API operations are organized by functionality
31+ /// - **Error Handling**: Comprehensive error handling for API responses
32+ ///
33+ /// ## Example
34+ ///
35+ /// ```rust,no_run
36+ /// use repos::github::GitHubClient;
37+ ///
38+ /// # async fn example() -> anyhow::Result<()> {
39+ /// // Create client with authentication
40+ /// let client = GitHubClient::new(Some("your_github_token".to_string()));
41+ ///
42+ /// // Parse repository URL
43+ /// let (owner, repo) = client.parse_github_url("https://github.com/owner/repo")?;
44+ ///
45+ /// // Use client for various operations (see specific modules for examples)
46+ /// // - Pull requests: client.create_pull_request()
47+ /// // - Repositories: client.get_repository()
48+ /// # Ok(())
49+ /// # }
50+ /// ```
1051pub struct GitHubClient {
11- client : Client ,
12- auth : Option < GitHubAuth > ,
52+ pub ( crate ) client : Client ,
53+ pub ( crate ) auth : Option < GitHubAuth > ,
1354}
1455
1556impl GitHubClient {
1657 /// Create a new GitHub client
58+ ///
59+ /// # Arguments
60+ /// * `token` - Optional GitHub personal access token for authentication
61+ ///
62+ /// # Returns
63+ /// A new GitHubClient instance
64+ ///
65+ /// # Example
66+ /// ```rust
67+ /// use repos::github::GitHubClient;
68+ ///
69+ /// // Client without authentication (for public repositories)
70+ /// let public_client = GitHubClient::new(None);
71+ ///
72+ /// // Client with authentication (for private repos and higher rate limits)
73+ /// let auth_client = GitHubClient::new(Some("your_token".to_string()));
74+ /// ```
1775 pub fn new ( token : Option < String > ) -> Self {
1876 let auth = token. map ( GitHubAuth :: new) ;
1977 Self {
@@ -23,7 +81,30 @@ impl GitHubClient {
2381 }
2482
2583 /// Parse GitHub URL to extract owner and repository name
26- /// Supports both github.com and enterprise GitHub instances
84+ ///
85+ /// Supports both github.com and enterprise GitHub instances with various URL formats:
86+ /// - SSH: `git@github.com:owner/repo` or `git@github-enterprise:owner/repo`
87+ /// - HTTPS: `https://github.com/owner/repo` or `https://github-enterprise/owner/repo`
88+ /// - Legacy: `github.com/owner/repo`
89+ ///
90+ /// # Arguments
91+ /// * `url` - The GitHub repository URL to parse
92+ ///
93+ /// # Returns
94+ /// A tuple containing (owner, repository_name)
95+ ///
96+ /// # Errors
97+ /// Returns an error if the URL format is not recognized as a valid GitHub URL
98+ ///
99+ /// # Example
100+ /// ```rust
101+ /// use repos::github::GitHubClient;
102+ ///
103+ /// let client = GitHubClient::new(None);
104+ /// let (owner, repo) = client.parse_github_url("https://github.com/rust-lang/rust").unwrap();
105+ /// assert_eq!(owner, "rust-lang");
106+ /// assert_eq!(repo, "rust");
107+ /// ```
27108 pub fn parse_github_url ( & self , url : & str ) -> Result < ( String , String ) > {
28109 let url = url. trim_end_matches ( '/' ) . trim_end_matches ( ".git" ) ;
29110
@@ -49,46 +130,23 @@ impl GitHubClient {
49130 return Ok ( ( owner, repo) ) ;
50131 }
51132
52- Err ( anyhow:: anyhow!( "Invalid GitHub URL: {}" , url) )
133+ Err ( anyhow:: anyhow!( "Invalid GitHub URL format : {}" , url) )
53134 }
54135
55- /// Create a pull request
56- pub async fn create_pull_request ( & self , params : PullRequestParams < ' _ > ) -> Result < Value > {
57- let auth = self
58- . auth
59- . as_ref ( )
60- . ok_or_else ( || anyhow:: anyhow!( "GitHub token is required" ) ) ?;
61-
62- let url = format ! (
63- "{}/repos/{}/{}/pulls" ,
64- GITHUB_API_BASE , params. owner, params. repo
65- ) ;
66-
67- let payload = json ! ( {
68- "title" : params. title,
69- "body" : params. body,
70- "head" : params. head,
71- "base" : params. base,
72- "draft" : params. draft
73- } ) ;
74-
75- let response = self
76- . client
77- . post ( & url)
78- . header ( "Authorization" , format ! ( "token {}" , auth. token( ) ) )
79- . header ( "User-Agent" , DEFAULT_USER_AGENT )
80- . header ( "Accept" , "application/vnd.github.v3+json" )
81- . json ( & payload)
82- . send ( )
83- . await ?;
84-
85- if response. status ( ) . is_success ( ) {
86- let result: Value = response. json ( ) . await ?;
87- Ok ( result)
88- } else {
89- let error_text = response. text ( ) . await ?;
90- Err ( anyhow:: anyhow!( "GitHub API error: {}" , error_text) )
91- }
136+ /// Check if the client has authentication configured
137+ ///
138+ /// # Returns
139+ /// `true` if the client has a GitHub token configured, `false` otherwise
140+ pub fn is_authenticated ( & self ) -> bool {
141+ self . auth . is_some ( )
142+ }
143+
144+ /// Get the authentication token (if available)
145+ ///
146+ /// # Returns
147+ /// `Some(token)` if authenticated, `None` otherwise
148+ pub fn token ( & self ) -> Option < & str > {
149+ self . auth . as_ref ( ) . map ( |auth| auth. token ( ) )
92150 }
93151}
94152
0 commit comments