mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Add support for absolute file paths in the CLI
This commit is contained in:
parent
588ae96d66
commit
8e168d3a9d
4 changed files with 140 additions and 32 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -8,6 +8,21 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added support for running scripts by passing absolute file paths in the CLI
|
||||||
|
- This does not have the restriction of scripts having to use the `.luau` or `.lua` extension, since it is presumed that if you pass an absolute path you know exactly what you are doing
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved error messages for passing invalid file names / file paths substantially - they now include helpful formatting to make file names distinct from file extensions, and give suggestions on how to solve the problem
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed the CLI being a bit too picky about file names when trying to run files in `lune` or `.lune` directories
|
||||||
|
|
||||||
## `0.5.4` - March 7th, 2023
|
## `0.5.4` - March 7th, 2023
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -46,4 +46,4 @@ Runs a script passed to Lune using stdin. Occasionally useful for running script
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**_<sup>[1]</sup>_** _Lune also supports files with the `.lua` extension but using the `.luau` extension is highly recommended. Additionally, if you don't want Lune to look in sub-directories you can provide a full file path with the file extension included, instead of only the file name._
|
**_<sup>[1]</sup>_** _Lune also supports files with the `.lua` extension but using the `.luau` extension is highly recommended. Additionally, if you don't want Lune to look in sub-directories or try to find files with `.lua` / `.luau` extensions at all, you can provide an absolute file path. This will disable all file path parsing and checks, and just run the file directly._
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
generate_selene_defs_from_definitions, generate_wiki_dir_from_definitions,
|
generate_selene_defs_from_definitions, generate_wiki_dir_from_definitions,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
files::find_parse_file_path,
|
files::discover_script_file_path_including_lune_dirs,
|
||||||
listing::{find_lune_scripts, print_lune_scripts, sort_lune_scripts},
|
listing::{find_lune_scripts, print_lune_scripts, sort_lune_scripts},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -168,7 +168,7 @@ impl Cli {
|
||||||
.context("Failed to read script contents from stdin")?;
|
.context("Failed to read script contents from stdin")?;
|
||||||
("stdin".to_string(), stdin_contents)
|
("stdin".to_string(), stdin_contents)
|
||||||
} else {
|
} else {
|
||||||
let file_path = find_parse_file_path(&script_path)?;
|
let file_path = discover_script_file_path_including_lune_dirs(&script_path)?;
|
||||||
let file_contents = read_to_vec(&file_path).await?;
|
let file_contents = read_to_vec(&file_path).await?;
|
||||||
// NOTE: We skip the extension here to remove it from stack traces
|
// NOTE: We skip the extension here to remove it from stack traces
|
||||||
let file_display_name = file_path.with_extension("").display().to_string();
|
let file_display_name = file_path.with_extension("").display().to_string();
|
||||||
|
|
|
@ -1,43 +1,136 @@
|
||||||
use std::path::{PathBuf, MAIN_SEPARATOR};
|
use std::{
|
||||||
|
fs::Metadata,
|
||||||
|
path::{PathBuf, MAIN_SEPARATOR},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use console::style;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
const LUNE_COMMENT_PREFIX: &str = "-->";
|
const LUNE_COMMENT_PREFIX: &str = "-->";
|
||||||
|
|
||||||
pub fn find_luau_file_path(path: &str) -> Option<PathBuf> {
|
lazy_static! {
|
||||||
|
static ref ERR_MESSAGE_HELP_NOTE: String = format!(
|
||||||
|
"To run this file, either:\n{}\n{}",
|
||||||
|
format_args!(
|
||||||
|
"{} rename it to use a {} or {} extension",
|
||||||
|
style("-").dim(),
|
||||||
|
style(".luau").blue(),
|
||||||
|
style(".lua").blue()
|
||||||
|
),
|
||||||
|
format_args!(
|
||||||
|
"{} pass it as an absolute path instead of relative",
|
||||||
|
style("-").dim()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Discovers a script file path based on a given script name.
|
||||||
|
|
||||||
|
Script discovery is done in several steps here for the best possible user experience:
|
||||||
|
|
||||||
|
1. If we got a file that definitely exists, make sure it is either
|
||||||
|
- using an absolute path
|
||||||
|
- has the lua or luau extension
|
||||||
|
2. If we got a directory, let the user know
|
||||||
|
3. If we got an absolute path, don't check any extensions, just let the user know it didn't exist
|
||||||
|
4. If we got a relative path with no extension, also look for a file with a lua or luau extension
|
||||||
|
5. No other options left, the file simply did not exist
|
||||||
|
|
||||||
|
This behavior ensures that users can do pretty much whatever they want if they pass in an absolute
|
||||||
|
path, and that they then have control over script discovery behavior, whereas if they pass in
|
||||||
|
a relative path we will instead try to be as permissive as possible for user-friendliness
|
||||||
|
*/
|
||||||
|
pub fn discover_script_file_path(path: &str) -> Result<PathBuf> {
|
||||||
let file_path = PathBuf::from(path);
|
let file_path = PathBuf::from(path);
|
||||||
if let Some(ext) = file_path.extension() {
|
// NOTE: We use metadata directly here to try to
|
||||||
|
// avoid accessing the file path more than once
|
||||||
|
let file_meta = file_path.metadata();
|
||||||
|
let is_file = file_meta.as_ref().map_or(false, Metadata::is_file);
|
||||||
|
let is_dir = file_meta.as_ref().map_or(false, Metadata::is_dir);
|
||||||
|
let is_abs = file_path.is_absolute();
|
||||||
|
let ext = file_path.extension();
|
||||||
|
if is_file {
|
||||||
|
if is_abs {
|
||||||
|
Ok(file_path)
|
||||||
|
} else if let Some(ext) = file_path.extension() {
|
||||||
match ext {
|
match ext {
|
||||||
e if e == "lua" || e == "luau" && file_path.exists() => Some(file_path),
|
e if e == "lua" || e == "luau" => Ok(file_path),
|
||||||
_ => None,
|
_ => Err(anyhow!(
|
||||||
|
"A file was found at {} but it uses the '{}' file extension\n{}",
|
||||||
|
style(file_path.display()).green(),
|
||||||
|
style(ext.to_string_lossy()).blue(),
|
||||||
|
*ERR_MESSAGE_HELP_NOTE
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let file_path_lua = PathBuf::from(path).with_extension("lua");
|
Err(anyhow!(
|
||||||
if file_path_lua.exists() {
|
"A file was found at {} but it has no file extension\n{}",
|
||||||
Some(file_path_lua)
|
style(file_path.display()).green(),
|
||||||
} else {
|
*ERR_MESSAGE_HELP_NOTE
|
||||||
let file_path_luau = PathBuf::from(path).with_extension("luau");
|
))
|
||||||
if file_path_luau.exists() {
|
|
||||||
Some(file_path_luau)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
} else if is_dir {
|
||||||
|
Err(anyhow!(
|
||||||
|
"No file was found at {}, found a directory",
|
||||||
|
style(file_path.display()).yellow()
|
||||||
|
))
|
||||||
|
} else if is_abs {
|
||||||
|
Err(anyhow!(
|
||||||
|
"No file was found at {}",
|
||||||
|
style(file_path.display()).yellow()
|
||||||
|
))
|
||||||
|
} else if ext.is_none() {
|
||||||
|
let file_path_lua = file_path.with_extension("lua");
|
||||||
|
let file_path_luau = file_path.with_extension("luau");
|
||||||
|
if file_path_lua.is_file() {
|
||||||
|
Ok(file_path_lua)
|
||||||
|
} else if file_path_luau.is_file() {
|
||||||
|
Ok(file_path_luau)
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"No file was found at {}",
|
||||||
|
style(file_path.display()).yellow()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"No file was found at {}",
|
||||||
|
style(file_path.display()).yellow()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_parse_file_path(path: &str) -> Result<PathBuf> {
|
/**
|
||||||
let parsed_file_path = find_luau_file_path(path)
|
Discovers a script file path based on a given script name, and tries to
|
||||||
.or_else(|| find_luau_file_path(&format!("lune{MAIN_SEPARATOR}{path}")))
|
find scripts in `lune` and `.lune` folders if one was not directly found.
|
||||||
.or_else(|| find_luau_file_path(&format!(".lune{MAIN_SEPARATOR}{path}")));
|
|
||||||
if let Some(file_path) = parsed_file_path {
|
Note that looking in `lune` and `.lune` folders is automatically
|
||||||
if file_path.exists() {
|
disabled if the given script name is an absolute path.
|
||||||
Ok(file_path)
|
|
||||||
} else {
|
Behavior is otherwise exactly the same as for `discover_script_file_path`.
|
||||||
bail!("File does not exist at path: '{}'", path)
|
*/
|
||||||
|
pub fn discover_script_file_path_including_lune_dirs(path: &str) -> Result<PathBuf> {
|
||||||
|
match discover_script_file_path(path) {
|
||||||
|
Ok(path) => Ok(path),
|
||||||
|
Err(e) => {
|
||||||
|
// If we got any absolute path it means the user has also
|
||||||
|
// told us to not look in any special relative directories
|
||||||
|
// so we should error right away with the first err message
|
||||||
|
if PathBuf::from(path).is_absolute() {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
// Otherwise we take a look in either relative lune or .lune directories
|
||||||
|
let res_lune = discover_script_file_path(&format!("lune{MAIN_SEPARATOR}{path}"));
|
||||||
|
let res_dotlune = discover_script_file_path(&format!(".lune{MAIN_SEPARATOR}{path}"));
|
||||||
|
match res_lune.or(res_dotlune) {
|
||||||
|
// NOTE: The first error message is generally more
|
||||||
|
// descriptive than the ones for the lune subfolders
|
||||||
|
Err(_) => Err(e),
|
||||||
|
Ok(path) => Ok(path),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
bail!("Invalid file path: '{}'", path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue