x_core/
lib.rs

1// Copyright (c) Aptos
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{core_config::XCoreConfig, git::GitCli};
5use camino::{Utf8Path, Utf8PathBuf};
6use debug_ignore::DebugIgnore;
7use graph::PackageGraphPlus;
8use guppy::graph::PackageGraph;
9use once_cell::sync::OnceCell;
10
11pub mod core_config;
12mod errors;
13pub mod git;
14mod graph;
15mod workspace_subset;
16
17pub use errors::*;
18pub use workspace_subset::*;
19
20/// Core context shared across all of x.
21#[derive(Debug)]
22pub struct XCoreContext {
23    project_root: &'static Utf8Path,
24    config: XCoreConfig,
25    current_dir: Utf8PathBuf,
26    current_rel_dir: Utf8PathBuf,
27    git_cli: OnceCell<GitCli>,
28    package_graph_plus: DebugIgnore<OnceCell<PackageGraphPlus>>,
29}
30
31impl XCoreContext {
32    /// Creates a new XCoreContext.
33    pub fn new(
34        project_root: &'static Utf8Path,
35        current_dir: Utf8PathBuf,
36        config: XCoreConfig,
37    ) -> Result<Self> {
38        let current_rel_dir = match current_dir.strip_prefix(project_root) {
39            Ok(rel_dir) => rel_dir.to_path_buf(),
40            Err(_) => {
41                return Err(SystemError::CwdNotInProjectRoot {
42                    current_dir,
43                    project_root,
44                })
45            }
46        };
47        // TODO: The project root should be managed by this struct, not by the global project_root
48        // function.
49        Ok(Self {
50            project_root,
51            config,
52            current_dir,
53            current_rel_dir,
54            git_cli: OnceCell::new(),
55            package_graph_plus: DebugIgnore(OnceCell::new()),
56        })
57    }
58
59    /// Returns the project root for this workspace.
60    pub fn project_root(&self) -> &'static Utf8Path {
61        self.project_root
62    }
63
64    /// Returns the core config.
65    pub fn config(&self) -> &XCoreConfig {
66        &self.config
67    }
68
69    /// Returns the current working directory for this process.
70    pub fn current_dir(&self) -> &Utf8Path {
71        &self.current_dir
72    }
73
74    /// Returns the current working directory for this process, relative to the project root.
75    pub fn current_rel_dir(&self) -> &Utf8Path {
76        &self.current_rel_dir
77    }
78
79    /// Returns true if x has been run from the project root.
80    pub fn current_dir_is_root(&self) -> bool {
81        self.current_rel_dir == ""
82    }
83
84    /// Returns the Git CLI for this workspace.
85    pub fn git_cli(&self) -> Result<&GitCli> {
86        let root = self.project_root;
87        self.git_cli.get_or_try_init(|| GitCli::new(root))
88    }
89
90    /// Returns the package graph for this workspace.
91    pub fn package_graph(&self) -> Result<&PackageGraph> {
92        Ok(self.package_graph_plus()?.package_graph())
93    }
94
95    /// For a given list of workspace packages, returns a tuple of (known, unknown) packages.
96    ///
97    /// Initializes the package graph if it isn't already done so, and returns an error if the
98    pub fn partition_workspace_names<'a, B>(
99        &self,
100        names: impl IntoIterator<Item = &'a str>,
101    ) -> Result<(B, B)>
102    where
103        B: Default + Extend<&'a str>,
104    {
105        let workspace = self.package_graph()?.workspace();
106        let (known, unknown) = names
107            .into_iter()
108            .partition(|name| workspace.contains_name(name));
109        Ok((known, unknown))
110    }
111
112    /// Returns information about the subsets for this workspace.
113    pub fn subsets(&self) -> Result<&WorkspaceSubsets> {
114        Ok(self.package_graph_plus()?.subsets())
115    }
116
117    // ---
118    // Helper methods
119    // ---
120
121    fn package_graph_plus(&self) -> Result<&PackageGraphPlus> {
122        self.package_graph_plus
123            .get_or_try_init(|| PackageGraphPlus::create(self))
124    }
125}