feat: add --debug flag to print debug information (#794)

This commit is contained in:
sxyazi 2024-03-09 19:04:46 +08:00
parent 9396d8760c
commit b10f2de16d
No known key found for this signature in database
9 changed files with 322 additions and 234 deletions

View File

@ -12,21 +12,14 @@ body:
- Linux Wayland - Linux Wayland
- macOS - macOS
- Windows - Windows
- Windows WSL
validations: validations:
required: true required: true
- type: input - type: input
id: terminal id: terminal
attributes: attributes:
label: What terminal are you running Yazi in? label: What terminal are you running Yazi in?
placeholder: "ex: Kitty v0.30.1" placeholder: "ex: kitty v0.32.2"
validations:
required: true
- type: input
id: version
attributes:
label: Yazi version
description: Please do a `yazi -V` and paste the output here.
placeholder: "ex: yazi 0.1.5 (3867c29 2023-11-25)"
validations: validations:
required: true required: true
- type: dropdown - type: dropdown
@ -38,6 +31,18 @@ body:
- Not tried, and I'll explain why below - Not tried, and I'll explain why below
validations: validations:
required: true required: true
- type: textarea
id: debug
attributes:
label: "`yazi --debug` output"
description: Please do a `yazi --debug` and paste the output here.
value: |
<!-- Paste the output between the backticks below: -->
```sh
```
validations:
required: true
- type: textarea - type: textarea
id: description id: description
attributes: attributes:

1
Cargo.lock generated
View File

@ -2716,6 +2716,7 @@ dependencies = [
"clap_complete_nushell", "clap_complete_nushell",
"serde", "serde",
"vergen", "vergen",
"yazi-adaptor",
"yazi-config", "yazi-config",
"yazi-shared", "yazi-shared",
] ]

View File

@ -1,13 +1,12 @@
use std::{env, fmt::Display, io::{Read, Write}, path::Path, sync::Arc}; use std::{env, fmt::Display, path::Path, sync::Arc};
use anyhow::{anyhow, Result}; use anyhow::Result;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use ratatui::layout::Rect; use ratatui::layout::Rect;
use tracing::warn; use tracing::warn;
use yazi_shared::{env_exists, term::Term}; use yazi_shared::{env_exists, term::Term};
use super::{Iterm2, Kitty, KittyOld}; use super::{Iterm2, Kitty, KittyOld};
use crate::{ueberzug::Ueberzug, Sixel, SHOWN, TMUX}; use crate::{ueberzug::Ueberzug, Emulator, Sixel, SHOWN, TMUX};
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Adaptor { pub enum Adaptor {
@ -22,82 +21,73 @@ pub enum Adaptor {
Chafa, Chafa,
} }
#[derive(Clone)] impl Display for Adaptor {
enum Emulator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Unknown(Vec<Adaptor>), match self {
Kitty, Self::Kitty => write!(f, "kitty"),
Konsole, Self::KittyOld => write!(f, "kitty"),
Iterm2, Self::Iterm2 => write!(f, "iterm2"),
WezTerm, Self::Sixel => write!(f, "sixel"),
Foot, Self::X11 => write!(f, "x11"),
Ghostty, Self::Wayland => write!(f, "wayland"),
BlackBox, Self::Chafa => write!(f, "chafa"),
VSCode, }
Tabby, }
Hyper,
Mintty,
Neovim,
} }
impl Adaptor { impl Adaptor {
fn emulator() -> Emulator { pub async fn image_show(self, path: &Path, rect: Rect) -> Result<(u32, u32)> {
if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") { match self {
return Emulator::Neovim; Self::Kitty => Kitty::image_show(path, rect).await,
Self::KittyOld => KittyOld::image_show(path, rect).await,
Self::Iterm2 => Iterm2::image_show(path, rect).await,
Self::Sixel => Sixel::image_show(path, rect).await,
_ => Ueberzug::image_show(path, rect).await,
} }
let vars = [
("KITTY_WINDOW_ID", Emulator::Kitty),
("KONSOLE_VERSION", Emulator::Konsole),
("ITERM_SESSION_ID", Emulator::Iterm2),
("WEZTERM_EXECUTABLE", Emulator::WezTerm),
("GHOSTTY_RESOURCES_DIR", Emulator::Ghostty),
("VSCODE_INJECTION", Emulator::VSCode),
("TABBY_CONFIG_DIRECTORY", Emulator::Tabby),
];
match vars.into_iter().find(|v| env_exists(v.0)) {
Some(var) => return var.1,
None => warn!("[Adaptor] No special environment variables detected"),
}
let (term, program) = Self::via_env();
match program.as_str() {
"iTerm.app" => return Emulator::Iterm2,
"WezTerm" => return Emulator::WezTerm,
"ghostty" => return Emulator::Ghostty,
"BlackBox" => return Emulator::BlackBox,
"vscode" => return Emulator::VSCode,
"Tabby" => return Emulator::Tabby,
"Hyper" => return Emulator::Hyper,
"mintty" => return Emulator::Mintty,
_ => warn!("[Adaptor] Unknown TERM_PROGRAM: {program}"),
}
match term.as_str() {
"xterm-kitty" => return Emulator::Kitty,
"foot" => return Emulator::Foot,
"foot-extra" => return Emulator::Foot,
"xterm-ghostty" => return Emulator::Ghostty,
_ => warn!("[Adaptor] Unknown TERM: {term}"),
}
Self::via_csi().unwrap_or(Emulator::Unknown(vec![]))
} }
pub(super) fn detect() -> Self { pub fn image_hide(self) -> Result<()> {
let mut protocols = match Self::emulator() { if let Some(rect) = SHOWN.swap(None) { self.image_erase(*rect) } else { Ok(()) }
Emulator::Unknown(adapters) => adapters, }
Emulator::Kitty => vec![Self::Kitty],
Emulator::Konsole => vec![Self::KittyOld, Self::Iterm2, Self::Sixel], pub fn image_erase(self, rect: Rect) -> Result<()> {
Emulator::Iterm2 => vec![Self::Iterm2, Self::Sixel], match self {
Emulator::WezTerm => vec![Self::Iterm2, Self::Sixel], Self::Kitty => Kitty::image_erase(rect),
Emulator::Foot => vec![Self::Sixel], Self::Iterm2 => Iterm2::image_erase(rect),
Emulator::Ghostty => vec![Self::KittyOld], Self::KittyOld => KittyOld::image_erase(),
Emulator::BlackBox => vec![Self::Sixel], Self::Sixel => Sixel::image_erase(rect),
Emulator::VSCode => vec![Self::Iterm2, Self::Sixel], _ => Ueberzug::image_erase(rect),
Emulator::Tabby => vec![Self::Iterm2, Self::Sixel], }
Emulator::Hyper => vec![Self::Iterm2, Self::Sixel], }
Emulator::Mintty => vec![Self::Iterm2],
Emulator::Neovim => vec![], #[inline]
}; pub fn shown_load(self) -> Option<Rect> { SHOWN.load_full().map(|r| *r) }
pub(super) fn start(self) { Ueberzug::start(self); }
#[inline]
pub(super) fn shown_store(rect: Rect, size: (u32, u32)) {
SHOWN.store(Some(Arc::new(
Term::ratio()
.map(|(r1, r2)| Rect {
x: rect.x,
y: rect.y,
width: (size.0 as f64 / r1).ceil() as u16,
height: (size.1 as f64 / r2).ceil() as u16,
})
.unwrap_or(rect),
)));
}
#[inline]
pub(super) fn needs_ueberzug(self) -> bool {
!matches!(self, Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel)
}
}
impl Adaptor {
pub fn matches() -> Self {
let mut protocols = Emulator::detect().adapters();
#[cfg(windows)] #[cfg(windows)]
protocols.retain(|p| *p == Self::Iterm2); protocols.retain(|p| *p == Self::Iterm2);
@ -129,134 +119,4 @@ impl Adaptor {
warn!("[Adaptor] Falling back to chafa"); warn!("[Adaptor] Falling back to chafa");
Self::Chafa Self::Chafa
} }
fn via_env() -> (String, String) {
fn tmux_env(name: &str) -> Result<String> {
let output = std::process::Command::new("tmux").args(["show-environment", name]).output()?;
String::from_utf8(output.stdout)?
.trim()
.strip_prefix(&format!("{name}="))
.map_or_else(|| Err(anyhow!("")), |s| Ok(s.to_string()))
}
let mut term = env::var("TERM").unwrap_or_default();
let mut program = env::var("TERM_PROGRAM").unwrap_or_default();
if *TMUX {
term = tmux_env("TERM").unwrap_or(term);
program = tmux_env("TERM_PROGRAM").unwrap_or(program);
}
(term, program)
}
fn via_csi() -> Result<Emulator> {
enable_raw_mode()?;
std::io::stdout().write_all(b"\x1b[>q\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c")?;
std::io::stdout().flush()?;
let mut stdin = std::io::stdin().lock();
let mut buf = String::with_capacity(200);
loop {
let mut c = [0; 1];
if stdin.read(&mut c)? == 0 {
break;
}
if c[0] == b'c' && buf.contains("\x1b[?") {
break;
}
buf.push(c[0] as char);
}
disable_raw_mode().ok();
let names = [
("kitty", Emulator::Kitty),
("Konsole", Emulator::Konsole),
("iTerm2", Emulator::Iterm2),
("WezTerm", Emulator::WezTerm),
("foot", Emulator::Foot),
("ghostty", Emulator::Ghostty),
];
for (name, emulator) in names.iter() {
if buf.contains(name) {
return Ok(emulator.clone());
}
}
let mut adapters = Vec::with_capacity(2);
if buf.contains("\x1b_Gi=31;OK") {
adapters.push(Adaptor::KittyOld);
}
if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| buf.contains(s)) {
adapters.push(Adaptor::Sixel);
}
Ok(Emulator::Unknown(adapters))
}
}
impl Display for Adaptor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Kitty => write!(f, "kitty"),
Self::KittyOld => write!(f, "kitty"),
Self::Iterm2 => write!(f, "iterm2"),
Self::Sixel => write!(f, "sixel"),
Self::X11 => write!(f, "x11"),
Self::Wayland => write!(f, "wayland"),
Self::Chafa => write!(f, "chafa"),
}
}
}
impl Adaptor {
pub(super) fn start(self) { Ueberzug::start(self); }
pub async fn image_show(self, path: &Path, rect: Rect) -> Result<(u32, u32)> {
match self {
Self::Kitty => Kitty::image_show(path, rect).await,
Self::KittyOld => KittyOld::image_show(path, rect).await,
Self::Iterm2 => Iterm2::image_show(path, rect).await,
Self::Sixel => Sixel::image_show(path, rect).await,
_ => Ueberzug::image_show(path, rect).await,
}
}
pub fn image_hide(self) -> Result<()> {
if let Some(rect) = SHOWN.swap(None) { self.image_erase(*rect) } else { Ok(()) }
}
pub fn image_erase(self, rect: Rect) -> Result<()> {
match self {
Self::Kitty => Kitty::image_erase(rect),
Self::Iterm2 => Iterm2::image_erase(rect),
Self::KittyOld => KittyOld::image_erase(),
Self::Sixel => Sixel::image_erase(rect),
_ => Ueberzug::image_erase(rect),
}
}
#[inline]
pub fn shown_load(self) -> Option<Rect> { SHOWN.load_full().map(|r| *r) }
#[inline]
pub(super) fn shown_store(rect: Rect, size: (u32, u32)) {
SHOWN.store(Some(Arc::new(
Term::ratio()
.map(|(r1, r2)| Rect {
x: rect.x,
y: rect.y,
width: (size.0 as f64 / r1).ceil() as u16,
height: (size.1 as f64 / r2).ceil() as u16,
})
.unwrap_or(rect),
)));
}
#[inline]
pub(super) fn needs_ueberzug(self) -> bool {
!matches!(self, Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel)
}
} }

View File

@ -0,0 +1,155 @@
use std::{env, io::{Read, Write}};
use anyhow::{anyhow, Result};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use tracing::warn;
use yazi_shared::env_exists;
use crate::{Adaptor, TMUX};
#[derive(Clone, Debug)]
pub enum Emulator {
Unknown(Vec<Adaptor>),
Kitty,
Konsole,
Iterm2,
WezTerm,
Foot,
Ghostty,
BlackBox,
VSCode,
Tabby,
Hyper,
Mintty,
Neovim,
}
impl Emulator {
pub fn adapters(self) -> Vec<Adaptor> {
match self {
Self::Unknown(adapters) => adapters,
Self::Kitty => vec![Adaptor::Kitty],
Self::Konsole => vec![Adaptor::KittyOld, Adaptor::Iterm2, Adaptor::Sixel],
Self::Iterm2 => vec![Adaptor::Iterm2, Adaptor::Sixel],
Self::WezTerm => vec![Adaptor::Iterm2, Adaptor::Sixel],
Self::Foot => vec![Adaptor::Sixel],
Self::Ghostty => vec![Adaptor::KittyOld],
Self::BlackBox => vec![Adaptor::Sixel],
Self::VSCode => vec![Adaptor::Iterm2, Adaptor::Sixel],
Self::Tabby => vec![Adaptor::Iterm2, Adaptor::Sixel],
Self::Hyper => vec![Adaptor::Iterm2, Adaptor::Sixel],
Self::Mintty => vec![Adaptor::Iterm2],
Self::Neovim => vec![],
}
}
}
impl Emulator {
pub fn detect() -> Self {
if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") {
return Self::Neovim;
}
let vars = [
("KITTY_WINDOW_ID", Self::Kitty),
("KONSOLE_VERSION", Self::Konsole),
("ITERM_SESSION_ID", Self::Iterm2),
("WEZTERM_EXECUTABLE", Self::WezTerm),
("GHOSTTY_RESOURCES_DIR", Self::Ghostty),
("VSCODE_INJECTION", Self::VSCode),
("TABBY_CONFIG_DIRECTORY", Self::Tabby),
];
match vars.into_iter().find(|v| env_exists(v.0)) {
Some(var) => return var.1,
None => warn!("[Adaptor] No special environment variables detected"),
}
let (term, program) = Self::via_env();
match program.as_str() {
"iTerm.app" => return Self::Iterm2,
"WezTerm" => return Self::WezTerm,
"ghostty" => return Self::Ghostty,
"BlackBox" => return Self::BlackBox,
"vscode" => return Self::VSCode,
"Tabby" => return Self::Tabby,
"Hyper" => return Self::Hyper,
"mintty" => return Self::Mintty,
_ => warn!("[Adaptor] Unknown TERM_PROGRAM: {program}"),
}
match term.as_str() {
"xterm-kitty" => return Self::Kitty,
"foot" => return Self::Foot,
"foot-extra" => return Self::Foot,
"xterm-ghostty" => return Self::Ghostty,
_ => warn!("[Adaptor] Unknown TERM: {term}"),
}
Self::via_csi().unwrap_or(Self::Unknown(vec![]))
}
pub fn via_env() -> (String, String) {
fn tmux_env(name: &str) -> Result<String> {
let output = std::process::Command::new("tmux").args(["show-environment", name]).output()?;
String::from_utf8(output.stdout)?
.trim()
.strip_prefix(&format!("{name}="))
.map_or_else(|| Err(anyhow!("")), |s| Ok(s.to_string()))
}
let mut term = env::var("TERM").unwrap_or_default();
let mut program = env::var("TERM_PROGRAM").unwrap_or_default();
if *TMUX {
term = tmux_env("TERM").unwrap_or(term);
program = tmux_env("TERM_PROGRAM").unwrap_or(program);
}
(term, program)
}
pub fn via_csi() -> Result<Self> {
enable_raw_mode()?;
std::io::stdout().write_all(b"\x1b[>q\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c")?;
std::io::stdout().flush()?;
let mut stdin = std::io::stdin().lock();
let mut buf = String::with_capacity(200);
loop {
let mut c = [0; 1];
if stdin.read(&mut c)? == 0 {
break;
}
if c[0] == b'c' && buf.contains("\x1b[?") {
break;
}
buf.push(c[0] as char);
}
disable_raw_mode().ok();
let names = [
("kitty", Self::Kitty),
("Konsole", Self::Konsole),
("iTerm2", Self::Iterm2),
("WezTerm", Self::WezTerm),
("foot", Self::Foot),
("ghostty", Self::Ghostty),
];
for (name, emulator) in names.iter() {
if buf.contains(name) {
return Ok(emulator.clone());
}
}
let mut adapters = Vec::with_capacity(2);
if buf.contains("\x1b_Gi=31;OK") {
adapters.push(Adaptor::KittyOld);
}
if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| buf.contains(s)) {
adapters.push(Adaptor::Sixel);
}
Ok(Self::Unknown(adapters))
}
}

View File

@ -1,6 +1,7 @@
#![allow(clippy::unit_arg)] #![allow(clippy::unit_arg)]
mod adaptor; mod adaptor;
mod emulator;
mod image; mod image;
mod iterm2; mod iterm2;
mod kitty; mod kitty;
@ -8,7 +9,8 @@ mod kitty_old;
mod sixel; mod sixel;
mod ueberzug; mod ueberzug;
use adaptor::*; pub use adaptor::*;
pub use emulator::*;
use iterm2::*; use iterm2::*;
use kitty::*; use kitty::*;
use kitty_old::*; use kitty_old::*;
@ -20,7 +22,7 @@ pub use crate::image::*;
pub static ADAPTOR: RoCell<Adaptor> = RoCell::new(); pub static ADAPTOR: RoCell<Adaptor> = RoCell::new();
// Tmux support // Tmux support
static TMUX: RoCell<bool> = RoCell::new(); pub static TMUX: RoCell<bool> = RoCell::new();
static ESCAPE: RoCell<&'static str> = RoCell::new(); static ESCAPE: RoCell<&'static str> = RoCell::new();
static START: RoCell<&'static str> = RoCell::new(); static START: RoCell<&'static str> = RoCell::new();
static CLOSE: RoCell<&'static str> = RoCell::new(); static CLOSE: RoCell<&'static str> = RoCell::new();
@ -36,7 +38,7 @@ pub fn init() {
SHOWN.with(Default::default); SHOWN.with(Default::default);
ADAPTOR.init(Adaptor::detect()); ADAPTOR.init(Adaptor::matches());
ADAPTOR.start(); ADAPTOR.start();
if *TMUX { if *TMUX {

View File

@ -4,7 +4,7 @@ use anyhow::{bail, Result};
use imagesize::ImageSize; use imagesize::ImageSize;
use ratatui::layout::Rect; use ratatui::layout::Rect;
use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}}; use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}};
use tracing::debug; use tracing::{debug, warn};
use yazi_config::PREVIEW; use yazi_config::PREVIEW;
use yazi_shared::RoCell; use yazi_shared::RoCell;
@ -71,14 +71,17 @@ impl Ueberzug {
} }
fn create_demon(adaptor: Adaptor) -> Result<Child> { fn create_demon(adaptor: Adaptor) -> Result<Child> {
Ok( let result = Command::new("ueberzug")
Command::new("ueberzug") .args(["layer", "-so", &adaptor.to_string()])
.args(["layer", "-so", &adaptor.to_string()]) .kill_on_drop(true)
.kill_on_drop(true) .stdin(Stdio::piped())
.stdin(Stdio::piped()) .stderr(Stdio::null())
.stderr(Stdio::null()) .spawn();
.spawn()?,
) if let Err(ref e) = result {
warn!("ueberzug spawning failed: {}", e);
}
Ok(result?)
} }
fn adjust_rect(mut rect: Rect) -> Rect { fn adjust_rect(mut rect: Rect) -> Rect {

View File

@ -9,8 +9,9 @@ homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi" repository = "https://github.com/sxyazi/yazi"
[dependencies] [dependencies]
yazi-config = { path = "../yazi-config", version = "0.2.4" } yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.4" }
yazi-shared = { path = "../yazi-shared", version = "0.2.4" } yazi-config = { path = "../yazi-config", version = "0.2.4" }
yazi-shared = { path = "../yazi-shared", version = "0.2.4" }
# External dependencies # External dependencies
clap = { version = "^4", features = [ "derive" ] } clap = { version = "^4", features = [ "derive" ] }

View File

@ -20,6 +20,10 @@ pub struct Args {
#[arg(long, action)] #[arg(long, action)]
pub clear_cache: bool, pub clear_cache: bool,
/// Print debug information
#[arg(long, action)]
pub debug: bool,
/// Print version /// Print version
#[arg(short = 'V', long)] #[arg(short = 'V', long)]
pub version: bool, pub version: bool,

View File

@ -1,4 +1,4 @@
use std::{ffi::OsString, path::{Path, PathBuf}, process}; use std::{env, ffi::OsString, path::{Path, PathBuf}, process};
use clap::Parser; use clap::Parser;
use serde::Serialize; use serde::Serialize;
@ -32,6 +32,63 @@ impl Boot {
(parent.unwrap().to_owned(), Some(entry.file_name().unwrap().to_owned())) (parent.unwrap().to_owned(), Some(entry.file_name().unwrap().to_owned()))
} }
fn action_version() {
println!(
"yazi {} ({} {})",
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_BUILD_DATE")
);
}
fn action_debug() {
print!("Yazi\n ");
Self::action_version();
println!("\nEnvironment");
println!(
" OS: {}-{} ({})",
std::env::consts::OS,
std::env::consts::ARCH,
std::env::consts::FAMILY
);
println!(" Debug: {}", cfg!(debug_assertions));
println!("\nEmulator");
println!(" Emulator.via_env: {:?}", yazi_adaptor::Emulator::via_env());
println!(" Emulator.via_csi: {:?}", yazi_adaptor::Emulator::via_csi());
println!(" Emulator.detect: {:?}", yazi_adaptor::Emulator::detect());
println!("\nAdaptor");
println!(" Adaptor.matches: {:?}", yazi_adaptor::Adaptor::matches());
println!("\ntmux");
println!(" TMUX: {:?}", *yazi_adaptor::TMUX);
println!("\nZellij");
println!(" ZELLIJ_SESSION_NAME: {:?}", env::var_os("ZELLIJ_SESSION_NAME"));
println!("\nDesktop");
println!(" XDG_SESSION_TYPE: {:?}", env::var_os("XDG_SESSION_TYPE"));
println!(" WAYLAND_DISPLAY: {:?}", env::var_os("WAYLAND_DISPLAY"));
println!(" DISPLAY: {:?}", env::var_os("DISPLAY"));
println!("\nUeberzug");
println!(" Version: {:?}", std::process::Command::new("ueberzug").arg("--version").output());
println!("\nWSL");
println!(
" /proc/sys/fs/binfmt_misc/WSLInterop: {:?}",
std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok()
);
println!("\n\n--------------------------------------------------");
println!(
"When reporting a bug, please also upload the `yazi.log` log file - only upload the most recent content by time."
);
println!("You can find it in the {:?} directory.", Xdg::state_dir());
}
} }
impl Default for Boot { impl Default for Boot {
@ -58,13 +115,13 @@ impl Default for Args {
fn default() -> Self { fn default() -> Self {
let args = Self::parse(); let args = Self::parse();
if args.debug {
Boot::action_debug();
process::exit(0);
}
if args.version { if args.version {
println!( Boot::action_version();
"yazi {} ({} {})",
env!("CARGO_PKG_VERSION"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_BUILD_DATE")
);
process::exit(0); process::exit(0);
} }