gdown-cli 0.1.0

CLI for downloading files from Google Drive
//! gdown-cli - CLI for Google Drive downloads

use clap::Parser;
use gdown_core::download::{Downloader, DownloadOptions};
use gdown_core::folder::{download_folder, FolderDownloadOptions};
use gdown_core::{Result};
use std::path::PathBuf;
use tracing_subscriber::EnvFilter;

#[derive(Parser, Debug)]
#[command(
    name = "gdown",
    about = "Download files from Google Drive",
    long_about = None,
    author = "gdown-rs contributors"
)]
struct Args {
    /// URL or file/folder ID to download
    #[arg()]
    url: String,

    /// Output file name/path
    #[arg(short = 'O', long)]
    output: Option<PathBuf>,

    /// Suppress output
    #[arg(short = 'q', long)]
    quiet: bool,

    /// Proxy URL (http://host:port or socks5://host:port)
    #[arg(long)]
    proxy: Option<String>,

    /// Download speed limit (e.g., "10MB/s")
    #[arg(long)]
    speed: Option<String>,

    /// Don't use cookies
    #[arg(long)]
    no_cookies: bool,

    /// Don't check TLS certificate
    #[arg(long)]
    no_check_certificate: bool,

    /// Resume interrupted downloads
    #[arg(short = 'c', long)]
    continue_resume: bool,

    /// Download entire folder
    #[arg(long)]
    folder: bool,

    /// Export format for Google Docs/Sheets/Slides
    #[arg(long)]
    format: Option<String>,

    /// MD5 hash for verification
    #[arg(long)]
    md5: Option<String>,

    /// SHA1 hash for verification
    #[arg(long)]
    sha1: Option<String>,

    /// SHA256 hash for verification
    #[arg(long)]
    sha256: Option<String>,

    /// SHA512 hash for verification
    #[arg(long)]
    sha512: Option<String>,

    /// Extract archive after download
    #[arg(long)]
    extract: bool,
}

impl Args {
    fn parse_speed(&self) -> Option<u64> {
        self.speed.as_ref().map(|s| {
            let s = s.trim().to_uppercase();
            if let Some(num) = s.strip_suffix("KB/S") {
                num.parse::<u64>().unwrap_or(0) * 1024
            } else if let Some(num) = s.strip_suffix("MB/S") {
                num.parse::<u64>().unwrap_or(0) * 1024 * 1024
            } else if let Some(num) = s.strip_suffix("GB/S") {
                num.parse::<u64>().unwrap_or(0) * 1024 * 1024 * 1024
            } else {
                s.parse::<u64>().unwrap_or_default()
            }
        })
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let args = Args::parse();

    // Initialize logging
    if !args.quiet {
        tracing_subscriber::fmt()
            .with_env_filter(EnvFilter::from_default_env())
            .init();
    }

    // Create downloader
    let mut downloader = Downloader::new();
    if let Some(proxy) = &args.proxy {
        downloader = downloader.proxy(proxy);
    }
    if args.no_check_certificate {
        downloader = downloader.verify_ssl(false);
    }

    if args.folder {
        download_folder_cmd(&args, &downloader).await?;
    } else {
        download_file_cmd(&args, &downloader).await?;
    }

    Ok(())
}

async fn download_file_cmd(args: &Args, downloader: &Downloader) -> Result<()> {
    let output = args.output.clone().unwrap_or_else(|| {
        // Try to extract filename from URL or use default
        PathBuf::from("download")
    });

    let speed_limit = args.parse_speed();

    let options = DownloadOptions {
        speed_limit,
        resume: args.continue_resume,
        format: args.format.clone(),
        progress_callback: None,
    };

    // Create progress bar if not quiet
    if !args.quiet {
        println!("Downloading {} to {}", args.url, output.display());
    }

    let size = downloader.download(&args.url, &output, options).await?;

    if !args.quiet {
        println!("Downloaded {} bytes to {}", size, output.display());
    }

    Ok(())
}

async fn download_folder_cmd(args: &Args, downloader: &Downloader) -> Result<()> {
    let output = args.output.clone().unwrap_or_else(|| PathBuf::from("download"));
    let speed_limit = args.parse_speed();

    let options = FolderDownloadOptions {
        speed_limit,
        resume: args.continue_resume,
    };

    if !args.quiet {
        println!("Downloading folder {} to {}", args.url, output.display());
    }

    let files = download_folder(downloader, &args.url, output, options).await?;

    if !args.quiet {
        println!("Downloaded {} files/folders", files.len());
    }

    Ok(())
}