1use crate::error::MetadataError;
11use crate::{
12 extract_and_prepare_metadata, Keywords, MetaTagGroups, MetadataMap,
13};
14use std::io::Read;
15use std::path::Path;
16
17pub fn extract_from_reader<R: Read>(
37 mut reader: R,
38) -> Result<(MetadataMap, Keywords, MetaTagGroups), MetadataError> {
39 let mut content = String::new();
40 reader.read_to_string(&mut content)?;
41 extract_from_content(&content)
42}
43
44pub fn extract_from_file<P: AsRef<Path>>(
64 path: P,
65) -> Result<(MetadataMap, Keywords, MetaTagGroups), MetadataError> {
66 extract_from_reader(std::fs::File::open(path)?)
67}
68
69pub(crate) fn extract_from_content(
71 content: &str,
72) -> Result<(MetadataMap, Keywords, MetaTagGroups), MetadataError> {
73 if content.trim().is_empty() {
74 return Ok((
75 MetadataMap::new(),
76 Vec::new(),
77 MetaTagGroups::default(),
78 ));
79 }
80 extract_and_prepare_metadata(content)
81}
82
83#[cfg(all(test, feature = "yaml"))]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn reader_round_trip_matches_string_api() {
89 let doc = "---\ntitle: T\ndate: 2024-01-02\n---\nbody";
90 let via_reader = extract_from_reader(doc.as_bytes()).unwrap();
91 let direct = extract_and_prepare_metadata(doc).unwrap();
92 assert_eq!(via_reader.0, direct.0);
93 assert_eq!(via_reader.1, direct.1);
94 assert_eq!(via_reader.2, direct.2);
95 }
96
97 #[test]
98 fn empty_reader_is_empty_metadata() {
99 let (map, keywords, tags) =
100 extract_from_reader(b" \n" as &[u8]).unwrap();
101 assert!(map.is_empty());
102 assert!(keywords.is_empty());
103 assert_eq!(tags, MetaTagGroups::default());
104 }
105
106 #[test]
107 fn invalid_utf8_is_an_io_error() {
108 let err = extract_from_reader(&[0xff, 0xfe][..]).unwrap_err();
109 assert!(matches!(err, MetadataError::IoError(_)));
110 }
111
112 #[cfg_attr(
113 miri,
114 ignore = "touches the filesystem; Miri isolation forbids it"
115 )]
116 #[test]
117 fn missing_file_is_an_io_error() {
118 let err =
119 extract_from_file("/definitely/not/here.md").unwrap_err();
120 assert!(matches!(err, MetadataError::IoError(_)));
121 }
122}