1use std::path::{Path, PathBuf};
2
3use crate::{ConfigParser, Error, ParseError, Properties, PropertiesSource, Section};
4
5pub struct ConfigFile {
7 pub path: PathBuf,
10 pub reader: ConfigParser<std::io::BufReader<std::fs::File>>,
12}
13
14impl ConfigFile {
15 pub fn open(path: impl Into<PathBuf>) -> Result<ConfigFile, ParseError> {
19 let path = path.into();
20 let file = std::fs::File::open(&path).map_err(ParseError::Io)?;
21 let reader = ConfigParser::new_buffered_with_path(file, Some(path.as_ref()))?;
22 Ok(ConfigFile { path, reader })
23 }
24
25 pub fn add_error_context(&self, error: ParseError) -> Error {
29 Error::InFile(self.path.clone(), self.reader.line_no(), error)
30 }
31}
32
33impl Iterator for ConfigFile {
34 type Item = Result<Section, ParseError>;
35 fn next(&mut self) -> Option<Self::Item> {
36 self.reader.next()
37 }
38}
39
40impl std::iter::FusedIterator for ConfigFile {}
41
42impl PropertiesSource for &mut ConfigFile {
43 fn apply_to(self, props: &mut Properties, path: impl AsRef<Path>) -> Result<(), crate::Error> {
48 let get_parent = || self.path.parent();
49 let path = if let Some(parent) = get_parent() {
50 let path = path.as_ref();
51 path.strip_prefix(parent).unwrap_or(path)
52 } else {
53 path.as_ref()
54 };
55 match self.reader.apply_to(props, path) {
56 Ok(()) => Ok(()),
57 Err(crate::Error::Parse(e)) => Err(self.add_error_context(e)),
58 Err(e) => panic!("unexpected error variant {:?}", e),
59 }
60 }
61}
62
63pub struct ConfigFiles(Vec<ConfigFile>);
70
71impl ConfigFiles {
72 #[allow(clippy::needless_pass_by_value)]
80 pub fn open(
81 path: impl AsRef<Path>,
82 config_path_override: Option<impl AsRef<std::path::Path>>,
83 ) -> Result<ConfigFiles, Error> {
84 use std::borrow::Cow;
85 let filename = config_path_override
86 .as_ref()
87 .map_or_else(|| ".editorconfig".as_ref(), |f| f.as_ref());
88 Ok(ConfigFiles(if filename.is_relative() {
89 let mut abs_path = Cow::from(path.as_ref());
90 if abs_path.is_relative() {
91 abs_path = std::env::current_dir()
92 .map_err(Error::InvalidCwd)?
93 .join(&path)
94 .into()
95 }
96 let mut path = abs_path.as_ref();
97 let mut vec = Vec::new();
98 while let Some(dir) = path.parent() {
99 if let Ok(file) = ConfigFile::open(dir.join(filename)) {
100 let should_break = file.reader.is_root;
101 vec.push(file);
102 if should_break {
103 break;
104 }
105 }
106 path = dir;
107 }
108 vec
109 } else {
110 vec![ConfigFile::open(filename).map_err(Error::Parse)?]
112 }))
113 }
114
115 pub fn iter(&self) -> impl Iterator<Item = &ConfigFile> {
117 self.0.iter().rev()
118 }
119
120 }
123
124impl Iterator for ConfigFiles {
125 type Item = ConfigFile;
126 fn next(&mut self) -> Option<ConfigFile> {
127 self.0.pop()
128 }
129}
130
131impl std::iter::FusedIterator for ConfigFiles {}
132
133impl PropertiesSource for ConfigFiles {
134 fn apply_to(self, props: &mut Properties, path: impl AsRef<Path>) -> Result<(), crate::Error> {
138 let path = path.as_ref();
139 for mut file in self {
140 file.apply_to(props, path)?;
141 }
142 Ok(())
143 }
144}