### Handle LFS files with GitHubClient and LfsPointer Source: https://context7.com/abhixdd/ghgrab/llms.txt The library supports GitHub LFS (Large File Storage) by automatically detecting and resolving pointer files to download the actual content. The LfsPointer struct can parse LFS pointer content to extract OID and size. The GitHubClient can then be used to get the LFS download URL and resolve LFS files within a list of repository items. ```rust use ghgrab::github::{GitHubClient, LfsPointer, RepoItem}; #[tokio::main] async fn main() -> anyhow::Result<()> { let client = GitHubClient::new(Some("ghp_token".to_string()))?; // LFS pointer file content looks like this: let lfs_content = r#"version https://git-lfs.github.com/spec/v1 oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393 size 12345678"#; // Parse LFS pointer to extract OID and size if let Some(pointer) = LfsPointer::parse(lfs_content) { println!("LFS OID: {}", pointer.oid); println!("LFS Size: {} bytes", pointer.size); // Get the actual download URL from GitHub LFS let download_url = client.get_lfs_download_url( "owner", "repo", &pointer.oid, pointer.size ).await?; println!("Download URL: {}", download_url); } // Resolve LFS files in a list of items automatically let mut items: Vec = vec![/* fetched items */]; client.resolve_lfs_files(&mut items, "owner", "repo", "main").await; // After resolution, use actual_download_url() and actual_size() for item in &items { if item.is_lfs() { println!("LFS file: {} (actual size: {:?})", item.name, item.actual_size() ); } } Ok(()) } ``` -------------------------------- ### Handle GitHub API Errors - Rust Source: https://context7.com/abhixdd/ghgrab/llms.txt The `GitHubError` enum in ghgrab provides structured error handling for various GitHub API-related failures. This includes invalid tokens, rate limit exceeded errors, not found resources, general API errors, and other unexpected issues. The example demonstrates how to match on these errors to implement fallback mechanisms or provide informative messages to the user. ```rust use ghgrab::github::{GitHubClient, GitHubError}; #[tokio::main] async fn main() { let client = GitHubClient::new(Some("invalid_token".to_string())).unwrap(); match client.fetch_recursive_tree("owner", "repo", "main").await { Ok(tree) => println!("Fetched {} items", tree.tree.len()), Err(GitHubError::InvalidToken) => { eprintln!("Token is invalid, falling back to public API"); // Retry without token let public_client = GitHubClient::new(None).unwrap(); // ... continue with public client } Err(GitHubError::RateLimitReached(user_type)) => { eprintln!("Rate limit exceeded for {}. Add a token for higher limits.", user_type); // user_type is "authenticated user" or "unauthenticated user" } Err(GitHubError::NotFound(resource)) => { eprintln!("Resource not found: {}", resource); // May need to try "master" branch instead of "main" } Err(GitHubError::ApiError(msg)) => { eprintln!("API error: {}", msg); } Err(GitHubError::Other(e)) => { eprintln!("Unexpected error: {}", e); } } } ``` -------------------------------- ### Run Terminal UI Programmatically - Rust Source: https://context7.com/abhixdd/ghgrab/llms.txt The `ui::run_tui` function allows launching the ghgrab interactive terminal user interface programmatically. It can be configured with an initial repository URL, a GitHub token, a download path, and options to download to the current working directory or to skip creating a repository subfolder. This provides flexibility in how the TUI is initiated. ```rust use ghgrab::ui; #[tokio::main] async fn main() -> anyhow::Result<()> { // Run TUI with default settings (no pre-loaded URL) ui::run_tui( None, // initial_url: Option None, // token: Option None, // download_path: Option false, // cwd: bool false, // no_folder: bool ).await?; // Run TUI with pre-loaded repository URL ui::run_tui( Some("https://github.com/rust-lang/rust".to_string()), Some("ghp_token".to_string()), Some("/custom/downloads".to_string()), false, false, ).await?; // Run TUI downloading to current directory without repo folder ui::run_tui( Some("https://github.com/user/repo/tree/main/src".to_string()), None, None, true, // cwd: download to current directory true, // no_folder: don't create repo subfolder ).await?; Ok(()) } ``` -------------------------------- ### Download Files and Folders with Progress - Rust Source: https://context7.com/abhixdd/ghgrab/llms.txt The Downloader struct in ghgrab handles downloading files and folders from a repository. It supports progress callbacks to monitor download status and can be initialized with a target directory and an optional GitHub token. The `download_items` function takes a list of repository items, the repository name, and a closure for progress reporting, returning any errors encountered. ```rust use ghgrab::download::Downloader; use ghgrab::github::RepoItem; use std::path::PathBuf; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create downloader with target directory and optional token let download_path = PathBuf::from("/home/user/downloads/repo"); let downloader = Downloader::new(download_path, Some("ghp_token".to_string()))?; // Prepare items to download (typically from GitHubClient) let items: Vec = vec![/* items from fetch_contents */]; // Download with progress callback let errors = downloader.download_items( &items, "owner/repo", |progress_msg| { println!("Progress: {}", progress_msg); // Output: "Downloading: README.md" // Output: "Downloading [LFS]: large-file.bin" // Output: "Scanning folder: src" } ).await?; if errors.is_empty() { println!("All downloads completed successfully!"); } else { for error in errors { eprintln!("Error: {}", error); } } Ok(()) } ``` -------------------------------- ### Interact with GitHub API using GitHubClient Source: https://context7.com/abhixdd/ghgrab/llms.txt The GitHubClient facilitates GitHub API calls, including fetching repository contents, retrieving directory trees, and handling raw file content. It supports both unauthenticated (rate-limited) and token-authenticated requests. The client can fetch contents at a single directory level or recursively traverse the entire repository tree. ```rust use ghgrab::github::{GitHubClient, GitHubUrl, RepoItem}; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create client without authentication (60 requests/hour limit) let client = GitHubClient::new(None)?; // Create client with GitHub token (5000 requests/hour limit) let auth_client = GitHubClient::new(Some("ghp_your_token".to_string()))?; // Parse a GitHub URL let gh_url = GitHubUrl::parse("https://github.com/abhixdd/ghgrab")?; // Fetch repository contents (single directory level) let items: Vec = client.fetch_contents(&gh_url.api_url()).await?; for item in &items { println!("{}: {} ({})", if item.is_dir() { "DIR" } else { "FILE" }, item.name, item.size.unwrap_or(0) ); } // Fetch entire repository tree recursively (more efficient for large repos) let tree = client.fetch_recursive_tree( &gh_url.owner, &gh_url.repo, &gh_url.branch ).await?; println!("Total items: {}, Truncated: {}", tree.tree.len(), tree.truncated); // Fetch raw file content if let Some(item) = items.iter().find(|i| i.is_file()) { if let Some(url) = &item.download_url { let content = client.fetch_raw_content(url).await?; println!("File content length: {}", content.len()); } } Ok(()) } ``` -------------------------------- ### Manage Persistent Configuration - Rust Source: https://context7.com/abhixdd/ghgrab/llms.txt The Config struct in ghgrab manages persistent application settings, typically stored in the user's configuration directory. It allows loading existing configurations, setting values like GitHub tokens and download paths, validating paths, and saving the configuration. The `load`, `save`, and `validate_path` methods are key for managing these settings. ```rust use ghgrab::config::Config; fn main() -> anyhow::Result<()> { // Load existing config or create default let mut config = Config::load().unwrap_or_default(); // Set GitHub token config.github_token = Some("ghp_xxxxxxxxxxxx".to_string()); // Validate and set download path let custom_path = "/home/user/github-downloads"; Config::validate_path(custom_path)?; config.download_path = Some(custom_path.to_string()); // Save configuration to ~/.config/ghgrab/config.json config.save()?; // Read config values if let Some(token) = &config.github_token { // Mask token for display let masked = if token.len() > 8 { format!("{}...{}", &token[..4], &token[token.len()-4..]) } else { "********".to_string() }; println!("Token: {}", masked); } if let Some(path) = &config.download_path { println!("Download path: {}", path); } else { println!("Download path: Default (~/Downloads)"); } Ok(()) } ``` -------------------------------- ### Parse GitHub URLs with GitHubUrl Source: https://context7.com/abhixdd/ghgrab/llms.txt The GitHubUrl struct parses various GitHub URL formats, extracting owner, repository, branch, and path. It can handle full URLs, root repository URLs (defaulting to 'main' branch), and blob URLs. It also provides a method to generate the corresponding API URL and detect local git remotes. ```rust use ghgrab::github::GitHubUrl; fn main() -> anyhow::Result<()> { // Parse a full GitHub URL with path let url = GitHubUrl::parse("https://github.com/rust-lang/rust/tree/master/src/tools")?; assert_eq!(url.owner, "rust-lang"); assert_eq!(url.repo, "rust"); assert_eq!(url.branch, "master"); assert_eq!(url.path, "src/tools"); // Parse a root repository URL (defaults to main branch) let root_url = GitHubUrl::parse("https://github.com/abhixdd/ghgrab")?; assert_eq!(root_url.owner, "abhixdd"); assert_eq!(root_url.repo, "ghgrab"); assert_eq!(root_url.branch, "main"); assert_eq!(root_url.path, ""); // Parse a blob URL (single file) let file_url = GitHubUrl::parse("https://github.com/owner/repo/blob/main/src/lib.rs")?; assert_eq!(file_url.path, "src/lib.rs"); // Generate API URL for fetching contents let api_url = url.api_url(); // Returns: "https://api.github.com/repos/rust-lang/rust/contents/src/tools?ref=master" // Detect local git remote if let Some(remote) = GitHubUrl::get_local_git_remote() { println!("Current repo: {}", remote); } Ok(()) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.