With Octocrab crate
IntroductionThe github_api repository demonstrates how to interact with the GitHub API using Rust. This guide will walk you through setting up a new Rust project and understanding the code in env.rs, github.rs, and main.rs. By the end, you'll have a clear understanding of how to fetch commits from a GitHub repository using Rust. We’ll examine each file’s specific implementation details and how they work together.
\ including:
Async/await programming
Builder pattern implementation
Structured error handling
Environment-based configuration
Secure token management
Comprehensive logging
\
Note: To Install Rust if you do not have already installed, you may visit Rust Language web site and follow the instructions. Feel free to ask any question if you have.
Development Setting Up the ProjectTo start, create a new Rust project using Cargo:
cargo new github_api cd github_api Cargo.tomlNext, update your Cargo.toml file to include the necessary dependencies. These dependencies include octocrab for interacting with the GitHub API, tokio for asynchronous runtime, tracing for logging, dotenv for environment variable management, and anyhow for error handling.
[package] name = "github_api" version = "0.1.0" edition = "2021" [dependencies] dotenv = "0.15.0" anyhow = "1.0.94" tokio = { version = "1.42.0", features = ["full"] } octocrab = "0.42.1" tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } colored = "2.2.0" env.rsThe env.rs file handles the configuration of the GitHub API client by loading environment variables.
use anyhow::*; use colored::Colorize; use std::env::var; /// A Config represents the configuration of the GitHub API client. /// /// The Config should be generated from the user's environment, and should /// contain the following fields: /// /// * `github_token`: A valid GitHub API token. /// pub struct Config { github_token: String, pub repo_owner: String, pub repo_name: String, pub log_level: String, } impl Config { /// Loads configuration from environment variables /// /// # Returns /// A Result containing the Config if successful, or an error if any required /// environment variables are missing pub fn from_env() -> anyhow::ResultIn this file, the Config struct is defined to hold configuration details. The from_env function loads these details from environment variables, and the validate function ensures the necessary variables are set. The use of .context with anyhow provides additional context for error messages, making it easier to debug issues related to missing environment variables.
\ Key Implementation Details:
The github.rs file initializes the GitHub API client using the Octocrab library.
use octocrab::Octocrab; use tracing::info; use anyhow::Result; #[derive(Debug)] pub struct GitHubClientBuilder { pub octocrab: Octocrab, } impl GitHubClientBuilder { pub async fn new(token: String) -> ResultThe GitHubClientBuilder struct is used to set up the GitHub API client. The new function creates an instance of Octocrab using the provided token, and the client function returns the initialized client.
\ Technical Features:
Serves for our project.
pub mod github; pub mod env; main.rsThe main.rs file is the entry point of the application, where the configuration is loaded, the client is initialized, and commits are fetched from the GitHub repository.
use std::{ error::Error, time::Duration, thread::sleep, io::Write}; use anyhow::Result; use colored::Colorize; use github_api::{ github::*, env::*}; use tracing::{info, error}; use tracing_subscriber::{ layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() -> Result<()> { let config = Config::from_env()?; // Initialize the tracing subscriber tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::new( config.log_level.to_string() )) .with(tracing_subscriber::fmt::layer().with_target(true)) .init(); // Clear the terminal std::process::Command::new("clear").status().unwrap();println!("\n"); info!("Config: Repo Owner {}", config.repo_owner); info!("Config: Repo Name {}", config.repo_name); info!("Config: Log Level {}", config.log_level); let github_client = GitHubClientBuilder::new(config.github_token()).await?.client(); info!("GitHub Client is ready!"); for _ in 0..10 { print!("."); std::io::stdout().flush().unwrap(); sleep(Duration::from_millis(100)); } println!("\n"); let commits = github_client.repos( config.repo_owner, config.repo_name).list_commits().send().await; std::process::Command::new("clear").status().unwrap();println!("\n"); match commits { Err(e) => { // Print the error message error!("Error: {}", e); // Print the source of the error if let Some(source) = e.source() { error!("Caused by: {}", source); } }, Ok(commits) => { println!("Commits received"); for commit in commits { println!("{}", commit.sha.green()); } }, } Ok(()) }In main.rs, the Config is loaded and used to initialize logging. The GitHubClientBuilder initializes the GitHub client, and the application fetches and prints the latest commits from the specified repository.
\ Key Technical Components:
Async Runtime:
\ Error Handling:
\ Logging System:
\ Output Formatting:
In addition to fetching commits, you can extend the functionality of your GitHub client to create issues, list pull requests, and fetch repository details. Here are some examples and you may try by yourselves
// Create an issue let issue = github_client .issues(config.repo_owner.clone(), config.repo_name.clone()) .create("New Issue Title") .body("This is the body of the new issue.") .send() .await?; info!("Created issue: {}", issue.html_url); // List pull requests let pulls = github_client .pulls(config.repo_owner.clone(), config.repo_name.clone()) .list() .send() .await?; for pull in pulls { println!("Pull Request: {} - {:?}", pull.number, pull.title); } // Fetch repository details let repo = github_client .repos(config.repo_owner.clone(), config.repo_name.clone()) .get() .await?; info!("Repository details fetched: {:?}", repo.full_name); ConclusionBy following the steps outlined above, you can set up a Rust project to interact with the GitHub API. The env.rs file handles configuration, github.rs initializes the client, and main.rs fetches and displays commit information. This setup demonstrates a practical approach to integrating Rust with GitHub's API using environment variables and the Octocrab library.
Thank you and have a nice day.
\ You may find another implementation of Github API on X-Bot repo too. This one implements Twitter API too.
\ The next article that I wrote, “Reference(&) vs. Arc, Mutex, Rc, and RefCell with Long-Lived Data Structures in Rust”.
\ Please feel free to comment about the articles here or any particular subject you are looking for in Rust…
All Rights Reserved. Copyright , Central Coast Communications, Inc.