mfs 0.2.1

Fetcher for scholarly metadata
use crate::cli::PubCommands;
use crate::output::{MetadataSource, ReportEntry, ReportStatus, write_data};
use hyper::StatusCode;
use nix::unistd::isatty;
use std::io::{self, BufRead};
use std::os::fd::AsRawFd;
use std::sync::mpsc;
use std::thread;
use tokio::time::Duration;

pub async fn handle_pub_command(commands: &PubCommands) {
    match commands {
        PubCommands::Doi {
            doi,
            source_list,
            output_path,
        } => {
            let dois = if let Some(input) = doi {
                vec![input.to_string()]
            } else {
                if isatty(io::stdin().as_raw_fd()).unwrap_or(false) {
                    eprintln!("No input was provided.");
                    std::process::exit(1);
                }

                let (sender, receiver) = mpsc::channel();

                // Spawn a thread for blocking stdin read
                thread::spawn(move || {
                    let stdin = io::stdin();
                    let mut buffer = String::new();
                    let mut handle = stdin.lock();

                    while let Ok(bytes) = handle.read_line(&mut buffer) {
                        if bytes == 0 {
                            // EOF reached
                            break;
                        }
                    }
                    // Send the entire buffer to the main thread
                    sender
                        .send(buffer.trim().to_string())
                        .expect("Failed to send input");
                    buffer.clear();
                });

                let timeout = Duration::from_millis(100);
                let data =
                    match tokio::time::timeout(timeout, async { receiver.recv().unwrap() }).await {
                        Ok(input) => input,
                        Err(_) => {
                            eprintln!("Timed out waiting for input on stdin");
                            "".to_string()
                        }
                    };
                let s: Vec<String> = data
                    .split("\n")
                    .map(|i| i.to_string())
                    .filter(|i| !i.is_empty())
                    .collect();

                s
            };
            let report = fetch_dois(&dois, source_list).await;
            // convert_dois()
            if !report.is_empty() {
                let _ = write_data(&report, output_path).await;
            }
        }
    }
}

pub async fn fetch_dois(dois: &Vec<String>, source_list: &[MetadataSource]) -> Vec<ReportEntry> {
    let mut report: Vec<ReportEntry> = Vec::new();

    for doi in dois {
        let mut status = ReportStatus::NotFound;

        for source in source_list {
            let response = reqwest::get(&source.url_for_doi(doi)).await.unwrap();
            let response = response.error_for_status();
            match response {
                Ok(resp) => match resp.text().await {
                    Ok(text) => {
                        status = ReportStatus::Ok(source.clone(), text);
                        break;
                    }
                    Err(err) => match err.status() {
                        Some(StatusCode::NOT_FOUND) => {}
                        _ => status = ReportStatus::Error,
                    },
                },
                Err(err) => match err.status() {
                    Some(StatusCode::NOT_FOUND) => {}
                    _ => status = ReportStatus::Error,
                },
            }
        }

        report.push(ReportEntry {
            doi: doi.clone(),
            status,
        });
    }

    report
}