Start better documentation + fix bug in g::source
This commit is contained in:
parent
9fe327a78a
commit
c5141fd19e
239
README.md
239
README.md
@ -1,3 +1,242 @@
|
|||||||
# g.bash
|
# g.bash
|
||||||
|
|
||||||
A Bash framework.
|
A Bash framework.
|
||||||
|
|
||||||
|
## Documentation (WIP)
|
||||||
|
|
||||||
|
### Global Helpers
|
||||||
|
|
||||||
|
- `g::eval [...command]` - Execute a command, but w/ in-framework logging
|
||||||
|
- `g::silence [...command]` - Execute a command, squashing all output
|
||||||
|
- `g::now` - Get a timestamp in ISO8061
|
||||||
|
- `g::bc [...args]` - Execute a `bc` math statement
|
||||||
|
- `g::awk [...inputs]` - Execute an `awk` print command
|
||||||
|
|
||||||
|
|
||||||
|
### Utilties (`g::util`)
|
||||||
|
|
||||||
|
- `isNumeric [value]` - Checks if a given value is a number with optional decimal and positive/negative
|
||||||
|
- `true` - A successful exit code/return value (`0`)
|
||||||
|
- `false` - A failure exit code/return value (`1`)
|
||||||
|
- `returnToEcho [...command]` - Execute a command that returns `true`/`false` and instead echo `1` on success, `0` otherwise
|
||||||
|
- `uuid` - Generate a UUID
|
||||||
|
- `uuid::underscore` - Generate a UUID (underscore-separated)
|
||||||
|
- `escape [value]` - Bash-escape a value for safe use in `eval` strings
|
||||||
|
- `realpath [path]` - A non-GNU real path resolver
|
||||||
|
- `trace [?message] [?skip = frames to exclude]` - Generate a stack trace with an optional message
|
||||||
|
|
||||||
|
|
||||||
|
### Strings (`g::str`)
|
||||||
|
|
||||||
|
- `quote [str]` - Surround a string in quotes
|
||||||
|
- `eval [...args]` - Execute a command and quote the output
|
||||||
|
- `length [str]` - Get the number of chars in a string
|
||||||
|
- `padLeft [str] [length] [pad character=' ']` - Left-pad a string to the given length
|
||||||
|
- `padRight [str] [length] [pad character=' ']` - Right-pad a string to the given length
|
||||||
|
- `padCenter [str] [length] [pad character=' ']` - Center-pad a string to the given length
|
||||||
|
- `substring [str] [startAt] [length]` - Get a substring
|
||||||
|
- `offset [str] [startAt]` - Drop the first `n` characters of a string
|
||||||
|
- `reverse [str]` - Reverse a string
|
||||||
|
- `startsWith [haystack] [needle]` - Check if the haystack starts with the needle
|
||||||
|
- `endsWith [haystack] [needle]` - Check if the haystack ends with the needle
|
||||||
|
- `trim [str]` - Trim the whitespace from either end of the string
|
||||||
|
- `indexOf [str] [substring]` - Print the character index where the `substring` appears, if any (returns `g::util::false` if no match)
|
||||||
|
- `replace [str] [find] [replace]` - Find-and-replace all occurrences in a string
|
||||||
|
- `replace::once [str] [find] [replace]` - Find-and-replace the first occurrence in a string
|
||||||
|
|
||||||
|
|
||||||
|
### Arrays (`g::arr`)
|
||||||
|
|
||||||
|
> Note: When passing an array as an argument to a function, it should be passed as `"${array[@]}"`
|
||||||
|
|
||||||
|
- `includes [value] [array]` - Checks if a given value exists in the array
|
||||||
|
- `assoc::hasKey [key] [array]` - Checks if an associative array has a given key
|
||||||
|
- `join [delimiter] [array]` - Implodes a list of values to a string using the given delimiter
|
||||||
|
|
||||||
|
|
||||||
|
### Math (`g::math`)
|
||||||
|
|
||||||
|
- Constants (`g::math::c`)
|
||||||
|
- `e` - Euler's constant
|
||||||
|
- `ln2` - Natural log of 2
|
||||||
|
- `ln10` - Natural log of 10
|
||||||
|
- `pi` - Pi
|
||||||
|
- `sqrt05` - Square root of 1/2
|
||||||
|
- `sqrt2` - Square root of 2
|
||||||
|
- `abs [num]` - Absolute value of a number
|
||||||
|
- `cubeRoot [num]` - Cube root of a number
|
||||||
|
- `squareRoot [num]` - Square root of a number
|
||||||
|
- `lessThan [left] [right]` - Check if `left < right`
|
||||||
|
- `greaterThan [left] [right]` - Check if `left > right`
|
||||||
|
- `equalTo [left] [right]` - Check if `left == right`, numerically
|
||||||
|
- `isPositive [num]` - Check if `num > 0`
|
||||||
|
- `isNegative [num]` - Check if `num < 0`
|
||||||
|
- `signOf [num]` - Prints the leading sign of a number (either `+` or `-`)
|
||||||
|
- `mod [num] [modulus]` - Compute the modulus of a number
|
||||||
|
- `ceiling [num]` - Round the number up to the nearest int
|
||||||
|
- `truncate [num]` - Truncate the number to an int
|
||||||
|
- `floor [num]` - Round the number down to the nearest int
|
||||||
|
- `exponent [num] [power]` - Compute `num^power`
|
||||||
|
- `cos [num]` - Cosine of a number
|
||||||
|
- `sine [num]` - Sine of a number
|
||||||
|
- `tan [num]` - Tangent of a number
|
||||||
|
- `ln [num]` - Natural log of a number
|
||||||
|
- `log10 [num]` - Log base 10 of a number
|
||||||
|
- `log2 [num]` - Log base 2 of a number
|
||||||
|
- `log [num] [base]` - Arbitrary base logarithm
|
||||||
|
- `random` - Get a random float
|
||||||
|
- `max [...nums]` - Get the max value of some numbers
|
||||||
|
- `min [...nums]` - Get the min value of some numbers
|
||||||
|
- `round [num] [precision]` - Round a number to the specified number of decimal points
|
||||||
|
|
||||||
|
### Files (`g::file`)
|
||||||
|
|
||||||
|
- `appendString [path] [string]` - Append the string contents to the end of a file
|
||||||
|
- `exists [path]` - Checks if a file exists
|
||||||
|
- `directoryExists [path]` - Checks if a directory exists
|
||||||
|
- `touch [path]` - Create a file if it does not exist
|
||||||
|
- `truncate [path]` - If a file exists, empty its contents
|
||||||
|
|
||||||
|
### Source Code (`g::source`)
|
||||||
|
|
||||||
|
`g::source` allows you to organize your scripts into multiple files, and perform filesystem-relative source imports. Similarly, it will prevent the same source file from being re-imported multiple times.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# index.bash
|
||||||
|
g::source src/setup
|
||||||
|
g::source src/logging
|
||||||
|
|
||||||
|
# src/setup.bash
|
||||||
|
g::source logging
|
||||||
|
|
||||||
|
# src/logging.bash
|
||||||
|
# This file is included once
|
||||||
|
```
|
||||||
|
|
||||||
|
- `g::source [path] [?up=1]` - Load a source file relative to the current script (must end in `.bash` and will only be loaded once)
|
||||||
|
- `resolve [path] [?up=1]` - Resolve the path of a source file relative to the current script
|
||||||
|
- `exists [path] [?up=1]` - Check if a source file exists relative to the current script
|
||||||
|
- `force [path] [?up=1]` - Resolve and load a source file, bypassing the cache
|
||||||
|
- `has [path] [?up=1]` - Check if the given source file has been loaded
|
||||||
|
|
||||||
|
### Configuration File (`g::config`)
|
||||||
|
|
||||||
|
Provides a simple key-value way of storing persistent configuration.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
echo "Last run: $(g::config::get last-run Never)"
|
||||||
|
g::config::set 'last-run' "$(g::now)"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `setDefault [name]` - Set the default name of the config file
|
||||||
|
- `getDefault` - Get the default name of the config file
|
||||||
|
- `init [?name]` - Make sure a config file exists
|
||||||
|
- `get [name] [?default value]` - Get a value from the default config file
|
||||||
|
- `get::forFile [file] [name] [?default value]` - Get a value from a non-default config file
|
||||||
|
- `set [name] [value]` - Store a value in the default config file
|
||||||
|
- `set::forFile [file] [name] [value]` - Store a value in a non-default config file
|
||||||
|
|
||||||
|
### Paths (`g::path`)
|
||||||
|
|
||||||
|
- `concat [path] [...parts]` - Combine path segments into a path, accounting for leading and trailing slashes
|
||||||
|
- `resolve [...parts]` - Concat path parts and determine the real path
|
||||||
|
- `resolveFrom [base dir] [...parts]` - Concat path parts and determine the real path relative to some base directory
|
||||||
|
- `cd [...parts]` - Resolve path parts and change directories
|
||||||
|
- `tmp` - Get a temp file path (and mark it for cleanup)
|
||||||
|
- `tmpdir` - Get a temp dir path (and mark it for cleanup)
|
||||||
|
|
||||||
|
### Errors (`g::error`)
|
||||||
|
|
||||||
|
- `throw [message]` - Throw an error
|
||||||
|
- (global) `try`
|
||||||
|
- (global) `catch`
|
||||||
|
|
||||||
|
### Logging (`g::log`)
|
||||||
|
|
||||||
|
`g::log` provides level- and target-aware logging output. The `internal` level is used by `g.bash` itself.
|
||||||
|
|
||||||
|
Available levels: `error` | `warn` | `info` | `debug` | `verbose` | `internal`
|
||||||
|
|
||||||
|
- `enableTarget [name]` - Enable log outputs for the given target
|
||||||
|
- `enableAllTargets` - Enable log outputs for ALL targets
|
||||||
|
- `all` - Shorthand to enable the highest verbosity for ALL targets
|
||||||
|
- `getLevel` - Get the current logging level
|
||||||
|
- `setLevel [name]` - Set a new logging level
|
||||||
|
- `enable [target=stdout|stderr|file] [?param]` - Enable logging to the specified target
|
||||||
|
- `disable [target=stdout|stderr|file]` - Disable logging to the specified target
|
||||||
|
- `g::error [output] [?target]` - Write a log message at the `error` level, optionally for a specific target
|
||||||
|
- Same format for `g::warn`, `g::info`, `g::debug`, `g::verbose`, and `g::internal`
|
||||||
|
- `g::log::rotate` - Archive the log file to a timestamped version and start a new one
|
||||||
|
|
||||||
|
### Locks (`g::lock`)
|
||||||
|
|
||||||
|
- `try [name]` - Try to acquire a lock, returning `g::util::true` if successful
|
||||||
|
- `acquire [name]` - Acquire a lock, sleeping until it is available
|
||||||
|
- `holds [name]` - Check if we currently hold the given lock
|
||||||
|
- `release [name]` - Release the given lock if held
|
||||||
|
- `singleton` - Throw an error if this script is being executed concurrently
|
||||||
|
- `singleton::acquire` - Wait for other instances of this script to finish
|
||||||
|
|
||||||
|
### App Framework (`g::app`)
|
||||||
|
|
||||||
|
`g::app` is a way of defining multi-directive CLI applications with argument/option parsing.
|
||||||
|
|
||||||
|
Each application is broken up into "commands," and each command can have multiple arguments/flags.
|
||||||
|
|
||||||
|
Your source code can provide multiple applications (switching between them using `g::app`) with app-scoped storage.
|
||||||
|
|
||||||
|
- `g::app [?name=app] [?description]` - Create a new top-level application
|
||||||
|
- `get [name] [?default value]` - Get the value of an app-scoped variable
|
||||||
|
- `set [name] [value]` - Set the value of an app-scoped variable
|
||||||
|
- `has [name]` - Check if the app-scope has a variable with the given name
|
||||||
|
- `usage` - Print usage information for the current application
|
||||||
|
- `invoke [command] [...args]` - Invoke a command in the current application
|
||||||
|
|
||||||
|
#### Defining Commands (`g::app::command`)
|
||||||
|
|
||||||
|
A command is an invokable sub-command function for an application, which can have multiple flags and positional arguments associated with it.
|
||||||
|
|
||||||
|
When a command is executed, its arguments are parsed and a function `app::<app name>::<command name>` is called. Basic example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
g::app myapp "An example application"
|
||||||
|
|
||||||
|
g::app::command greet "Greeting command"
|
||||||
|
g::app::command::arg name "Name of the person to greet" "World"
|
||||||
|
g::app::command::flag greeting= "Override the greeting"
|
||||||
|
|
||||||
|
function app::myapp::greet() {
|
||||||
|
local args="$1"
|
||||||
|
name="$(g::arg "$args" name)"
|
||||||
|
greeting="$(g::flag "$args" greeting)"
|
||||||
|
if [ -z "$greeting" ]; then
|
||||||
|
greeting="Hello,"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$greeting $name"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `g::app::command [name] [?description]` - Start defining a new command in the current app
|
||||||
|
- `exists [name]` - Check if a given command exists in the current app
|
||||||
|
- `description [name]` - Get the description for a command in the current app
|
||||||
|
- `arg [name] [?description] [?default value]` - Add a positional argument to the current command
|
||||||
|
- `flag [name] [?description]` - Add a position-less flag option. If `name` trails with a `=`, then a value is expected
|
||||||
|
- `rest [name] [?description]` - Register a rest-argument (`[...args]`) for the current command
|
||||||
|
|
||||||
|
#### Accessing Arguments
|
||||||
|
|
||||||
|
When arguments for a command are parsed, they are stored in an "argument set." The argument set is a series of data structures containing the parsed/validated flags & positional arguments.
|
||||||
|
|
||||||
|
The argument set ID is passed to the command handler as the only parameter (`$1`) and can be used to look up the values of flags and arguments.
|
||||||
|
|
||||||
|
- `g::args [uuid]` - Get the raw arguments from a parsed set (akin to `$@`)
|
||||||
|
- `g::arg [uuid] [name]` - Get the value of a positional argument
|
||||||
|
- `g::arg::has [uuid] [name]` - Checks if a positional argument was provided
|
||||||
|
- `g::arg::rest [uuid]` - Get all the unmatched CLI arguments/flags
|
||||||
|
- `g::flag [uuid] [name]` - Get the value of a position-less flag
|
||||||
|
- `g::flag::has [uuid] [name]` - Checks if a position-less flag was specified
|
||||||
|
@ -1366,6 +1366,7 @@ function g::source() {
|
|||||||
local slug="$1"
|
local slug="$1"
|
||||||
local up="${2:-1}"
|
local up="${2:-1}"
|
||||||
|
|
||||||
|
local resolved="$(g::source::resolve "$slug" $(( up + 1 )))"
|
||||||
if ! g::source::has "$slug" $(( up + 1 )); then
|
if ! g::source::has "$slug" $(( up + 1 )); then
|
||||||
g::source::force "$slug" $(( up + 1 ))
|
g::source::force "$slug" $(( up + 1 ))
|
||||||
g__IMPORTED_FILES+=("$resolved")
|
g__IMPORTED_FILES+=("$resolved")
|
||||||
|
Loading…
Reference in New Issue
Block a user