use std::io::Write;
use anyhow::{anyhow, Context, Result};
use doiget_core::orchestrator::{cite_metadata, resolve_only, MetadataOnlyOutcome};
use doiget_core::store::{render, FsStore, Metadata, Store};
use doiget_core::{CapabilityProfile, Ref};
use super::resolve_store_root;
#[allow(clippy::print_stderr)]
fn print_err(args: std::fmt::Arguments<'_>) {
eprintln!("{args}");
}
pub async fn run(input: String, offline: bool, _mode: super::output::OutputMode) -> Result<()> {
let ref_ = Ref::parse(&input).with_context(|| format!("invalid ref: {input}"))?;
if offline {
let bib = bib_from_store(&ref_)?.ok_or_else(|| {
anyhow!(
"--offline: no local store entry for {input} (fetch it first with `doiget fetch`)"
)
})?;
return write_bib(&bib);
}
let ctx = crate::commands::fetch::build_resolve_context()?;
let profile = CapabilityProfile::from_env().context("resolving capability profile")?;
match resolve_only(&ref_, &profile, &ctx).await {
Ok(outcome) => {
let mut metadata = cite_metadata(&ref_, &outcome);
if let Some(doi_ref) = published_doi_ref(&ref_, &outcome) {
match resolve_only(&doi_ref, &profile, &ctx).await {
Ok(doi_outcome) => {
metadata = merge_published(cite_metadata(&doi_ref, &doi_outcome), metadata);
}
Err(e) => print_err(format_args!(
"note: published-version DOI resolve failed ({e}); citing the arXiv preprint"
)),
}
}
let bib = render::to_bibtex(ref_.safekey().as_str(), &metadata);
write_bib(&bib)
}
Err(e) => {
match bib_from_store(&ref_)? {
Some(bib) => {
print_err(format_args!(
"note: live resolve failed ({e}); citing offline from the local store"
));
write_bib(&bib)
}
None => Err(anyhow::Error::new(e).context(format!(
"failed to resolve {input}, and no local store entry to cite offline"
))),
}
}
}
}
fn published_doi_ref(ref_: &Ref, outcome: &MetadataOnlyOutcome) -> Option<Ref> {
if !matches!(ref_, Ref::Arxiv(_)) {
return None;
}
let doi = outcome.metadata.get("doi").and_then(|v| v.as_str())?;
match Ref::parse(doi).ok()? {
r @ Ref::Doi(_) => Some(r),
Ref::Arxiv(_) => None,
}
}
fn merge_published(mut article: Metadata, arxiv: Metadata) -> Metadata {
article.arxiv_id = arxiv.arxiv_id;
article.arxiv_categories = arxiv.arxiv_categories;
article
}
fn bib_from_store(ref_: &Ref) -> Result<Option<String>> {
let store = FsStore::new(resolve_store_root()?)?;
let safekey = ref_.safekey();
Ok(store
.read(&safekey)?
.map(|m| render::to_bibtex(safekey.as_str(), &m)))
}
fn write_bib(bib: &str) -> Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
write!(out, "{bib}").context("failed to write BibTeX entry to stdout")
}