Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/project-model/src/project_json.rs')
-rw-r--r--crates/project-model/src/project_json.rs180
1 files changed, 177 insertions, 3 deletions
diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs
index 5bee446f61..4a916e570b 100644
--- a/crates/project-model/src/project_json.rs
+++ b/crates/project-model/src/project_json.rs
@@ -33,7 +33,7 @@
//!
//! * file on disk
//! * a field in the config (ie, you can send a JSON request with the contents
-//! of rust-project.json to rust-analyzer, no need to write anything to disk)
+//! of `rust-project.json` to rust-analyzer, no need to write anything to disk)
//!
//! Another possible thing we don't do today, but which would be totally valid,
//! is to add an extension point to VS Code extension to register custom
@@ -55,8 +55,7 @@ use rustc_hash::FxHashMap;
use serde::{de, Deserialize, Serialize};
use span::Edition;
-use crate::cfg::CfgFlag;
-use crate::ManifestPath;
+use crate::{cfg::CfgFlag, ManifestPath, TargetKind};
/// Roots and crates that compose this Rust project.
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -68,6 +67,10 @@ pub struct ProjectJson {
project_root: AbsPathBuf,
manifest: Option<ManifestPath>,
crates: Vec<Crate>,
+ /// Configuration for CLI commands.
+ ///
+ /// Examples include a check build or a test run.
+ runnables: Vec<Runnable>,
}
/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
@@ -88,6 +91,86 @@ pub struct Crate {
pub(crate) exclude: Vec<AbsPathBuf>,
pub(crate) is_proc_macro: bool,
pub(crate) repository: Option<String>,
+ pub build: Option<Build>,
+}
+
+/// Additional, build-specific data about a crate.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Build {
+ /// The name associated with this crate.
+ ///
+ /// This is determined by the build system that produced
+ /// the `rust-project.json` in question. For instance, if buck were used,
+ /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`.
+ ///
+ /// Do not attempt to parse the contents of this string; it is a build system-specific
+ /// identifier similar to [`Crate::display_name`].
+ pub label: String,
+ /// Path corresponding to the build system-specific file defining the crate.
+ ///
+ /// It is roughly analogous to [`ManifestPath`], but it should *not* be used with
+ /// [`crate::ProjectManifest::from_manifest_file`], as the build file may not be
+ /// be in the `rust-project.json`.
+ pub build_file: Utf8PathBuf,
+ /// The kind of target.
+ ///
+ /// Examples (non-exhaustively) include [`TargetKind::Bin`], [`TargetKind::Lib`],
+ /// and [`TargetKind::Test`]. This information is used to determine what sort
+ /// of runnable codelens to provide, if any.
+ pub target_kind: TargetKind,
+}
+
+/// A template-like structure for describing runnables.
+///
+/// These are used for running and debugging binaries and tests without encoding
+/// build system-specific knowledge into rust-analyzer.
+///
+/// # Example
+///
+/// Below is an example of a test runnable. `{label}` and `{test_id}`
+/// are explained in [`Runnable::args`]'s documentation.
+///
+/// ```json
+/// {
+/// "program": "buck",
+/// "args": [
+/// "test",
+/// "{label}",
+/// "--",
+/// "{test_id}",
+/// "--print-passing-details"
+/// ],
+/// "cwd": "/home/user/repo-root/",
+/// "kind": "testOne"
+/// }
+/// ```
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Runnable {
+ /// The program invoked by the runnable.
+ ///
+ /// For example, this might be `cargo`, `buck`, or `bazel`.
+ pub program: String,
+ /// The arguments passed to [`Runnable::program`].
+ ///
+ /// The args can contain two template strings: `{label}` and `{test_id}`.
+ /// rust-analyzer will find and replace `{label}` with [`Build::label`] and
+ /// `{test_id}` with the test name.
+ pub args: Vec<String>,
+ /// The current working directory of the runnable.
+ pub cwd: Utf8PathBuf,
+ pub kind: RunnableKind,
+}
+
+/// The kind of runnable.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum RunnableKind {
+ Check,
+
+ /// Can run a binary.
+ Run,
+
+ /// Run a single test.
+ TestOne,
}
impl ProjectJson {
@@ -95,6 +178,7 @@ impl ProjectJson {
///
/// # Arguments
///
+ /// * `manifest` - The path to the `rust-project.json`.
/// * `base` - The path to the workspace root (i.e. the folder containing `rust-project.json`)
/// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via
/// configuration.
@@ -109,6 +193,7 @@ impl ProjectJson {
sysroot_src: data.sysroot_src.map(absolutize_on_base),
project_root: base.to_path_buf(),
manifest,
+ runnables: data.runnables.into_iter().map(Runnable::from).collect(),
crates: data
.crates
.into_iter()
@@ -127,6 +212,15 @@ impl ProjectJson {
None => (vec![root_module.parent().unwrap().to_path_buf()], Vec::new()),
};
+ let build = match crate_data.build {
+ Some(build) => Some(Build {
+ label: build.label,
+ build_file: build.build_file,
+ target_kind: build.target_kind.into(),
+ }),
+ None => None,
+ };
+
Crate {
display_name: crate_data
.display_name
@@ -146,6 +240,7 @@ impl ProjectJson {
exclude,
is_proc_macro: crate_data.is_proc_macro,
repository: crate_data.repository,
+ build,
}
})
.collect(),
@@ -167,10 +262,27 @@ impl ProjectJson {
&self.project_root
}
+ pub fn crate_by_root(&self, root: &AbsPath) -> Option<Crate> {
+ self.crates
+ .iter()
+ .filter(|krate| krate.is_workspace_member)
+ .find(|krate| krate.root_module == root)
+ .cloned()
+ }
+
+ /// Returns the path to the project's manifest, if it exists.
+ pub fn manifest(&self) -> Option<&ManifestPath> {
+ self.manifest.as_ref()
+ }
+
/// Returns the path to the project's manifest or root folder, if no manifest exists.
pub fn manifest_or_root(&self) -> &AbsPath {
self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref())
}
+
+ pub fn runnables(&self) -> &[Runnable] {
+ &self.runnables
+ }
}
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -178,6 +290,8 @@ pub struct ProjectJsonData {
sysroot: Option<Utf8PathBuf>,
sysroot_src: Option<Utf8PathBuf>,
crates: Vec<CrateData>,
+ #[serde(default)]
+ runnables: Vec<RunnableData>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -200,6 +314,8 @@ struct CrateData {
is_proc_macro: bool,
#[serde(default)]
repository: Option<String>,
+ #[serde(default)]
+ build: Option<BuildData>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -215,6 +331,48 @@ enum EditionData {
Edition2024,
}
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct BuildData {
+ label: String,
+ build_file: Utf8PathBuf,
+ target_kind: TargetKindData,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct RunnableData {
+ pub program: String,
+ pub args: Vec<String>,
+ pub cwd: Utf8PathBuf,
+ pub kind: RunnableKindData,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum RunnableKindData {
+ Check,
+ Run,
+ TestOne,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum TargetKindData {
+ Bin,
+ /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
+ Lib,
+ Test,
+}
+
+impl From<TargetKindData> for TargetKind {
+ fn from(data: TargetKindData) -> Self {
+ match data {
+ TargetKindData::Bin => TargetKind::Bin,
+ TargetKindData::Lib => TargetKind::Lib { is_proc_macro: false },
+ TargetKindData::Test => TargetKind::Test,
+ }
+ }
+}
+
impl From<EditionData> for Edition {
fn from(data: EditionData) -> Self {
match data {
@@ -226,6 +384,22 @@ impl From<EditionData> for Edition {
}
}
+impl From<RunnableData> for Runnable {
+ fn from(data: RunnableData) -> Self {
+ Runnable { program: data.program, args: data.args, cwd: data.cwd, kind: data.kind.into() }
+ }
+}
+
+impl From<RunnableKindData> for RunnableKind {
+ fn from(data: RunnableKindData) -> Self {
+ match data {
+ RunnableKindData::Check => RunnableKind::Check,
+ RunnableKindData::Run => RunnableKind::Run,
+ RunnableKindData::TestOne => RunnableKind::TestOne,
+ }
+ }
+}
+
/// Identifies a crate by position in the crates array.
///
/// This will differ from `CrateId` when multiple `ProjectJson`