pub struct Doi(/* private fields */);Expand description
A Digital Object Identifier, held bare (10.3886/ICPSR36966.v1).
A newtype for one reason: a DOI is rendered by prefixing
https://doi.org/, and a record that had already stored the prefixed form
would render https://doi.org/https://doi.org/10.…. Doi::new accepts
either spelling and stores the bare one, so the prefix is applied exactly
once no matter which form was written down.
§What is stored is the identifier, not a URL
The value held here is the DOI name itself, raw: 10.1234/a#b is a DOI
whose suffix contains a literal #. It is never the percent-encoded form.
Doi::url applies that encoding on the way out, and Doi::new undoes it
when the input arrived as a resolver URL, so the two are inverses and
Doi::new(d.url()) returns d.
Stating it that way round is what keeps the type coherent. The alternative —
storing the encoded form — makes Doi::as_str not the identifier but a
fragment of a URL, and leaves no way to tell a DOI containing a literal %
from one whose % opens an escape. Recording what the thing is and
transforming at the boundary is the same rule GivenName follows.
Implementations§
Source§impl Doi
impl Doi
Sourcepub fn new(text: &str) -> Result<Self, NotADoi>
pub fn new(text: &str) -> Result<Self, NotADoi>
Parse a DOI, accepting the bare form, a doi: prefix, or a
https://doi.org/ (or http://, or dx.doi.org) resolver URL.
The shape checked is the one the DOI Handbook defines: a 10. prefix, a
registrant code of digits (possibly dot-separated, as in
10.1000.10/123), a /, and a non-empty suffix all of whose
characters are printable (is_doi_name_char). That is deliberately
stricter than “starts with 10. and contains a slash”, because
everything this type accepts is rendered as a resolver link — and a link
that resolves to nothing is exactly the plausible-looking citation this
module exists to prevent. Beyond the shape it cannot go: whether a
well-formed DOI is registered is a question only the network answers,
and this crate has no network by construction.
§Errors
Returns NotADoi when what remains after the prefix is not that shape.
Sourcepub fn url(&self) -> String
pub fn url(&self) -> String
The DOI as APA renders it: https://doi.org/10.….
The identifier is percent-encoded on the way in, because a DOI may
legitimately contain characters that mean something else inside a URL.
The sharpest is #: concatenated raw, 10.1234/a#b becomes
https://doi.org/10.1234/a#b, whose #b is an HTTP fragment and is
never sent to the resolver — so the link retrieves 10.1234/a, a
different record, silently and with every appearance of working. ?
opens a query string and does the same; a bare % invalidates the escape
sequence it looks like.
Encoded rather than refused, and the reason is evidence rather than
taste: 10.1002/(SICI)1097-0258(19970815)16:15<1707::AID-SIM605>3.0.CO;2-Y
is a real registered DOI, and <, >, (, ; and : are ordinary in
the wild. A suffix rule narrow enough to be URL-safe by construction would
refuse identifiers that exist, which is a worse failure than encoding
them. Doi::as_str still returns the DOI exactly as recorded — this is
a transport encoding applied where the transport is, the same kind of
operation as applying the resolver prefix, and for the same reason it is
applied exactly once.
What passes through unescaped is RFC 3986’s pchar (is_url_path_safe),
plus /; everything else
becomes %XX. Byte by byte, so a multi-byte character is encoded as the
bytes a URL actually carries.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Doi
impl<'de> Deserialize<'de> for Doi
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for Doi
Source§impl Ord for Doi
impl Ord for Doi
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
1.21.0 (const: unstable) · Source§fn min(self, other: Self) -> Selfwhere
Self: Sized,
fn min(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl PartialOrd for Doi
impl PartialOrd for Doi
impl StructuralPartialEq for Doi
Auto Trait Implementations§
impl Freeze for Doi
impl RefUnwindSafe for Doi
impl Send for Doi
impl Sync for Doi
impl Unpin for Doi
impl UnsafeUnpin for Doi
impl UnwindSafe for Doi
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Comparable<K> for Q
impl<Q, K> Comparable<K> for Q
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.