1use std::fs;
6use std::io::{self, Read};
7use std::path::Path;
8
9use crate::{ImportError, Imported};
10
11#[derive(Debug, thiserror::Error)]
13pub enum ReadError {
14 #[error("not a Markdown or Word file")]
16 Unsupported,
17 #[error("{0}")]
18 Io(#[from] io::Error),
19 #[error("not UTF-8 text")]
20 NotUtf8,
21 #[error("{0}")]
22 Import(#[from] ImportError),
23}
24
25pub fn read_manuscript(path: &Path) -> Result<Imported, ReadError> {
29 let ext = path
30 .extension()
31 .and_then(|e| e.to_str())
32 .map(str::to_ascii_lowercase);
33 match ext.as_deref() {
34 Some("md" | "markdown") => {
35 let bytes = fs::read(path)?;
36 let src = std::str::from_utf8(&bytes).map_err(|_| ReadError::NotUtf8)?;
37 let src = src.strip_prefix('\u{feff}').unwrap_or(src);
39 let dir = path.parent().unwrap_or(Path::new(""));
40 Ok(crate::markdown::import(src, |p| read_image(dir, p))?)
41 }
42 Some("docx") => Ok(crate::docx::import(&fs::read(path)?)?),
43 _ => Err(ReadError::Unsupported),
44 }
45}
46
47pub fn read_image(dir: &Path, path: &str) -> Option<Vec<u8>> {
51 let file = dir.join(path);
52 if !file.is_file() {
53 return None;
54 }
55 let mut bytes = Vec::new();
56 fs::File::open(file)
57 .ok()?
58 .take(crate::MAX_IMAGE_BYTES + 1)
59 .read_to_end(&mut bytes)
60 .ok()?;
61 Some(bytes)
62}