Unmix error handling when discovering workspaces

Hitting an io::Error is a legit problem. Finding more than one
Cargo.toml is not.
This commit is contained in:
Aleksey Kladov 2020-04-16 22:19:38 +02:00
parent be2654b0ed
commit 422ae477ce
3 changed files with 41 additions and 75 deletions

View file

@ -5,9 +5,8 @@ mod json_project;
mod sysroot; mod sysroot;
use std::{ use std::{
error::Error,
fs::{read_dir, File, ReadDir}, fs::{read_dir, File, ReadDir},
io::BufReader, io::{self, BufReader},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
}; };
@ -25,25 +24,6 @@ pub use crate::{
}; };
pub use ra_proc_macro::ProcMacroClient; pub use ra_proc_macro::ProcMacroClient;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct CargoTomlNotFoundError {
pub searched_at: PathBuf,
pub reason: String,
}
impl std::fmt::Display for CargoTomlNotFoundError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
fmt,
"can't find Cargo.toml at {}, due to {}",
self.searched_at.display(),
self.reason
)
}
}
impl Error for CargoTomlNotFoundError {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ProjectWorkspace { pub enum ProjectWorkspace {
/// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
@ -94,11 +74,25 @@ impl ProjectRoot {
bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
} }
pub fn discover(path: &Path) -> Result<ProjectRoot, CargoTomlNotFoundError> { pub fn discover_single(path: &Path) -> Result<ProjectRoot> {
if let Some(project_json) = find_rust_project_json(path) { let mut candidates = ProjectRoot::discover(path)?;
return Ok(ProjectRoot::ProjectJson(project_json)); let res = match candidates.pop() {
None => bail!("no projects"),
Some(it) => it,
};
if !candidates.is_empty() {
bail!("more than one project")
} }
return find_cargo_toml(path).map(ProjectRoot::CargoToml); Ok(res)
}
pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> {
if let Some(project_json) = find_rust_project_json(path) {
return Ok(vec![ProjectRoot::ProjectJson(project_json)]);
}
return find_cargo_toml(path)
.map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect());
fn find_rust_project_json(path: &Path) -> Option<PathBuf> { fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
if path.ends_with("rust-project.json") { if path.ends_with("rust-project.json") {
@ -117,43 +111,17 @@ impl ProjectRoot {
None None
} }
fn find_cargo_toml(path: &Path) -> Result<PathBuf, CargoTomlNotFoundError> { fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
if path.ends_with("Cargo.toml") { if path.ends_with("Cargo.toml") {
return Ok(path.to_path_buf()); return Ok(vec![path.to_path_buf()]);
} }
if let Some(p) = find_cargo_toml_in_parent_dir(path) { if let Some(p) = find_cargo_toml_in_parent_dir(path) {
return Ok(p); return Ok(vec![p]);
} }
let entities = match read_dir(path) { let entities = read_dir(path)?;
Ok(entities) => entities, Ok(find_cargo_toml_in_child_dir(entities))
Err(e) => {
return Err(CargoTomlNotFoundError {
searched_at: path.to_path_buf(),
reason: format!("file system error: {}", e),
}
.into());
}
};
let mut valid_canditates = find_cargo_toml_in_child_dir(entities);
return match valid_canditates.len() {
1 => Ok(valid_canditates.remove(0)),
0 => Err(CargoTomlNotFoundError {
searched_at: path.to_path_buf(),
reason: "no Cargo.toml file found".to_string(),
}
.into()),
_ => Err(CargoTomlNotFoundError {
searched_at: path.to_path_buf(),
reason: format!(
"multiple equally valid Cargo.toml files found: {:?}",
valid_canditates
),
}
.into()),
};
} }
fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> { fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {

View file

@ -27,7 +27,7 @@ pub(crate) fn load_cargo(
load_out_dirs_from_check: bool, load_out_dirs_from_check: bool,
) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { ) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
let root = std::env::current_dir()?.join(root); let root = std::env::current_dir()?.join(root);
let root = ProjectRoot::discover(&root)?; let root = ProjectRoot::discover_single(&root)?;
let ws = ProjectWorkspace::load( let ws = ProjectWorkspace::load(
root, root,
&CargoConfig { load_out_dirs_from_check, ..Default::default() }, &CargoConfig { load_out_dirs_from_check, ..Default::default() },

View file

@ -15,6 +15,7 @@ use std::{
}; };
use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
use itertools::Itertools;
use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
use lsp_types::{ use lsp_types::{
NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams,
@ -93,27 +94,24 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
let mut visited = FxHashSet::default(); let mut visited = FxHashSet::default();
let project_roots = ws_roots let project_roots = ws_roots
.iter() .iter()
.map(|it| ra_project_model::ProjectRoot::discover(it)) .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
.filter_map(|dir| { .flatten()
dir.map_err(|cargo_toml_not_found| { .filter(|it| visited.insert(it.clone()))
log::error!("discovering workspace failed: {:?}", cargo_toml_not_found); .collect::<Vec<_>>();
if config.notifications.cargo_toml_not_found { if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
show_message( show_message(
req::MessageType::Error, req::MessageType::Error,
format!( format!(
"rust-analyzer failed to discover workspace: {:?}", "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
cargo_toml_not_found ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
), ),
&connection.sender, &connection.sender,
); );
} };
})
.ok()
})
.filter(|it| visited.insert(it.clone()));
project_roots project_roots
.into_iter()
.filter_map(|root| { .filter_map(|root| {
ra_project_model::ProjectWorkspace::load( ra_project_model::ProjectWorkspace::load(
root, root,