[rdct] launcher: anyhow for error handling

GitOrigin-RevId: b7e0fb01782f332ee7811c5cee1cfe62ffc85d9a
This commit is contained in:
Ivan Pashchenko
2022-08-29 20:27:27 +02:00
committed by intellij-monorepo-bot
parent 0b6c0bfb90
commit 838765c341
12 changed files with 238 additions and 273 deletions

View File

@@ -4,7 +4,9 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/utils/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/utils/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

View File

@@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
@@ -31,6 +40,9 @@ name = "anyhow"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
dependencies = [
"backtrace",
]
[[package]]
name = "arrayvec"
@@ -74,6 +86,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.0"
@@ -997,6 +1024,12 @@ dependencies = [
"wasi",
]
[[package]]
name = "gimli"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
[[package]]
name = "git2"
version = "0.14.4"
@@ -1639,6 +1672,15 @@ dependencies = [
"objc",
]
[[package]]
name = "object"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.13.1"
@@ -2121,6 +2163,12 @@ dependencies = [
"serde",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustc-hash"
version = "1.1.0"
@@ -2710,6 +2758,14 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
[[package]]
name = "utils"
version = "0.1.0"
dependencies = [
"anyhow",
"log",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -2998,7 +3054,7 @@ dependencies = [
"serde",
"serde_json",
"simplelog",
"thiserror",
"utils",
]
[[package]]

View File

@@ -25,11 +25,12 @@ serde = { version="1.0.136", features = ["derive"] }
serde_json = "1.0.79"
is_executable = "1.0.1"
simplelog = "0.12.0"
thiserror = "1.0.30"
jni = { version="0.19.0" }
libloading = "0.7.3"
jni-sys = "0.3.0"
path-absolutize = "3.0.13"
utils = { path = "utils" }
anyhow = { version = "1.0.62", features = ["std", "backtrace"] }
[dev-dependencies]
# external CLI tools, specified here so that they would be added to Cargo.lock organically
@@ -41,4 +42,4 @@ fs_extra = "1.2.0"
anyhow = "1.0.62"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9.3"
core-foundation = "0.9.3"

View File

@@ -4,9 +4,9 @@ use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use log::{debug, warn};
use crate::{canonical_non_unc, err_from_string, LaunchConfiguration, LauncherError, ProductInfo};
use crate::errors::Result;
use crate::utils::{get_path_from_env_var, get_readable_file_from_env_var, is_readable, PathExt, read_file_to_end};
use anyhow::{bail, Result};
use utils::{canonical_non_unc, get_path_from_env_var, get_readable_file_from_env_var, is_readable, PathExt, read_file_to_end};
use crate::{LaunchConfiguration, ProductInfo};
pub struct DefaultLaunchConfiguration {
pub product_info: ProductInfo,
@@ -64,15 +64,12 @@ impl LaunchConfiguration for DefaultLaunchConfiguration {
let item_canonical_path = match canonical_non_unc(&item_path) {
Ok(x) => { x }
Err(e) => {
match e {
LauncherError::IoError(e) => {
match e.is::<std::io::Error>() {
true => {
warn!("{item_path:?}: IoError {e:?} when trying to get canonical path");
continue
}
e => {
let message = format!("Failed to get canonical non-UNC path for {item_path:?} {e:?}");
return err_from_string(message);
}
false => bail!("Failed to get canonical non-UNC path for {item_path:?} {e:?}"),
}
}
};
@@ -214,8 +211,7 @@ impl DefaultLaunchConfiguration {
};
if !default_java_output.status.success() {
let message = format!("'command -v java' didn't succeed, exit code: {exit_code}, stdout: {stdout}, stderr: {stderr}");
return err_from_string(message)
bail!("'command -v java' didn't succeed, exit code: {exit_code}, stdout: {stdout}, stderr: {stderr}");
}
// TODO: check if it's executable? (will be a behaviour change)
@@ -252,16 +248,16 @@ impl DefaultLaunchConfiguration {
let jbr_dir = self.ide_home.join("jbr");
if !jbr_dir.is_dir() {
return err_from_string(format!("{jbr_dir:?} is not a directory"))
bail!("{jbr_dir:?} is not a directory");
};
// TODO: non-mac
let java_executable = get_bin_java_path(&jbr_dir);
match is_executable::is_executable(&java_executable) {
true => { Ok(java_executable) }
true => Ok(java_executable),
// TODO: check if exists, separate method
false => { err_from_string(format!("{java_executable:?} is not an executable")) }
false => bail!("{java_executable:?} is not an executable")
}
}
@@ -289,8 +285,7 @@ impl DefaultLaunchConfiguration {
let metadata = jre_path_file.metadata()?;
if metadata.len() == 0 {
let message = format!("vmoptions file by path {jre_path_file:?} has zero length, will not try to resolve runtime from it");
return err_from_string(message);
bail!("vmoptions file by path {jre_path_file:?} has zero length, will not try to resolve runtime from it");
}
let content = read_file_to_end(jre_path_file.as_path())?;
@@ -303,8 +298,7 @@ impl DefaultLaunchConfiguration {
match is_executable::is_executable(&java_executable) {
true => { Ok(java_executable) }
false => {
let message = format!("{java_executable:?} specified in {jre_path_file:?} is not a valid executable");
err_from_string(message)
bail!("{java_executable:?} specified in {jre_path_file:?} is not a valid executable");
}
}
}
@@ -316,22 +310,19 @@ impl DefaultLaunchConfiguration {
let env_var_value = env::var(env_var_name)?;
if !env_var_value.is_empty() {
let message = format!("Env var {env_var_value} is not set, skipping JDK detection from it");
return err_from_string(message);
bail!("Env var {env_var_value} is not set, skipping JDK detection from it");
}
let product_jdk_dir = Path::new(env_var_value.as_str());
let java_executable = product_jdk_dir.join("bin").join("java");
if !java_executable.exists() {
let message = format!("Java executable from JDK {java_executable:?} does not exist");
return err_from_string(message);
bail!("Java executable from JDK {java_executable:?} does not exist");
}
// TODO: write the same code ourselves instead of using is_executable crate?
if !is_executable::is_executable(&java_executable) {
let message = format!("{java_executable:?} is not an executable file");
return err_from_string(message);
bail!("{java_executable:?} is not an executable file");
}
return Ok(java_executable);
@@ -384,9 +375,7 @@ impl DefaultLaunchConfiguration {
let user_vm_options_error = &errors[0];
let vm_options_error = &errors[1];
let message = format!(
"Failed to resolve any vmoptions files, user_vm_options: {user_vm_options_error:?}, vm_options: {vm_options_error:?}");
return err_from_string(message);
bail!("Failed to resolve any vmoptions files, user_vm_options: {user_vm_options_error:?}, vm_options: {vm_options_error:?}");
}
return Ok([vm_options, user_vm_options].concat());
@@ -397,14 +386,11 @@ impl DefaultLaunchConfiguration {
// TODO: there is a relative path in product-info json (launch), maybe use that?
let vm_options_file_name = vm_options_base_file_name +
match env::consts::OS {
"linux" => { "64.vmoptions" }
"macos" => { ".vmoptions" }
"linux" => "64.vmoptions",
"macos" => ".vmoptions",
//TODO: check if that's actual for Windows
"windows" => { "64.exe.vmoptions" }
unsupported_os => {
let message = format!("Unsupported OS: {unsupported_os}");
return err_from_string(message);
}
"windows" => "64.exe.vmoptions",
unsupported_os => bail!("Unsupported OS: {unsupported_os}"),
};
Ok(vm_options_file_name)
@@ -466,14 +452,11 @@ impl DefaultLaunchConfiguration {
}
let os_specific_dir = match env::consts::OS {
"linux" => { "linux" }
"macos" => { "mac" }
"linux" => "linux",
"macos" => "mac",
//TODO: check if that's actual for Windows
"windows" => { "windows" }
unsupported_os => {
let message = format!("Unsupported OS: {unsupported_os}");
return err_from_string(message);
}
"windows" => "windows",
unsupported_os => bail!("Unsupported OS: {unsupported_os}"),
};
let os_specific_vm_options = self.ide_bin.join(os_specific_dir).join(vm_options_file_name);
@@ -535,14 +518,11 @@ fn get_lib_path(ide_home: &Path) -> PathBuf {
fn get_product_info_home(ide_home: &Path) -> Result<PathBuf> {
let parent = match env::consts::OS {
"linux" => { ide_home.to_path_buf() }
"macos" => { ide_home.join("Resources") }
"linux" => ide_home.to_path_buf(),
"macos" => ide_home.join("Resources"),
//TODO: check if that's actual for Windows
"windows" => { ide_home.to_path_buf() }
unsupported_os => {
let message = format!("Unsupported OS: {unsupported_os}");
return err_from_string(message);
}
"windows" => ide_home.to_path_buf(),
unsupported_os => bail!("Unsupported OS: {unsupported_os}"),
};
Ok(parent)

View File

@@ -1,77 +0,0 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
use std::ffi::NulError;
use std::fmt::{Display, Formatter};
use log::error;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, LauncherError>;
#[derive(Error, Debug)]
pub enum LauncherError {
#[error("Error {0}")]
HumanReadableError(#[from] HumanReadableError),
#[error("I/O error {0}")]
IoError(#[from] std::io::Error),
#[error("Var error {0}")]
VarError(#[from] std::env::VarError),
#[error("JSON error {0}")]
SerdeError(#[from] serde_json::Error),
#[error("Libloading error {0}")]
LibloadingError(#[from] libloading::Error),
#[error("JNI error {0}")]
JniError(#[from] JniError),
#[error("Nul error {0}")]
NulError(#[from] NulError),
#[error("jni-rs error {0}")]
JniRsError(#[from] jni::errors::Error),
#[error("SetLoggerError error {0}")]
LogSetLoggerError(#[from] log::SetLoggerError),
}
pub fn err_from_string<T,S: AsRef<str>>(message: S) -> Result<T> {
let error = HumanReadableError { message: message.as_ref().to_string() };
return Err(LauncherError::HumanReadableError(error));
}
#[derive(Error, Debug)]
pub struct HumanReadableError {
pub message: String,
}
#[derive(Error, Debug)]
pub struct JniError {
pub message: String
}
impl JniError {
fn err_from_string(message: &str) -> Result<()> {
let error = JniError { message: message.to_string() };
return Err(LauncherError::JniError(error));
}
pub fn check_result(error_code: jni_sys::jint) -> Result<()> {
match error_code {
jni_sys::JNI_OK => Ok(()),
jni_sys::JNI_ERR => JniError::err_from_string("JNI_ERR: unknown error"),
jni_sys::JNI_EDETACHED => JniError::err_from_string("JNI_EDETACHED: thread is not attached to JVM"),
jni_sys::JNI_EVERSION => JniError::err_from_string("JNI_EVERSION: wrong JNI version"),
jni_sys::JNI_ENOMEM => JniError::err_from_string("JNI_ENOMEM: no enought memory"),
jni_sys::JNI_EEXIST => JniError::err_from_string("JNI_EEXIST: JVM already exists"),
jni_sys::JNI_EINVAL => JniError::err_from_string("JNI_EINVAL? invalid arguments"),
i => JniError::err_from_string(format!("Other: {i}").as_str()),
}
}
}
impl Display for JniError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl Display for HumanReadableError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}

View File

@@ -5,8 +5,7 @@ use std::path::{Path, PathBuf};
use jni::errors::Error;
use jni::objects::{JObject, JValue};
use log::{debug, error, info};
use crate::{err_from_string, errors};
use crate::errors::{LauncherError, Result};
use anyhow::{bail, Context, Result};
#[cfg(target_os = "linux")] use {
std::thread::sleep,
@@ -111,7 +110,16 @@ unsafe fn prepare_jni_env(
};
debug!("Create VM result={create_jvm_result}");
errors::JniError::check_result(create_jvm_result)?;
match create_jvm_result {
jni_sys::JNI_OK => { }
jni_sys::JNI_ERR => bail!("JNI_ERR: unknown error"),
jni_sys::JNI_EDETACHED => bail!("JNI_EDETACHED: thread is not attached to JVM"),
jni_sys::JNI_EVERSION => bail!("JNI_EVERSION: wrong JNI version"),
jni_sys::JNI_ENOMEM => bail!("JNI_ENOMEM: no enought memory"),
jni_sys::JNI_EEXIST => bail!("JNI_EEXIST: JVM already exists"),
jni_sys::JNI_EINVAL => bail!("JNI_EINVAL? invalid arguments"),
i => bail!("Other: {i}"),
}
let jni_env = unsafe {
jni::JNIEnv::from_raw(jni_env)?
@@ -139,13 +147,13 @@ pub fn call_intellij_main(jni_env: jni::JNIEnv<'_>, args: Vec<String>) -> Result
match jni_env.call_static_method("com/intellij/idea/Main", "main", "([Ljava/lang/String;)V", &method_call_args) {
Ok(_) => {}
Err(e) => {
return match e {
match e {
Error::JavaException => {
jni_env.exception_describe()?;
Err(LauncherError::JniRsError(e))
Err(e)
}
_ => Err(LauncherError::JniRsError(e))
}
_ => Err(e)
}?;
}
};
@@ -179,10 +187,7 @@ unsafe fn load_libjvm(libjvm_path: PathBuf) -> Result<libloading::Library> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
unsafe fn load_libjvm(libjvm_path: PathBuf) -> Result<libloading::Library> {
match unsafe { libloading::Library::new(libjvm_path.as_os_str()) } {
Ok(l) => { Ok(l)}
Err(e) => { Err(LauncherError::LibloadingError(e))}
}
unsafe { libloading::Library::new(libjvm_path.as_os_str()) }.context("Failed to load libjvm")
}
fn get_jvm_init_args(vm_options: Vec<String>) -> Result<jni_sys::JavaVMInitArgs> {
@@ -209,8 +214,7 @@ fn get_libjvm(java_home: &Path) -> Result<PathBuf> {
let libjvm = get_libjvm_path(java_home);
if !libjvm.exists() {
let message = format!("libvjm not found at path {libjvm:?}");
return err_from_string(message);
bail!("libvjm not found at path {libjvm:?}");
}
debug!("Found libjvm at {libjvm:?}");

View File

@@ -30,9 +30,7 @@ unused_results,
variant_size_differences
)]
mod errors;
mod java;
mod utils;
mod remote_dev;
mod default;
@@ -44,9 +42,8 @@ use log::{debug, error, LevelFilter};
use native_dialog::{MessageDialog, MessageType};
use simplelog::{ColorChoice, CombinedLogger, Config, TerminalMode, TermLogger, WriteLogger};
use crate::default::DefaultLaunchConfiguration;
use crate::errors::{err_from_string, LauncherError, Result};
use anyhow::{bail, Result};
use crate::remote_dev::RemoteDevLaunchConfiguration;
use crate::utils::canonical_non_unc;
pub fn main_lib() {
let main_result = main_impl();
@@ -125,10 +122,7 @@ impl ProductInfo {
self.launch.iter().find(|&l| l.os.to_lowercase() == current_os);
match os_specific_launch_field {
None => {
let message = format!("Could not find current os {current_os} launch element in product-info.json 'launch' field");
err_from_string(message)
}
None => bail!("Could not find current os {current_os} launch element in product-info.json 'launch' field"),
Some(x) => Ok(x),
}
}
@@ -181,22 +175,12 @@ fn unwrap_with_human_readable_error<S>(result: Result<S>) -> S {
match result {
Ok(x) => { x }
Err(e) => {
let message =
match e {
LauncherError::HumanReadableError(e) => {
e.message
}
_ => {
format!("{e:?}")
}
};
eprintln!("{}", message);
error!("{}", message);
eprintln!("{e:?}");
error!("{e:?}");
match env::var(DO_NOT_SHOW_ERROR_UI_ENV_VAR) {
Ok(_) => { }
Err(_) => { show_fail_to_start_message("Failed to start", format!("{message:?}").as_str()) }
Err(_) => { show_fail_to_start_message("Failed to start", format!("{e:?}").as_str()) }
}
std::process::exit(1);

View File

@@ -5,10 +5,10 @@ use std::io::{BufWriter, Write};
use std::path::PathBuf;
use log::{debug, info};
use path_absolutize::Absolutize;
use crate::errors::Result;
use crate::{DefaultLaunchConfiguration, err_from_string, LaunchConfiguration};
use anyhow::{bail, Context, Result};
use crate::default::get_config_home;
use crate::utils::{get_path_from_env_var, PathExt, read_file_to_end};
use utils::{get_path_from_env_var, PathExt, read_file_to_end};
use crate::{DefaultLaunchConfiguration, LaunchConfiguration};
pub struct RemoteDevLaunchConfiguration {
default: DefaultLaunchConfiguration,
@@ -117,7 +117,7 @@ impl DefaultLaunchConfiguration {
impl RemoteDevLaunchConfiguration {
pub fn parse_remote_dev_args(args: &[String]) -> Result<RemoteDevArgs> {
if args.is_empty() {
return err_from_string("Starter command is not specified")
bail!("Starter command is not specified")
}
let remote_dev_starter_command = args[0].as_str();
@@ -130,13 +130,13 @@ impl RemoteDevLaunchConfiguration {
"status" => { "cwmHostStatus" }
x => {
print_help();
return err_from_string(format!("Unknown command: {x}"))
bail!("Unknown command: {x}")
}
};
if args.len() < 1 {
print_help();
return err_from_string("Project path is not specified");
bail!("Project path is not specified");
}
let project_path_string = args[1].as_str();
@@ -156,7 +156,7 @@ impl RemoteDevLaunchConfiguration {
let project_path = PathBuf::from(project_path_string);
if !project_path.exists() {
print_help();
return err_from_string(format!("Project path does not exist: {project_path_string}"));
bail!("Project path does not exist: {project_path_string}");
}
// if [ -d "$PROJECT_PATH" ]; then
@@ -196,13 +196,9 @@ impl RemoteDevLaunchConfiguration {
}
pub fn new(project_path: PathBuf, default: DefaultLaunchConfiguration) -> Result<Self> {
let per_project_config_dir_name = match project_path.file_name() {
None => {
let message = format!("Failed to get project dir name, project path: {project_path:?}");
err_from_string(message)
}
Some(x) => Ok(x)
}?.to_string_lossy();
let per_project_config_dir_name = project_path.file_name()
.context("Failed to get project dir name, project path: {project_path:?}")
?.to_string_lossy();
let config_dir = default.prepare_host_config_dir(&per_project_config_dir_name)?;
let system_dir = default.prepare_system_config_dir(&per_project_config_dir_name)?;

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use std::{env, fs, io, thread, time};
use std::collections::HashMap;
@@ -10,6 +10,7 @@ use std::process::{Command, ExitStatus, Output};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Once;
use std::time::SystemTime;
use utils::get_path_from_env_var;
static INIT: Once = Once::new();
static mut SHARED: Option<TestEnvironmentShared> = None;
@@ -196,12 +197,10 @@ pub fn layout_launcher(
// ├── jbr/
// └── product-info.json
let launcher = project_root
.join("target")
.join("debug")
.join("xplat-launcher");
assert!(launcher.exists());
let launcher = &get_testing_binary_root(project_root).join("xplat-launcher");
if !launcher.exists() {
bail!("Didn't find source launcher to layout, expected path: {launcher:?}");
}
layout_launcher_impl(
target_dir,
@@ -243,11 +242,10 @@ pub fn layout_launcher(
// │ └── app.jar
// └── jbr/
let launcher = project_root
.join("target")
.join("debug")
.join("xplat-launcher");
assert!(launcher.exists());
let launcher = get_testing_binary_root(project_root).join("xplat-launcher");
if !launcher.exists() {
bail!("Didn't find source launcher to layout, expected path: {launcher:?}");
}
layout_launcher_impl(
target_dir,
@@ -292,11 +290,10 @@ pub fn layout_launcher(
// ├── jbr/
// └── product-info.json
let launcher = project_root
.join("target")
.join("debug")
.join("xplat-launcher.exe");
assert!(launcher.exists());
let launcher = get_testing_binary_root(project_root).join("xplat-launcher.exe");
if !launcher.exists() {
bail!("Didn't find source launcher to layout, expected path: {launcher:?}");
}
layout_launcher_impl(
target_dir,
@@ -318,6 +315,22 @@ pub fn layout_launcher(
Ok(())
}
fn get_testing_binary_root(project_root: &Path) -> PathBuf {
match get_path_from_env_var("XPLAT_LAUNCHER_TESTS_TARGET_BINARY_ROOT") {
Ok(x) => x,
Err(_) => {
let target = match cfg!(debug_assertions) {
true => { "debug".to_string() }
false => { "release".to_string() }
};
project_root
.join("target")
.join(target)
}
}
}
fn layout_launcher_impl(
target_dir: &Path,
create_dirs: Vec<&str>,

1
native/XPlatLauncher/utils/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
Cargo.lock

View File

@@ -0,0 +1,10 @@
[package]
name = "utils"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { version = "1.0.62", features = ["std", "backtrace"] }
log = "0.4.14"

View File

@@ -1,81 +1,76 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
use std::{env, fs};
use std::ffi::OsStr;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use log::debug;
use crate::err_from_string;
use crate::errors::Result;
#[cfg(target_os = "windows")]
pub fn canonical_non_unc(path: &Path) -> Result<String> {
let canonical = path.canonicalize()?;
let os_str = canonical.as_os_str().to_string_lossy().to_string();
let stripped_unc = os_str.strip_prefix("\\\\?\\").unwrap().to_string();
Ok(stripped_unc)
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn canonical_non_unc(path: &Path) -> Result<String> {
let canonical = path.canonicalize()?;
let os_str = canonical.as_os_str().to_string_lossy().to_string();
Ok(os_str)
}
pub fn get_readable_file_from_env_var<S:AsRef<OsStr>>(env_var_name: S) -> Result<PathBuf> {
let file = get_path_from_env_var(env_var_name)?;
let _path_buf = is_readable(&file)?;
Ok(file)
}
pub fn get_path_from_env_var<S:AsRef<OsStr>>(env_var_name: S) -> Result<PathBuf> {
let env_var_value = env::var(env_var_name)?;
if env_var_value.is_empty() {
let message = format!("Env var {env_var_value} is not set, skipping resolving path from it");
return err_from_string(message);
}
let path = PathBuf::from(env_var_value.as_str());
Ok(path)
}
pub fn is_readable<P: AsRef<Path>>(file: P) -> Result<PathBuf> {
// TODO: seems like the best possible x-plat way to verify whether the file is readable
// note: file is closed when we exit the scope
{
let _file = fs::OpenOptions::new().read(true).open(&file)?;
}
Ok(file.as_ref().to_path_buf())
}
pub fn read_file_to_end(path: &Path) -> Result<String> {
let file = File::open(path)?;
let mut reader = BufReader::new(file);
let mut content = String::from("");
let bytes_read = reader.read_to_string(&mut content)?;
debug!("Read {bytes_read} bytes from {path:?}");
debug!("Contents of {path:?}: {content}");
Ok(content)
}
pub trait PathExt {
fn parent_or_err(&self) -> Result<PathBuf>;
}
impl PathExt for Path {
fn parent_or_err(&self) -> Result<PathBuf> {
match self.parent() {
None => {
let message = format!("No parent dir for '{self:?}'");
err_from_string(message)
}
Some(s) => { Ok(s.to_path_buf()) }
}
}
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
use std::{env, fs};
use std::ffi::OsStr;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use anyhow::{bail, Result};
use log::debug;
#[cfg(target_os = "windows")]
pub fn canonical_non_unc(path: &Path) -> Result<String> {
let canonical = path.canonicalize()?;
let os_str = canonical.as_os_str().to_string_lossy().to_string();
let stripped_unc = os_str.strip_prefix("\\\\?\\").unwrap().to_string();
Ok(stripped_unc)
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn canonical_non_unc(path: &Path) -> Result<String> {
let canonical = path.canonicalize()?;
let os_str = canonical.as_os_str().to_string_lossy().to_string();
Ok(os_str)
}
pub fn get_readable_file_from_env_var<S:AsRef<OsStr>>(env_var_name: S) -> Result<PathBuf> {
let file = get_path_from_env_var(env_var_name)?;
let _path_buf = is_readable(&file)?;
Ok(file)
}
pub fn get_path_from_env_var<S:AsRef<OsStr>>(env_var_name: S) -> Result<PathBuf> {
let env_var_value = env::var(env_var_name)?;
if env_var_value.is_empty() {
bail!("Env var {env_var_value} is not set, skipping resolving path from it")
}
let path = PathBuf::from(env_var_value.as_str());
Ok(path)
}
pub fn is_readable<P: AsRef<Path>>(file: P) -> Result<PathBuf> {
// TODO: seems like the best possible x-plat way to verify whether the file is readable
// note: file is closed when we exit the scope
{
let _file = fs::OpenOptions::new().read(true).open(&file)?;
}
Ok(file.as_ref().to_path_buf())
}
pub fn read_file_to_end(path: &Path) -> Result<String> {
let file = File::open(path)?;
let mut reader = BufReader::new(file);
let mut content = String::from("");
let bytes_read = reader.read_to_string(&mut content)?;
debug!("Read {bytes_read} bytes from {path:?}");
debug!("Contents of {path:?}: {content}");
Ok(content)
}
pub trait PathExt {
fn parent_or_err(&self) -> Result<PathBuf>;
}
impl PathExt for Path {
fn parent_or_err(&self) -> Result<PathBuf> {
match self.parent() {
None => bail!("No parent dir for '{self:?}'"),
Some(s) => Ok(s.to_path_buf()),
}
}
}