taplo_cli/
lib.rs

1use anyhow::{anyhow, Context};
2use args::GeneralArgs;
3use itertools::Itertools;
4use std::{
5    path::{Path, PathBuf},
6    str,
7    sync::Arc,
8};
9#[cfg(feature = "lint")]
10use taplo_common::schema::Schemas;
11use taplo_common::{config::Config, environment::Environment, util::Normalize};
12
13pub mod args;
14pub mod commands;
15pub mod printing;
16
17pub struct Taplo<E: Environment> {
18    env: E,
19    colors: bool,
20    #[cfg(feature = "lint")]
21    schemas: Schemas<E>,
22    config: Option<Arc<Config>>,
23}
24
25impl<E: Environment> Taplo<E> {
26    pub fn new(env: E) -> Self {
27        #[cfg(all(not(target_arch = "wasm32"), feature = "lint"))]
28        let http =
29            taplo_common::util::get_reqwest_client(std::time::Duration::from_secs(5)).unwrap();
30
31        #[cfg(all(target_arch = "wasm32", feature = "lint"))]
32        let http = reqwest::Client::default();
33
34        Self {
35            #[cfg(feature = "lint")]
36            schemas: Schemas::new(env.clone(), http),
37            colors: env.atty_stderr(),
38            config: None,
39            env,
40        }
41    }
42
43    #[tracing::instrument(skip_all)]
44    async fn load_config(&mut self, general: &GeneralArgs) -> Result<Arc<Config>, anyhow::Error> {
45        if let Some(c) = self.config.clone() {
46            return Ok(c);
47        }
48
49        let mut config_path = general.config.clone();
50
51        if config_path.is_none() && !general.no_auto_config {
52            if let Some(cwd) = self.env.cwd_normalized() {
53                config_path = self.env.find_config_file_normalized(&cwd).await
54            }
55        }
56
57        let mut config = Config::default();
58        if let Some(c) = config_path {
59            tracing::info!(path = ?c, "found configuration file");
60            match self.env.read_file(&c).await {
61                Ok(cfg) => match toml::from_str(str::from_utf8(&cfg)?) {
62                    Ok(c) => config = c,
63                    Err(error) => {
64                        tracing::warn!(%error, "invalid configuration file");
65                    }
66                },
67                Err(error) => {
68                    tracing::warn!(%error, "failed to read configuration file");
69                }
70            }
71        }
72
73        config
74            .prepare(
75                &self.env,
76                &self
77                    .env
78                    .cwd_normalized()
79                    .ok_or_else(|| anyhow!("working directory is required"))?,
80            )
81            .context("invalid configuration")?;
82
83        let c = Arc::new(config);
84
85        self.config = Some(c.clone());
86
87        Ok(c)
88    }
89
90    #[tracing::instrument(skip_all, fields(?cwd))]
91    async fn collect_files(
92        &self,
93        cwd: &Path,
94        config: &Config,
95        arg_patterns: impl Iterator<Item = String>,
96    ) -> Result<Vec<PathBuf>, anyhow::Error> {
97        let mut patterns: Vec<String> = arg_patterns
98            .map(|pat| {
99                if !self.env.is_absolute(Path::new(&pat)) {
100                    cwd.join(&pat).normalize().to_string_lossy().into_owned()
101                } else {
102                    pat
103                }
104            })
105            .collect();
106
107        if patterns.is_empty() {
108            patterns = match config.include.clone() {
109                Some(patterns) => patterns,
110                None => Vec::from([cwd
111                    .join("**/*.toml")
112                    .normalize()
113                    .to_string_lossy()
114                    .into_owned()]),
115            };
116        };
117
118        let patterns = patterns
119            .into_iter()
120            .unique()
121            .map(|p| glob::Pattern::new(&p).map(|_| p))
122            .collect::<Result<Vec<_>, _>>()?;
123
124        let files = patterns
125            .into_iter()
126            .map(|pat| self.env.glob_files_normalized(&pat))
127            .collect::<Result<Vec<_>, _>>()
128            .into_iter()
129            .flatten()
130            .flatten()
131            .collect::<Vec<_>>();
132
133        let total = files.len();
134
135        let files = files
136            .into_iter()
137            .filter(|path| config.is_included(path))
138            .collect::<Vec<_>>();
139
140        let excluded = total - files.len();
141
142        tracing::info!(total, excluded, ?files, "found files");
143
144        Ok(files)
145    }
146}
147
148pub fn default_config() -> Config {
149    Config {
150        plugins: None,
151        ..Default::default()
152    }
153}