1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#[cfg(test)]
mod tests;

mod assets;
mod bin_package;
mod cli;
mod dotenvs;
mod end2end;
mod lib_package;
mod profile;
mod project;
mod style;
mod tailwind;

use std::{fmt::Debug, sync::Arc};

pub use self::cli::{Cli, Commands, Log, Opts};
use crate::ext::{
    anyhow::{Context, Result},
    MetadataExt,
};
use anyhow::bail;
use camino::{Utf8Path, Utf8PathBuf};
use cargo_metadata::Metadata;
pub use profile::Profile;
pub use project::{Project, ProjectConfig};
pub use style::StyleConfig;
pub use tailwind::TailwindConfig;

pub struct Config {
    /// absolute path to the working dir
    pub working_dir: Utf8PathBuf,
    pub projects: Vec<Arc<Project>>,
    pub cli: Opts,
    pub watch: bool,
}

impl Debug for Config {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Config")
            .field("projects", &self.projects)
            .field("cli", &self.cli)
            .field("watch", &self.watch)
            .finish_non_exhaustive()
    }
}

impl Config {
    pub fn load(cli: Opts, cwd: &Utf8Path, manifest_path: &Utf8Path, watch: bool) -> Result<Self> {
        let metadata = Metadata::load_cleaned(manifest_path)?;

        let mut projects = Project::resolve(&cli, cwd, &metadata, watch).dot()?;

        if projects.is_empty() {
            bail!("Please define glory projects in the workspace Cargo.toml sections [[workspace.metadata.glory]]")
        }

        if let Some(proj_name) = &cli.project {
            if let Some(proj) = projects.iter().find(|p| p.name == *proj_name) {
                projects = vec![proj.clone()];
            } else {
                bail!(
                    r#"The specified project "{proj_name}" not found. Available projects: {}"#,
                    names(&projects)
                )
            }
        }

        Ok(Self {
            working_dir: metadata.workspace_root,
            projects,
            cli,
            watch,
        })
    }

    #[cfg(test)]
    pub fn test_load(cli: Opts, cwd: &str, manifest_path: &str, watch: bool) -> Self {
        use crate::ext::PathBufExt;

        let manifest_path = Utf8PathBuf::from(manifest_path).canonicalize_utf8().unwrap();
        let mut cwd = Utf8PathBuf::from(cwd).canonicalize_utf8().unwrap();
        cwd.clean_windows_path();
        Self::load(cli, &cwd, &manifest_path, watch).unwrap()
    }

    pub fn current_project(&self) -> Result<Arc<Project>> {
        if self.projects.len() == 1 {
            Ok(self.projects[0].clone())
        } else {
            bail!(
                "There are several projects available ({}). Please select one of them with the command line parameter --project",
                names(&self.projects)
            );
        }
    }
}

fn names(projects: &[Arc<Project>]) -> String {
    projects.iter().map(|p| p.name.clone()).collect::<Vec<_>>().join(", ")
}