Skip to main content

FetchPaperOutcome

Struct FetchPaperOutcome 

Source
#[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
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§source: String

Source::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: String

Resolver 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: String

OA 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: Utf8PathBuf

Absolute path of the artifact actually written (<root>/<safekey>.pdf on success, <root>/.metadata/<safekey>.toml on metadata-only fallback).

§size_bytes: u64

Stored PDF size in bytes; 0 on the metadata-only fallback (docs/REDIRECT_ALLOWLIST.md §3.5).

§schema_version: String

The schema version of the metadata TOML written (always crate::SCHEMA_VERSION for this build).

§pdf_leg: PdfLegStatus

What 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: String

Per-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: String

ADR-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: String

Title 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: Vec<String>

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

Source

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).

Source

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

Source§

fn clone(&self) -> FetchPaperOutcome

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FetchPaperOutcome

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more