#[non_exhaustive]pub struct FetchPaperOutcome {Show 16 fields
pub source: String,
pub resolver_profile: String,
pub license: String,
pub oa_status: Option<String>,
pub path: Utf8PathBuf,
pub size_bytes: u64,
pub schema_version: String,
pub pdf_leg: PdfLegStatus,
pub safekey: String,
pub canonical_digest: String,
pub title: String,
pub authors: Vec<String>,
pub year: Option<i32>,
pub attempts: Vec<SourceAttempt>,
pub metadata_quality: Vec<String>,
pub repaired_fields: BTreeMap<String, String>,
}Expand description
What fetch_paper wrote to disk and how.
path is the PDF (<root>/<safekey>.pdf) on a successful PDF
fetch, or the metadata TOML (<root>/.metadata/<safekey>.toml)
when the DOI path fell back to metadata-only. Self::pdf_leg
disambiguates why there is no PDF (genuinely none available vs.
available-but-blocked) so callers never report a blocked PDF as a
silent success (issue #118).
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.source: StringSource::name() of the resolver whose payload landed on disk:
"arxiv" for an arXiv ref, "oa-publisher" when the DOI OA PDF
leg succeeded, or "crossref" / "unpaywall" when the DOI path
fell back to metadata-only. Mirrors the value written to
[doiget].source in the metadata TOML.
resolver_profile: StringResolver profile under which the canonical-digest (ADR-0021 §1)
was minted for the final artifact. For an arXiv fetch this is
"arxiv"; for a successful DOI OA PDF leg this is
"oa-publisher"; for the DOI metadata-only fallback this is the
metadata source key ("crossref" / "unpaywall"). Equal to
Self::source verbatim in Slice 4 but kept distinct so future
slices can decouple “which resolver wrote to disk” from “which
resolver is the audit identity”. Surfaced through the
doiget_fetch_paper MCP envelope per ADR-0021 §4.
license: StringOA license string ("CC-BY-4.0", "cc-by", "arxiv-default",
"unknown"). Mirrors [doiget].license.
oa_status: Option<String>Open-access status (#281 item 4): Unpaywall’s gold / green /
hybrid / bronze / closed for a DOI, or "green" for an arXiv
ref. None when not determined. Mirrors [doiget].oa_status. Lets
a caller distinguish a paywalled work (closed + pdf_leg: NoOaUrl)
from one that is openly available.
path: Utf8PathBufAbsolute path of the artifact actually written
(<root>/<safekey>.pdf on success, <root>/.metadata/<safekey>.toml
on metadata-only fallback).
size_bytes: u64Stored PDF size in bytes; 0 on the metadata-only fallback
(docs/REDIRECT_ALLOWLIST.md §3.5).
schema_version: StringThe schema version of the metadata TOML written
(always crate::SCHEMA_VERSION for this build).
pdf_leg: PdfLegStatusWhat happened on the PDF leg (issue #118). Fetched /
NoOaUrl are clean outcomes; Blocked carries the structured
reason an OA PDF existed but could not be retrieved, so the
CLI / MCP surface it instead of a silent metadata-only success.
safekey: StringPer-ref crate::Safekey stringified (Ref::safekey().as_str()).
Exposed on the outcome so JSON-mode CLI / MCP callers can
emit a structured success body without re-parsing the input
ref (#210 / docs/ERRORS.md §3). Always populated.
canonical_digest: StringADR-0021 §1 canonical-digest as 64-char lowercase hex for the
resolver_profile that produced this outcome’s audit identity.
For an arXiv fetch this is the digest under "arxiv"; for a
DOI OA PDF leg this is under "oa-publisher"; for the DOI
metadata-only fallback this is under the metadata source key
("crossref" / "unpaywall"). Always populated.
title: StringTitle of the fetched work, mirrored from the resolved metadata so a caller can confirm the RIGHT paper landed in one call (#344). A ref-id placeholder when the resolver supplied no title.
Authors of the fetched work (empty when the resolver supplied none).
year: Option<i32>Publication year, when known (#344 identity confirmation).
attempts: Vec<SourceAttempt>One SourceAttempt per optional source, consulted or not (#445).
#413 attached this trace to NotFound only, so the question it
exists to answer — did anything else have this paper? — went
unanswered on the outcome where a user is most likely to ask it: an
OA copy was located at a host that then refused to serve it. “Found
nowhere” and “found at one host that refused me” have the same next
step, so they get the same trace.
Empty for an arXiv ref, which has no optional chain.
metadata_quality: Vec<String>Quality flags on the stored metadata, e.g. replacement_char:venue
for a field whose resolver value carries a U+FFFD that no enabled
source could repair (#608). Empty when the metadata is clean.
repaired_fields: BTreeMap<String, String>Fields repaired from another source, field → source key (#608).
Also recorded in the store as [doiget].repaired_fields.
Implementations§
Source§impl FetchPaperOutcome
impl FetchPaperOutcome
Sourcepub fn reported_error_code(&self) -> Option<ErrorCode>
pub fn reported_error_code(&self) -> Option<ErrorCode>
The error code this outcome reports to its caller, or None for a
clean success. A blocked PDF leg is Ok with a failed leg; its code
is the one the caller is shown, where a policy refusal (off the
allowlist, an insecure redirect, a blocklisted host) is
CAPABILITY_DENIED rather than the transport’s NETWORK_ERROR
(#145). Shared so repeat suppression reads the same answer the CLI
and MCP surfaces give (#507).
Sourcepub fn is_clean_success(&self) -> bool
pub fn is_clean_success(&self) -> bool
true when this outcome is a success with nothing withheld.
A Blocked PDF leg is an Ok outcome whose payload was refused, so
Result::is_ok alone calls it a clean success. Both front ends need
the same boundary and had drifted: the CLI special-cased Blocked
for its exit code and its SessionEnd row, the MCP server only for
the row’s error_code, so doiget_fetch_paper logged
result: "ok" beside a non-null code – a self-contradictory row for
the one outcome an agent is most likely to retry, and the outcome
#507’s repeat suppression has to be able to see.
Trait Implementations§
Source§impl Clone for FetchPaperOutcome
impl Clone for FetchPaperOutcome
Source§fn clone(&self) -> FetchPaperOutcome
fn clone(&self) -> FetchPaperOutcome
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more