-
-
Save RandyMcMillan/5ca601ce02ff19905ad1497c9d5acc08 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::env; | |
//use std::env::consts::OS; | |
use std::fs; | |
use std::path::Path; | |
use std::process::{Command, Stdio}; | |
use std::str; | |
fn nvm_has(command: &str) -> bool { | |
Command::new("type") | |
.arg(command) | |
.stdout(Stdio::null()) | |
.stderr(Stdio::null()) | |
.status() | |
.map_or(false, |status| status.success()) | |
} | |
fn nvm_echo(message: &str) { | |
Command::new("printf") | |
.arg(message) | |
.stdout(Stdio::inherit()) | |
.stderr(Stdio::null()) | |
.status() | |
.ok(); | |
} | |
fn nvm_grep(args: &[&str]) -> Result<String, String> { | |
let output = Command::new("grep") | |
.args(args) | |
.stdout(Stdio::piped()) | |
.stderr(Stdio::piped()) | |
.output() | |
.map_err(|e| format!("Failed to execute grep: {}", e))?; | |
if output.status.success() { | |
String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8: {}", e)) | |
} else { | |
String::from_utf8(output.stderr).map_err(|e| format!("Grep failed: {}", e)) | |
} | |
} | |
fn nvm_default_install_dir() -> String { | |
env::var("XDG_CONFIG_HOME").map_or_else( | |
|_| format!("{}/.nvm", env::var("HOME").unwrap_or_default()), | |
|xdg_config_home| format!("{}/nvm", xdg_config_home), | |
) | |
} | |
fn nvm_install_dir() -> String { | |
env::var("NVM_DIR").unwrap_or_else(|_| nvm_default_install_dir()) | |
} | |
fn nvm_latest_version() -> String { | |
"v0.40.2".to_string() | |
} | |
fn nvm_profile_is_bash_or_zsh(profile: &str) -> bool { | |
profile.contains("/.bashrc") | |
|| profile.contains("/.bash_profile") | |
|| profile.contains("/.zshrc") | |
|| profile.contains("/.zprofile") | |
} | |
fn nvm_source(method: &str) -> Result<String, String> { | |
let nvm_github_repo = env::var("NVM_INSTALL_GITHUB_REPO").unwrap_or_else(|_| "nvm-sh/nvm".to_string()); | |
if nvm_github_repo != "nvm-sh/nvm" { | |
eprintln!( | |
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\ | |
@ WARNING: REMOTE REPO IDENTIFICATION HAS CHANGED! @\n\ | |
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\ | |
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n\n\ | |
The default repository for this install is `nvm-sh/nvm`,\n\ | |
but the environment variables `$NVM_INSTALL_GITHUB_REPO` is\n\ | |
currently set to `{}`.\n\n\ | |
If this is not intentional, interrupt this installation and\n\ | |
verify your environment variables.", | |
nvm_github_repo | |
); | |
} | |
let nvm_version = env::var("NVM_INSTALL_VERSION").unwrap_or_else(|_| nvm_latest_version()); | |
let nvm_source_url = match method { | |
"script-nvm-exec" => format!( | |
"https://raw.githubusercontent.com/{}/{}/nvm-exec", | |
nvm_github_repo, nvm_version | |
), | |
"script-nvm-bash-completion" => format!( | |
"https://raw.githubusercontent.com/{}/{}/bash_completion", | |
nvm_github_repo, nvm_version | |
), | |
"script" => format!( | |
"https://raw.githubusercontent.com/{}/{}/nvm.sh", | |
nvm_github_repo, nvm_version | |
), | |
"git" | "" => format!("https://github.com/{}.git", nvm_github_repo), | |
_ => return Err(format!("Unexpected value \"{}\" for $NVM_METHOD", method)), | |
}; | |
Ok(nvm_source_url) | |
} | |
fn nvm_node_version() -> String { | |
env::var("NODE_VERSION").unwrap_or_default() | |
} | |
fn nvm_download(args: &[String]) -> Result<(), String> { | |
if nvm_has("curl") { | |
let mut command = Command::new("curl"); | |
command.args(args); | |
let output = command | |
.stdout(Stdio::inherit()) | |
.stderr(Stdio::inherit()) | |
.status() | |
.map_err(|e| format!("Failed to execute curl: {}", e))?; | |
if !output.success(){ | |
return Err("Curl failed".to_string()); | |
} | |
} else if nvm_has("wget") { | |
let sed_args: Vec<String> = args | |
.iter() | |
.map(|arg| { | |
arg.replace("--progress-bar", "--progress=bar") | |
.replace("--compressed", "") | |
.replace("--fail", "") | |
.replace("-L", "") | |
.replace("-I", "--server-response") | |
.replace("-s ", "-q ") | |
.replace("-sS", "-nv") | |
.replace("-o", "-O") | |
.replace("-C -", "-c") | |
}) | |
.collect(); | |
let mut command = Command::new("wget"); | |
command.args(sed_args); | |
let output = command | |
.stdout(Stdio::inherit()) | |
.stderr(Stdio::inherit()) | |
.status() | |
.map_err(|e| format!("Failed to execute wget: {}", e))?; | |
if !output.success(){ | |
return Err("Wget failed".to_string()); | |
} | |
} else { | |
return Err("Neither curl nor wget is available".to_string()); | |
} | |
Ok(()) | |
} | |
fn install_nvm_from_git() -> Result<(), String> { | |
let install_dir = nvm_install_dir(); | |
let nvm_version = env::var("NVM_INSTALL_VERSION").unwrap_or_else(|_| nvm_latest_version()); | |
if !nvm_version.is_empty() { | |
let _remote_ref = nvm_source("git")?; | |
let grep_output = nvm_grep(&["-q", &nvm_version])?; | |
if !grep_output.contains(&nvm_version) { | |
if nvm_download(&["-o".to_string(), "/dev/null".to_string(), nvm_source("script-nvm-exec")?]) | |
.is_err() | |
{ | |
return Err(format!("Failed to find '{}' version.", nvm_version)); | |
} | |
} | |
} | |
if Path::new(&format!("{}/.git", install_dir)).exists() { | |
nvm_echo(&format!( | |
"=> nvm is already installed in {}, trying to update using git", | |
install_dir | |
)); | |
nvm_echo("\r=> "); | |
} else { | |
nvm_echo(&format!("=> Downloading nvm from git to '{}'", install_dir)); | |
nvm_echo("\r=> "); | |
fs::create_dir_all(&install_dir).map_err(|e| format!("Failed to create directory: {}", e))?; | |
if fs::read_dir(&install_dir) | |
.map_err(|e| format!("Failed to read directory: {}", e))? | |
.next() | |
.is_some() | |
{ | |
Command::new("git") | |
.arg("init") | |
.arg(&install_dir) | |
.status() | |
.map_err(|e| format!("Failed to initialize git: {}", e))?; | |
Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("remote") | |
.arg("add") | |
.arg("origin") | |
.arg(nvm_source("")?) | |
.stderr(Stdio::null()) | |
.status() | |
.or_else(|_| { | |
Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("remote") | |
.arg("set-url") | |
.arg("origin") | |
.arg(nvm_source("").expect("")) | |
.status() | |
}) | |
.map_err(|e| format!("Failed to add remote: {}", e)).expect(""); | |
} else { | |
Command::new("git") | |
.arg("clone") | |
.arg(nvm_source("")?) | |
.arg(format!("--depth=1")) | |
.arg(&install_dir) | |
.status() | |
.map_err(|e| format!("Failed to clone git: {}", e))?; | |
} | |
} | |
Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("fetch") | |
.arg("origin") | |
.arg("tag") | |
.arg(&nvm_version) | |
.arg("--depth=1") | |
.stderr(Stdio::null()) | |
.status() | |
.or_else(|_| { | |
Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("fetch") | |
.arg("origin") | |
.arg(&nvm_version) | |
.arg("--depth=1") | |
.status() | |
}) | |
.map_err(|e| format!("Failed to fetch: {}", e))?; | |
Command::new("git") | |
.arg("-c") | |
.arg("advice.detachedHead=false") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("checkout") | |
.arg("-f") | |
.arg("--quiet") | |
.arg("FETCH_HEAD") | |
.status() | |
.map_err(|e| format!("Failed to checkout: {}", e))?; | |
if Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("show-ref") | |
.arg("refs/heads/master") | |
.status() | |
.map_or(false, |status| status.success()) | |
{ | |
if Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("branch") | |
.arg("--quiet") | |
.stderr(Stdio::null()) | |
.status() | |
.map_or(false, |status| status.success()) | |
{ | |
Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("branch") | |
.arg("-D") | |
.arg("master") | |
.stdout(Stdio::null()) | |
.stderr(Stdio::null()) | |
.status() | |
.ok(); | |
} else { | |
eprintln!("Your version of git is out of date. Please update it!"); | |
Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("branch") | |
.arg("-D") | |
.arg("master") | |
.stdout(Stdio::null()) | |
.stderr(Stdio::null()) | |
.status() | |
.ok(); | |
} | |
} | |
nvm_echo("=> Compressing and cleaning up git repository"); | |
if Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("reflog") | |
.arg("expire") | |
.arg("--expire=now") | |
.arg("--all") | |
.status() | |
.map_or(false, |status| status.success()) | |
{ | |
} else { | |
eprintln!("Your version of git is out of date. Please update it!"); | |
} | |
if Command::new("git") | |
.arg(format!("--git-dir={}/.git", install_dir)) | |
.arg(format!("--work-tree={}", install_dir)) | |
.arg("gc") | |
.arg("--auto") | |
.arg("--aggressive") | |
.arg("--prune=now") | |
.status() | |
.map_or(false, |status| status.success()) | |
{ | |
} else { | |
eprintln!("Your version of git is out of date. Please update it!"); | |
} | |
Ok(()) | |
} | |
fn nvm_install_node() -> Result<(), String> { | |
let node_version = nvm_node_version(); | |
if node_version.is_empty() { | |
return Ok(()); | |
} | |
nvm_echo(&format!("=> Installing Node.js version {}", node_version)); | |
Command::new("nvm") | |
.arg("install") | |
.arg(&node_version) | |
.status() | |
.map_err(|e| format!("Failed to install node: {}", e))?; | |
let current_node = Command::new("nvm") | |
.arg("current") | |
.output() | |
.map_err(|e| format!("Failed to get current node version: {}", e))?; | |
let current_node_str = str::from_utf8(¤t_node.stdout) | |
.map_err(|e| format!("Invalid UTF-8: {}", e))? | |
.trim(); | |
let installed_node = Command::new("nvm") | |
.arg("version") | |
.arg(&node_version) | |
.output() | |
.map_err(|e| format!("failed to get installed node version: {}",e))?; | |
let installed_node_str = str::from_utf8(&installed_node.stdout).map_err(|e| format!("Invalid UTF-8: {}", e))?.trim(); | |
if installed_node_str == current_node_str { | |
nvm_echo(&format!( | |
"=> Node.js version {} has been successfully installed", | |
node_version | |
)); | |
} else { | |
eprintln!("Failed to install Node.js {}", node_version); | |
} | |
Ok(()) | |
} | |
fn install_nvm_as_script() -> Result<(), String> { | |
let install_dir = nvm_install_dir(); | |
let nvm_source_script = nvm_source("script")?; | |
let nvm_exec_source = nvm_source("script-nvm-exec")?; | |
let nvm_bash_completion_source = nvm_source("script-nvm-bash-completion")?; | |
fs::create_dir_all(&install_dir).map_err(|e| format!("Failed to create directory: {}", e))?; | |
if Path::new(&format!("{}/nvm.sh", install_dir)).exists() { | |
nvm_echo(&format!( | |
"=> nvm is already installed in {}, trying to update the script", | |
install_dir | |
)); | |
} else { | |
nvm_echo(&format!("=> Downloading nvm as script to '{}'", install_dir)); | |
} | |
nvm_download(&["-s".to_string(), nvm_source_script, "-o".to_string(), format!("{}/nvm.sh", install_dir)])?; | |
nvm_download(&["-s".to_string(), nvm_exec_source, "-o".to_string(), format!("{}/nvm-exec", install_dir)])?; | |
nvm_download(&["-s".to_string(), nvm_bash_completion_source, "-o".to_string(), format!("{}/bash_completion", install_dir)])?; | |
Command::new("chmod") | |
.arg("a+x") | |
.arg(format!("{}/nvm-exec", install_dir)) | |
.status() | |
.map_err(|e| format!("Failed to make nvm-exec executable: {}", e))?; | |
Ok(()) | |
} | |
fn nvm_try_profile(profile: &str) -> Option<String> { | |
if !Path::new(profile).exists() { | |
return None; | |
} | |
Some(profile.to_string()) | |
} | |
fn nvm_detect_profile() -> Option<String> { | |
if env::var("PROFILE").as_deref() == Ok("/dev/null") { | |
return None; | |
} | |
if let Ok(profile) = env::var("PROFILE") { | |
if Path::new(&profile).exists() { | |
return Some(profile); | |
} | |
} | |
if env::var("SHELL").unwrap_or_default().contains("bash") { | |
if Path::new(&format!("{}/.bashrc", env::var("HOME").unwrap_or_default())).exists() { | |
return Some(format!("{}/.bashrc", env::var("HOME").unwrap_or_default())); | |
} else if Path::new(&format!("{}/.bash_profile", env::var("HOME").unwrap_or_default())).exists() { | |
return Some(format!("{}/.bash_profile", env::var("HOME").unwrap_or_default())); | |
} | |
} else if env::var("SHELL").unwrap_or_default().contains("zsh") { | |
let zdotdir = env::var("ZDOTDIR").unwrap_or_else(|_| env::var("HOME").unwrap_or_default()); | |
if Path::new(&format!("{}/.zshrc", zdotdir)).exists() { | |
return Some(format!("{}/.zshrc", zdotdir)); | |
} else if Path::new(&format!("{}/.zprofile", zdotdir)).exists() { | |
return Some(format!("{}/.zprofile", zdotdir)); | |
} | |
} | |
for profile in [".profile", ".bashrc", ".bash_profile", ".zprofile", ".zshrc"] { | |
if let Some(profile_path) = nvm_try_profile(&format!("{}/{}", env::var("HOME").unwrap_or_default(), profile)) { | |
return Some(profile_path); | |
} | |
} | |
None | |
} | |
fn nvm_check_global_modules() -> Result<(), String> { | |
let npm_command = Command::new("command") | |
.arg("-v") | |
.arg("npm") | |
.stderr(Stdio::null()) | |
.output() | |
.map_or(None, |output| { | |
if output.status.success() { | |
String::from_utf8(output.stdout).ok() | |
} else { | |
None | |
} | |
}); | |
if npm_command.is_none() { | |
return Ok(()); | |
} | |
if env::var("NVM_DIR").is_ok() { | |
if npm_command.clone().unwrap().contains(&env::var("NVM_DIR").unwrap()) { | |
return Ok(()); | |
} | |
} | |
let npm_version = Command::new("npm") | |
.arg("--version") | |
.output() | |
.map_or_else(|_| "-1".to_string(), |output| { | |
String::from_utf8(output.stdout).unwrap_or_default() | |
}); | |
let npm_version_num = npm_version.split(|c: char| !c.is_ascii_digit()).next().unwrap_or("-1").parse::<i32>().unwrap_or(-1); | |
if npm_version_num <= 0 { | |
return Ok(()); | |
} | |
let npm_global_modules = Command::new("npm") | |
.arg("list") | |
.arg("-g") | |
.arg("--depth=0") | |
.output() | |
.map_or_else(|_| "".to_string(), |output| { | |
String::from_utf8(output.stdout).unwrap_or_default() | |
}); | |
let module_count = npm_global_modules | |
.lines() | |
.skip(1) | |
.filter(|line| !line.contains("npm@") && !line.contains("(empty)")) | |
.count(); | |
if module_count != 0 { | |
eprintln!("=> You currently have modules installed globally with `npm`. These will no"); | |
eprintln!("=> longer be linked to the active version of Node when you install a new node"); | |
eprintln!("=> with `nvm`; and they may (depending on how you construct your `$PATH`)"); | |
eprintln!("=> override the binaries of modules installed with `nvm`:"); | |
eprintln!(); | |
npm_global_modules.lines().skip(1).filter(|line| !line.contains("npm@") && !line.contains("(empty)")).for_each(|line| eprintln!("{}", line)); | |
eprintln!("=> If you wish to uninstall them at a later point (or re-install them under your"); | |
eprintln!("=> `nvm` node installs), you can remove them from the system Node as follows:"); | |
eprintln!(); | |
eprintln!(" $ nvm use system"); | |
eprintln!(" $ npm uninstall -g a_module"); | |
eprintln!(); | |
} | |
Ok(()) | |
} | |
fn nvm_do_install() -> Result<(), String> { | |
let nvm_dir = env::var("NVM_DIR").ok(); | |
if let Some(dir) = nvm_dir { | |
if !Path::new(&dir).exists() { | |
if Path::new(&dir).exists() { | |
return Err(format!("File \"{}\" has the same name as installation directory.", dir)); | |
} | |
if dir == nvm_default_install_dir() { | |
fs::create_dir_all(&dir).map_err(|e| format!("Failed to create directory: {}", e))?; | |
} else { | |
return Err(format!("You have $NVM_DIR set to \"{}\", but that directory does not exist. Check your profile files and environment.", dir)); | |
} | |
} | |
} | |
if nvm_has("xcode-select") { | |
if Command::new("xcode-select") | |
.arg("-p") | |
.stdout(Stdio::null()) | |
.stderr(Stdio::null()) | |
.status() | |
.map_or(false, |s| s.code() == Some(2)) | |
{ | |
if nvm_has("git") && Command::new("which").arg("git").output().map_or(false, |o| String::from_utf8_lossy(&o.stdout).contains("/usr/bin/git")) && nvm_has("curl") && Command::new("which").arg("curl").output().map_or(false, |o| String::from_utf8_lossy(&o.stdout).contains("/usr/bin/curl")) { | |
eprintln!("You may be on a Mac, and need to install the Xcode Command Line Developer Tools."); | |
eprintln!("If so, run `xcode-select --install` and try again. If not, please report this!"); | |
return Err("Xcode Command Line Tools missing".to_string()); | |
} | |
} | |
} | |
let method = env::var("METHOD").unwrap_or_default(); | |
if method.is_empty() { | |
if nvm_has("git") { | |
install_nvm_from_git()?; | |
} else if nvm_has("curl") || nvm_has("wget") { | |
install_nvm_as_script()?; | |
} else { | |
return Err("You need git, curl, or wget to install nvm".to_string()); | |
} | |
} else if method == "git" { | |
if !nvm_has("git") { | |
return Err("You need git to install nvm".to_string()); | |
} | |
install_nvm_from_git()?; | |
} else if method == "script" { | |
if !nvm_has("curl") && !nvm_has("wget") { | |
return Err("You need curl or wget to install nvm".to_string()); | |
} | |
install_nvm_as_script()?; | |
} else { | |
return Err(format!( | |
"The environment variable $METHOD is set to \"{}\", which is not recognized as a valid installation method.", | |
method | |
)); | |
} | |
nvm_echo(""); | |
let nvm_profile = nvm_detect_profile(); | |
let profile_install_dir = nvm_install_dir().replace(&env::var("HOME").unwrap_or_default(), "$HOME"); | |
let source_str = format!( | |
"\\nexport NVM_DIR=\"{}\"\\n[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\" # This loads nvm\\n", | |
profile_install_dir | |
); | |
let completion_str = format!( | |
"[ -s \"$NVM_DIR/bash_completion\" ] && \\. \"$NVM_DIR/bash_completion\" # This loads nvm bash_completion\\n" | |
); | |
let bash_or_zsh = nvm_profile.as_ref().map(|profile| nvm_profile_is_bash_or_zsh(profile)).unwrap_or(false); | |
if nvm_profile.is_none() { | |
let tried_profile = env::var("PROFILE").map(|p| format!("{} (as defined in $PROFILE), ", p)).unwrap_or_default(); | |
nvm_echo(&format!( | |
"=> Profile not found. Tried {}~/.bashrc, ~/.bash_profile, ~/.zprofile, ~/.zshrc, and ~/.profile.", | |
tried_profile | |
)); | |
nvm_echo("=> Create one of them and run this script again"); | |
nvm_echo(" OR"); | |
nvm_echo("=> Append the following lines to the correct file yourself:"); | |
print!("{}", source_str); | |
nvm_echo(""); | |
} else { | |
if !Command::new("grep").arg("-qc").arg("/nvm.sh").arg(nvm_profile.as_ref().unwrap()).status().map_or(false, |s| s.success()) { | |
nvm_echo(&format!("=> Appending nvm source string to {}", nvm_profile.as_ref().unwrap())); | |
//fs::OpenOptions::new().append(true).open(nvm_profile.as_ref().unwrap()).map_err(|e| format!("Failed to open file: {}",e))?.write(source_str.as_bytes()).map_err(|e| format!("failed to write to file: {}",e))?; | |
} else { | |
nvm_echo(&format!("=> nvm source string already in {}", nvm_profile.as_ref().unwrap())); | |
} | |
if bash_or_zsh && !Command::new("grep").arg("-qc").arg("$NVM_DIR/bash_completion").arg(nvm_profile.as_ref().unwrap()).status().map_or(false, |s| s.success()) { | |
nvm_echo(&format!("=> Appending bash_completion source string to {}", nvm_profile.as_ref().unwrap())); | |
//fs::OpenOptions::new().append(true).open(nvm_profile.as_ref().unwrap()).map_err(|e| format!("Failed to open file: {}",e))?.write(completion_str.as_bytes()).map_err(|e| format!("failed to write to file: {}",e))?; | |
} else { | |
nvm_echo(&format!("=> bash_completion source string already in {}", nvm_profile.as_ref().unwrap())); | |
} | |
} | |
if bash_or_zsh && nvm_profile.is_none() { | |
nvm_echo("=> Please also append the following lines to the if you are using bash/zsh shell:"); | |
print!("{}", completion_str); | |
} | |
Command::new(format!("{}/nvm.sh", nvm_install_dir())) | |
.arg("reset") | |
.status() | |
.map_err(|e| format!("Failed to reset nvm: {}", e))?; | |
nvm_check_global_modules()?; | |
nvm_install_node()?; | |
nvm_echo("=> Close and reopen your terminal to start using nvm or run the following to use it now:"); | |
print!("{}", source_str); | |
if bash_or_zsh { | |
print!("{}", completion_str); | |
} | |
Ok(()) | |
} | |
fn main() { | |
if env::var("NVM_ENV").as_deref() != Ok("testing") { | |
if let Err(err) = nvm_do_install() { | |
eprintln!("Error: {}", err); | |
std::process::exit(1); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment