use anyhow::{anyhow, Context, Result}; use regex::Regex; use std::{path::PathBuf, pin::Pin}; use tokio::pin; use futures_util::stream::StreamExt; use crate::common::{read_file_by_lines, AppConfig, VarsMap}; pub(crate) struct VarsFile { pub(crate) filepath: PathBuf, pub(crate) vars: Option, pub(crate) encoding: String, } impl VarsFile { async fn from_file(filepath: &PathBuf, encoding: String) -> Result { let metadata = tokio::fs::metadata(&filepath).await.context(format!( "file not found {}", filepath.to_str().expect("str") ))?; if !metadata.is_file() { Err(anyhow!("{} is not a file", filepath.to_str().expect("str")))? } Ok(VarsFile { filepath: filepath.to_path_buf(), vars: None, encoding, }) } async fn from_dir(dir: PathBuf, encoding: String) -> Result { let filepath = dir.join("vars"); let err_context = format!( "vars or vars.bat file not found in {}", dir.to_str().expect("str") ); match Self::from_file(&filepath, encoding.clone()).await { Ok(res) => Ok(res), Err(_) => Self::from_file(&filepath.with_extension("bat"), encoding.clone()) .await .map_err(|e| e.context(err_context)), } } pub(crate) async fn from_config(config: &AppConfig) -> Result { Self::from_dir( PathBuf::from(&config.base_directory), config.encoding.clone(), ) .await } pub(crate) async fn parse(&mut self) -> Result<()> { let mut result = VarsMap::new(); let lines = read_file_by_lines(&self.filepath, &self.encoding).await?; let lines = Pin::from(lines); pin!(lines); let re_v2 = Regex::new(r#"^(export|set)\s\b(?P[\w\d_]+)\b=\s?"?(?P[^\#]+?)"?$"#) .context("regex v2")?; let re_v3 = Regex::new(r"^set_var\s(?P[\w\d_]+)\s+(?P[^\#]+?)$") .context("regex v3")?; while let Some(line) = lines.next().await { if let Some(caps) = re_v2.captures(line.as_str()) { result.insert(caps["key"].to_string(), caps["value"].to_string()); continue; } if let Some(caps) = re_v3.captures(line.as_str()) { result.insert(caps["key"].to_string(), caps["value"].to_string()); }; } self.vars = Some(result); Ok(()) } #[allow(dead_code)] fn apply(&self) -> Result<()> { if let Some(vars) = self.vars.clone() { for (key, value) in vars.iter() { unsafe { std::env::set_var(key, value); } } } else { Err(anyhow!("vars not parsed"))? } Ok(()) } }