mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-12 11:00:36 +00:00
feat: support binary scripts in PATH
This commit is contained in:
parent
986196ac67
commit
0fcddedad6
7 changed files with 245 additions and 80 deletions
|
@ -1,9 +1,13 @@
|
||||||
use crate::cli::{reqwest_client, IsUpToDate};
|
use crate::cli::{home_dir, reqwest_client, IsUpToDate};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use indicatif::MultiProgress;
|
use indicatif::MultiProgress;
|
||||||
use pesde::{lockfile::Lockfile, manifest::target::TargetKind, Project};
|
use pesde::{lockfile::Lockfile, manifest::target::TargetKind, Project};
|
||||||
use std::{collections::HashSet, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
collections::{BTreeSet, HashSet},
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
pub struct InstallCommand {
|
pub struct InstallCommand {
|
||||||
|
@ -12,6 +16,44 @@ pub struct InstallCommand {
|
||||||
threads: u64,
|
threads: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bin_link_file(alias: &str) -> String {
|
||||||
|
let mut all_combinations = BTreeSet::new();
|
||||||
|
|
||||||
|
for a in TargetKind::VARIANTS {
|
||||||
|
for b in TargetKind::VARIANTS {
|
||||||
|
all_combinations.insert((a, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let all_folders = all_combinations
|
||||||
|
.into_iter()
|
||||||
|
.map(|(a, b)| format!("{:?}", a.packages_folder(b)))
|
||||||
|
.collect::<BTreeSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let prefix = String::new();
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let prefix = "#!/usr/bin/env -S lune run\n";
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"{prefix}local process = require("@lune/process")
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
|
||||||
|
for _, packages_folder in {{ {all_folders} }} do
|
||||||
|
local path = `{{process.cwd}}/{{packages_folder}}/{alias}.bin.luau`
|
||||||
|
|
||||||
|
if fs.isFile(path) then
|
||||||
|
require(path)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl InstallCommand {
|
impl InstallCommand {
|
||||||
pub fn run(self, project: Project, multi: MultiProgress) -> anyhow::Result<()> {
|
pub fn run(self, project: Project, multi: MultiProgress) -> anyhow::Result<()> {
|
||||||
let mut refreshed_sources = HashSet::new();
|
let mut refreshed_sources = HashSet::new();
|
||||||
|
@ -120,6 +162,47 @@ impl InstallCommand {
|
||||||
.apply_patches(&downloaded_graph)
|
.apply_patches(&downloaded_graph)
|
||||||
.context("failed to apply patches")?;
|
.context("failed to apply patches")?;
|
||||||
|
|
||||||
|
let bin_folder = home_dir()?.join("bin");
|
||||||
|
|
||||||
|
for versions in downloaded_graph.values() {
|
||||||
|
for node in versions.values() {
|
||||||
|
if node.target.bin_path().is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some((alias, _)) = &node.node.direct else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let bin_file = bin_folder.join(format!("{alias}.luau"));
|
||||||
|
std::fs::write(&bin_file, bin_link_file(alias))
|
||||||
|
.context("failed to write bin link file")?;
|
||||||
|
|
||||||
|
// TODO: test if this actually works
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let mut perms = std::fs::metadata(&bin_file)
|
||||||
|
.context("failed to get bin link file metadata")?
|
||||||
|
.permissions();
|
||||||
|
perms.set_mode(perms.mode() | 0o111);
|
||||||
|
std::fs::set_permissions(&bin_file, perms)
|
||||||
|
.context("failed to set bin link file permissions")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let bin_file = bin_file.with_extension(std::env::consts::EXE_EXTENSION);
|
||||||
|
std::fs::copy(
|
||||||
|
std::env::current_exe().context("failed to get current executable path")?,
|
||||||
|
&bin_file,
|
||||||
|
)
|
||||||
|
.context("failed to copy bin link file")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
project
|
project
|
||||||
.write_lockfile(Lockfile {
|
.write_lockfile(Lockfile {
|
||||||
name: manifest.name,
|
name: manifest.name,
|
||||||
|
|
|
@ -149,15 +149,15 @@ pub fn reqwest_client(data_dir: &Path) -> anyhow::Result<reqwest::blocking::Clie
|
||||||
.build()?)
|
.build()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_scripts_folder(project: &Project) -> anyhow::Result<()> {
|
pub fn home_dir() -> anyhow::Result<std::path::PathBuf> {
|
||||||
let home_dir = directories::UserDirs::new()
|
Ok(directories::UserDirs::new()
|
||||||
.context("failed to get home directory")?
|
.context("failed to get home directory")?
|
||||||
.home_dir()
|
.home_dir()
|
||||||
.to_owned();
|
.join(concat!(".", env!("CARGO_PKG_NAME"))))
|
||||||
|
}
|
||||||
|
|
||||||
let scripts_dir = home_dir
|
pub fn update_scripts_folder(project: &Project) -> anyhow::Result<()> {
|
||||||
.join(concat!(".", env!("CARGO_PKG_NAME")))
|
let scripts_dir = home_dir()?.join("scripts");
|
||||||
.join("scripts");
|
|
||||||
|
|
||||||
if scripts_dir.exists() {
|
if scripts_dir.exists() {
|
||||||
let repo = gix::open(&scripts_dir).context("failed to open scripts repository")?;
|
let repo = gix::open(&scripts_dir).context("failed to open scripts repository")?;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use pesde::{manifest::target::Target, Project, MANIFEST_FILE_NAME, MAX_ARCHIVE_SIZE};
|
use pesde::{
|
||||||
|
manifest::target::Target, scripts::ScriptName, Project, MANIFEST_FILE_NAME, MAX_ARCHIVE_SIZE,
|
||||||
|
};
|
||||||
use std::path::Component;
|
use std::path::Component;
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -66,6 +68,26 @@ impl PublishCommand {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !manifest.includes.iter().any(|f| {
|
||||||
|
matches!(
|
||||||
|
f.to_lowercase().as_str(),
|
||||||
|
"readme" | "readme.md" | "readme.txt"
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
println!(
|
||||||
|
"{}: no README file in includes, consider adding one",
|
||||||
|
"warn".yellow().bold()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if manifest.includes.remove("default.project.json") {
|
||||||
|
println!(
|
||||||
|
"{}: default.project.json was in includes, this should be generated by the {} script upon dependants installation",
|
||||||
|
"warn".yellow().bold(),
|
||||||
|
ScriptName::RobloxSyncConfigGenerator
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (name, path) in [("lib path", lib_path), ("bin path", bin_path)] {
|
for (name, path) in [("lib path", lib_path), ("bin path", bin_path)] {
|
||||||
let Some(export_path) = path else { continue };
|
let Some(export_path) = path else { continue };
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::cli::update_scripts_folder;
|
use crate::cli::{home_dir, update_scripts_folder};
|
||||||
|
use anyhow::Context;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use pesde::Project;
|
use pesde::Project;
|
||||||
|
use std::fs::create_dir_all;
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
pub struct SelfInstallCommand {}
|
pub struct SelfInstallCommand {}
|
||||||
|
@ -9,6 +11,10 @@ impl SelfInstallCommand {
|
||||||
pub fn run(self, project: Project) -> anyhow::Result<()> {
|
pub fn run(self, project: Project) -> anyhow::Result<()> {
|
||||||
update_scripts_folder(&project)?;
|
update_scripts_folder(&project)?;
|
||||||
|
|
||||||
|
create_dir_all(home_dir()?.join("bin")).context("failed to create bin folder")?;
|
||||||
|
|
||||||
|
// TODO: add the bin folder to the PATH
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::path::{Component, Path};
|
use std::path::{Component, Path};
|
||||||
|
|
||||||
|
use crate::manifest::target::TargetKind;
|
||||||
use full_moon::{ast::luau::ExportedTypeDeclaration, visitors::Visitor};
|
use full_moon::{ast::luau::ExportedTypeDeclaration, visitors::Visitor};
|
||||||
|
use relative_path::RelativePathBuf;
|
||||||
use crate::manifest::target::Target;
|
|
||||||
|
|
||||||
struct TypeVisitor {
|
struct TypeVisitor {
|
||||||
types: Vec<String>,
|
types: Vec<String>,
|
||||||
|
@ -49,7 +49,7 @@ pub fn get_file_types(file: &str) -> Result<Vec<String>, Vec<full_moon::Error>>
|
||||||
Ok(visitor.types)
|
Ok(visitor.types)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_linking_module<I: IntoIterator<Item = S>, S: AsRef<str>>(
|
pub fn generate_lib_linking_module<I: IntoIterator<Item = S>, S: AsRef<str>>(
|
||||||
path: &str,
|
path: &str,
|
||||||
types: I,
|
types: I,
|
||||||
) -> String {
|
) -> String {
|
||||||
|
@ -64,27 +64,40 @@ pub fn generate_linking_module<I: IntoIterator<Item = S>, S: AsRef<str>>(
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_require_path(
|
fn luau_style_path(path: &Path) -> String {
|
||||||
target: &Target,
|
path.components()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, ct)| match ct {
|
||||||
|
Component::ParentDir => Some(if i == 0 {
|
||||||
|
".".to_string()
|
||||||
|
} else {
|
||||||
|
"..".to_string()
|
||||||
|
}),
|
||||||
|
Component::Normal(part) => Some(format!("{}", part.to_string_lossy())),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_lib_require_path(
|
||||||
|
target: &TargetKind,
|
||||||
base_dir: &Path,
|
base_dir: &Path,
|
||||||
|
lib_file: &RelativePathBuf,
|
||||||
destination_dir: &Path,
|
destination_dir: &Path,
|
||||||
use_new_structure: bool,
|
use_new_structure: bool,
|
||||||
) -> Result<String, errors::GetRequirePathError> {
|
) -> String {
|
||||||
let Some(lib_file) = target.lib_path() else {
|
|
||||||
return Err(errors::GetRequirePathError::NoLibPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = pathdiff::diff_paths(destination_dir, base_dir).unwrap();
|
let path = pathdiff::diff_paths(destination_dir, base_dir).unwrap();
|
||||||
let path = if !use_new_structure {
|
let path = if use_new_structure {
|
||||||
log::debug!("using old structure for require path");
|
log::debug!("using new structure for require path");
|
||||||
lib_file.to_path(path)
|
lib_file.to_path(path)
|
||||||
} else {
|
} else {
|
||||||
log::debug!("using new structure for require path");
|
log::debug!("using old structure for require path");
|
||||||
path
|
path
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "roblox")]
|
#[cfg(feature = "roblox")]
|
||||||
if matches!(target, Target::Roblox { .. }) {
|
if matches!(target, TargetKind::Roblox) {
|
||||||
let path = path
|
let path = path
|
||||||
.components()
|
.components()
|
||||||
.filter_map(|component| match component {
|
.filter_map(|component| match component {
|
||||||
|
@ -102,29 +115,23 @@ pub fn get_require_path(
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
return Ok(format!("script{path}"));
|
return format!("script{path}");
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = path
|
format!("{:?}", luau_style_path(&path))
|
||||||
.components()
|
|
||||||
.filter_map(|ct| match ct {
|
|
||||||
Component::ParentDir => Some("..".to_string()),
|
|
||||||
Component::Normal(part) => Some(format!("{}", part.to_string_lossy())),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("/");
|
|
||||||
|
|
||||||
Ok(format!("./{path}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod errors {
|
pub fn generate_bin_linking_module(path: &str) -> String {
|
||||||
use thiserror::Error;
|
format!("return require({path})")
|
||||||
|
}
|
||||||
#[derive(Debug, Error)]
|
|
||||||
#[non_exhaustive]
|
pub fn get_bin_require_path(
|
||||||
pub enum GetRequirePathError {
|
base_dir: &Path,
|
||||||
#[error("get require path called for target without a lib path")]
|
bin_file: &RelativePathBuf,
|
||||||
NoLibPath,
|
destination_dir: &Path,
|
||||||
}
|
) -> String {
|
||||||
|
let path = pathdiff::diff_paths(destination_dir, base_dir).unwrap();
|
||||||
|
let path = bin_file.to_path(path);
|
||||||
|
|
||||||
|
format!("{:?}", luau_style_path(&path))
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,14 @@ use std::{collections::BTreeMap, fs::create_dir_all};
|
||||||
|
|
||||||
pub mod generator;
|
pub mod generator;
|
||||||
|
|
||||||
|
fn create_and_canonicalize<P: AsRef<std::path::Path>>(
|
||||||
|
path: P,
|
||||||
|
) -> std::io::Result<std::path::PathBuf> {
|
||||||
|
let p = path.as_ref();
|
||||||
|
create_dir_all(p)?;
|
||||||
|
p.canonicalize()
|
||||||
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
pub fn link_dependencies(&self, graph: &DownloadedGraph) -> Result<(), errors::LinkingError> {
|
pub fn link_dependencies(&self, graph: &DownloadedGraph) -> Result<(), errors::LinkingError> {
|
||||||
let manifest = self.deser_manifest()?;
|
let manifest = self.deser_manifest()?;
|
||||||
|
@ -89,12 +97,12 @@ impl Project {
|
||||||
|
|
||||||
for (name, versions) in graph {
|
for (name, versions) in graph {
|
||||||
for (version_id, node) in versions {
|
for (version_id, node) in versions {
|
||||||
let base_folder = self.path().join(
|
let base_folder = create_and_canonicalize(
|
||||||
self.path()
|
self.path().join(
|
||||||
.join(node.node.base_folder(manifest.target.kind(), true)),
|
self.path()
|
||||||
);
|
.join(node.node.base_folder(manifest.target.kind(), true)),
|
||||||
create_dir_all(&base_folder)?;
|
),
|
||||||
let base_folder = base_folder.canonicalize()?;
|
)?;
|
||||||
let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME);
|
let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME);
|
||||||
|
|
||||||
let container_folder = node.node.container_folder(
|
let container_folder = node.node.container_folder(
|
||||||
|
@ -108,17 +116,36 @@ impl Project {
|
||||||
.and_then(|v| v.get(version_id))
|
.and_then(|v| v.get(version_id))
|
||||||
.and_then(|types| node.node.direct.as_ref().map(|(alias, _)| (alias, types)))
|
.and_then(|types| node.node.direct.as_ref().map(|(alias, _)| (alias, types)))
|
||||||
{
|
{
|
||||||
let module = generator::generate_linking_module(
|
if let Some(lib_file) = node.target.lib_path() {
|
||||||
&generator::get_require_path(
|
let linker_file = base_folder.join(format!("{alias}.luau"));
|
||||||
&node.target,
|
|
||||||
&base_folder,
|
|
||||||
&container_folder,
|
|
||||||
node.node.pkg_ref.use_new_structure(),
|
|
||||||
)?,
|
|
||||||
types,
|
|
||||||
);
|
|
||||||
|
|
||||||
std::fs::write(base_folder.join(format!("{alias}.luau")), module)?;
|
let module = generator::generate_lib_linking_module(
|
||||||
|
&generator::get_lib_require_path(
|
||||||
|
&node.target.kind(),
|
||||||
|
&linker_file,
|
||||||
|
lib_file,
|
||||||
|
&container_folder,
|
||||||
|
node.node.pkg_ref.use_new_structure(),
|
||||||
|
),
|
||||||
|
types,
|
||||||
|
);
|
||||||
|
|
||||||
|
std::fs::write(linker_file, module)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(bin_file) = node.target.bin_path() {
|
||||||
|
let linker_file = base_folder.join(format!("{alias}.bin.luau"));
|
||||||
|
|
||||||
|
let module = generator::generate_bin_linking_module(
|
||||||
|
&generator::get_bin_require_path(
|
||||||
|
&linker_file,
|
||||||
|
bin_file,
|
||||||
|
&container_folder,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
std::fs::write(linker_file, module)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (dependency_name, (dependency_version_id, dependency_alias)) in
|
for (dependency_name, (dependency_version_id, dependency_alias)) in
|
||||||
|
@ -134,26 +161,28 @@ impl Project {
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let dependency_container_folder = dependency_node.node.container_folder(
|
let Some(lib_file) = dependency_node.target.lib_path() else {
|
||||||
&packages_container_folder,
|
continue;
|
||||||
dependency_name,
|
};
|
||||||
dependency_version_id.version(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let linker_folder = container_folder
|
let linker_file = create_and_canonicalize(
|
||||||
.join(dependency_node.node.base_folder(node.target.kind(), false));
|
container_folder
|
||||||
create_dir_all(&linker_folder)?;
|
.join(dependency_node.node.base_folder(node.target.kind(), false)),
|
||||||
let linker_folder = linker_folder.canonicalize()?;
|
)?
|
||||||
|
.join(format!("{dependency_alias}.luau"));
|
||||||
|
|
||||||
let linker_file = linker_folder.join(format!("{dependency_alias}.luau"));
|
let module = generator::generate_lib_linking_module(
|
||||||
|
&generator::get_lib_require_path(
|
||||||
let module = generator::generate_linking_module(
|
&dependency_node.target.kind(),
|
||||||
&generator::get_require_path(
|
|
||||||
&dependency_node.target,
|
|
||||||
&linker_file,
|
&linker_file,
|
||||||
&dependency_container_folder,
|
lib_file,
|
||||||
|
&dependency_node.node.container_folder(
|
||||||
|
&packages_container_folder,
|
||||||
|
dependency_name,
|
||||||
|
dependency_version_id.version(),
|
||||||
|
),
|
||||||
node.node.pkg_ref.use_new_structure(),
|
node.node.pkg_ref.use_new_structure(),
|
||||||
)?,
|
),
|
||||||
package_types
|
package_types
|
||||||
.get(dependency_name)
|
.get(dependency_name)
|
||||||
.and_then(|v| v.get(dependency_version_id))
|
.and_then(|v| v.get(dependency_version_id))
|
||||||
|
@ -190,9 +219,6 @@ pub mod errors {
|
||||||
#[error("error parsing Luau script at {0}")]
|
#[error("error parsing Luau script at {0}")]
|
||||||
FullMoon(String, Vec<full_moon::Error>),
|
FullMoon(String, Vec<full_moon::Error>),
|
||||||
|
|
||||||
#[error("error generating require path")]
|
|
||||||
GetRequirePath(#[from] crate::linking::generator::errors::GetRequirePathError),
|
|
||||||
|
|
||||||
#[cfg(feature = "roblox")]
|
#[cfg(feature = "roblox")]
|
||||||
#[error("error generating roblox sync config for {0}")]
|
#[error("error generating roblox sync config for {0}")]
|
||||||
GenerateRobloxSyncConfig(String, #[source] std::io::Error),
|
GenerateRobloxSyncConfig(String, #[source] std::io::Error),
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -22,6 +22,27 @@ struct Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let exe = std::env::current_exe().expect("failed to get current executable path");
|
||||||
|
let exe_name = exe.with_extension("");
|
||||||
|
let exe_name = exe_name.file_name().unwrap();
|
||||||
|
|
||||||
|
if exe_name != env!("CARGO_BIN_NAME") {
|
||||||
|
let args = std::env::args_os();
|
||||||
|
|
||||||
|
let status = std::process::Command::new("lune")
|
||||||
|
.arg("run")
|
||||||
|
.arg(exe.with_extension("luau"))
|
||||||
|
.args(args.skip(1))
|
||||||
|
.current_dir(std::env::current_dir().unwrap())
|
||||||
|
.status()
|
||||||
|
.expect("failed to run lune");
|
||||||
|
|
||||||
|
std::process::exit(status.code().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let multi = {
|
let multi = {
|
||||||
let logger = pretty_env_logger::formatted_builder()
|
let logger = pretty_env_logger::formatted_builder()
|
||||||
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"))
|
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"))
|
||||||
|
|
Loading…
Reference in a new issue