1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
// Copyright 2018-2020 Sebastian Wiesner <sebastian@swsnr.de>
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//! Access to resources referenced from markdown documents.
use std::fmt::Debug;
use std::io::{Error, ErrorKind, Result};
use mime::Mime;
use url::Url;
mod file;
mod image;
pub(crate) mod svg;
pub(crate) use self::image::InlineImageProtocol;
pub use file::FileResourceHandler;
/// Data of a resource with associated mime type.
#[derive(Debug, Clone)]
pub struct MimeData {
/// The mime type if known.
pub mime_type: Option<Mime>,
/// The data.
pub data: Vec<u8>,
}
impl MimeData {
/// Get the essence of the mime type, if any.
///
/// The essence is roughly the mime type without parameters.
pub fn mime_type_essence(&self) -> Option<&str> {
self.mime_type.as_ref().map(|m| m.essence_str())
}
}
/// Handle resource URLs.
pub trait ResourceUrlHandler: Send + Sync + Debug {
/// Read a resource.
///
/// Read data from the given `url`, and return the data and its associated mime type if known,
/// or any IO error which occurred while reading from the resource.
///
/// Alternatively, return an IO error with [`ErrorKind::Unsupported`] to indicate that the
/// given `url` is not supported by this resource handler. In this case a higher level
/// resource handler may try a different handler.
fn read_resource(&self, url: &Url) -> Result<MimeData>;
}
impl<'a, R: ResourceUrlHandler + ?Sized> ResourceUrlHandler for &'a R {
fn read_resource(&self, url: &Url) -> Result<MimeData> {
(*self).read_resource(url)
}
}
/// Filter by URL scheme.
///
/// Return `Ok(url)` if `url` has the given `scheme`, otherwise return an IO error with error kind
/// [`ErrorKind::Unsupported`].
pub fn filter_schemes<'a>(schemes: &[&str], url: &'a Url) -> Result<&'a Url> {
if schemes.contains(&url.scheme()) {
Ok(url)
} else {
Err(Error::new(
ErrorKind::Unsupported,
format!("Unsupported scheme in {url}, expected one of {schemes:?}"),
))
}
}
/// A resource handler which dispatches reading among a list of inner handlers.
#[derive(Debug)]
pub struct DispatchingResourceHandler {
/// Inner handlers.
handlers: Vec<Box<dyn ResourceUrlHandler>>,
}
impl DispatchingResourceHandler {
/// Create a new handler wrapping all given `handlers`.
pub fn new(handlers: Vec<Box<dyn ResourceUrlHandler>>) -> Self {
Self { handlers }
}
}
impl ResourceUrlHandler for DispatchingResourceHandler {
/// Read from the given resource `url`.
///
/// Try every inner handler one after another, while handlers return an
/// [`ErrorKind::Unsupported`] IO error. For any other error abort and return the error.
///
/// Return the first different result, i.e. either data read or another error.
fn read_resource(&self, url: &Url) -> Result<MimeData> {
for handler in &self.handlers {
match handler.read_resource(url) {
Ok(data) => return Ok(data),
Err(error) if error.kind() == ErrorKind::Unsupported => continue,
Err(error) => return Err(error),
}
}
Err(Error::new(
ErrorKind::Unsupported,
format!("No handler supported reading from {url}"),
))
}
}
/// A resource handler which doesn't read anything.
#[derive(Debug, Clone, Copy)]
pub struct NoopResourceHandler;
impl ResourceUrlHandler for NoopResourceHandler {
/// Always return an [`ErrorKind::Unsupported`] error.
fn read_resource(&self, url: &Url) -> Result<MimeData> {
Err(Error::new(
ErrorKind::Unsupported,
format!("Reading from resource {url} is not supported"),
))
}
}