#[doc(hidden)]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use crate::{Error, Result};
use alloc::{
boxed::Box,
format,
string::{String, ToString},
};
#[cfg(feature = "std")]
use dirs::{config_dir, config_local_dir, home_dir};
#[cfg(feature = "std")]
use std::{
env,
fs::File,
io::{BufRead, BufReader},
path::PathBuf,
};
#[cfg(feature = "std")]
pub fn read_config_file(path: &PathBuf) -> Result<String> {
let filename = path.to_string_lossy().to_string();
let input = read_file_without_comments(path)?;
parse_config_file(&filename, input)
}
#[cfg(feature = "std")]
fn read_file_without_comments(path: &PathBuf) -> Result<String> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut result = String::new();
for line in reader.lines() {
let line = line?.trim().to_string();
let pos = line.find("//");
if let Some(pos) = pos {
result.push_str(&line[..pos]);
} else {
result.push_str(&line);
}
}
Ok(result)
}
#[cfg(feature = "std")]
pub fn parse_config_file(filename: &String, mut input: String) -> Result<String> {
while let Some(pos) = input.find("#include ") {
let start = pos - 1;
let len = input[pos..]
.find('"')
.ok_or_else(|| Error::InvalidInclude(filename.clone()))?;
let end = 2 + start + len;
let mut load_path = PathBuf::from(&filename);
let load_file = input[start + 10..end - 1].to_string();
if load_file.starts_with('.') {
let path = load_path
.parent()
.ok_or_else(|| Error::InvalidInclude(filename.clone()))?;
load_path = path.join(load_file);
}
let content = read_file_without_comments(&load_path)?;
let content = parse_config_file(filename, content)?;
input.replace_range(start..end, &content);
}
Ok(input)
}
#[cfg(feature = "std")]
pub fn find_config_file(filename: &str) -> Result<std::path::PathBuf> {
if let Ok(cwd) = env::current_dir() {
#[cfg(not(test))]
let path = cwd.join(filename);
#[cfg(test)]
let path = cwd.join("..").join(filename);
if path.is_file() {
return Ok(path);
}
#[cfg(test)]
let path = cwd.join("../..").join(filename);
if path.is_file() {
return Ok(path);
}
let path = cwd.join(".config").join(filename);
if path.is_file() {
return Ok(path);
}
#[cfg(test)]
let path = cwd.join("../.config").join(filename);
if path.is_file() {
return Ok(path);
}
#[cfg(test)]
let path = cwd.join("../../.config").join(filename);
if path.is_file() {
return Ok(path);
}
}
for path in [home_dir(), config_local_dir(), config_dir()]
.into_iter()
.flatten()
{
let file = path.join("dimas").join(filename);
if file.is_file() {
return Ok(file);
}
}
let text = format!("file {filename} not found");
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
text,
)))
}