Version 0.5.1

This commit is contained in:
Filip Tibell 2023-02-25 13:41:50 +01:00
parent c83917206f
commit 7d75d5c52f
No known key found for this signature in database
9 changed files with 313 additions and 41 deletions

View file

@ -8,6 +8,37 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## `0.5.1` - February 25th, 2023
### Added
- Added `net.encode` and `net.decode` which are equivalent to `net.jsonEncode` and `net.jsonDecode`, but with support for more formats.
**_WARNING: Unstable API_**
_This API is unstable and may change or be removed in the next major version of Lune. The purpose of making a new release with these functions is to gather feedback from the community, and potentially replace the JSON-specific encoding and decoding utilities._
Example usage:
```lua
local toml = net.decode("toml", [[
[package]
name = "my-cool-toml-package"
version = "0.1.0"
[values]
epic = true
]])
assert(toml.package.name == "my-cool-toml-package")
assert(toml.package.version == "0.1.0")
assert(toml.values.epic == true)
```
### Fixed
- Fixed indentation of closing curly bracket when printing tables
## `0.5.0` - February 23rd, 2023
### Added

54
Cargo.lock generated
View file

@ -797,6 +797,7 @@ dependencies = [
"serde_yaml",
"tokio",
"tokio-tungstenite",
"toml",
]
[[package]]
@ -1276,6 +1277,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@ -1510,6 +1520,41 @@ dependencies = [
"tracing",
]
[[package]]
name = "toml"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "tower-service"
version = "0.3.2"
@ -1858,6 +1903,15 @@ version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
name = "winnow"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c06e7dbfe731192c512aa707249279d720876a868eb08f21ea93eb9de1eca9"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"

View file

@ -2,6 +2,8 @@
members = ["packages/cli", "packages/lib"]
default-members = ["packages/cli"]
# Package config values shared across all packages,
# such as version, license, and other metadata
[workspace.package]
version = "0.5.0"
edition = "2021"
@ -12,23 +14,32 @@ readme = "README.md"
keywords = ["cli", "lua", "luau", "scripts"]
categories = ["command-line-interface"]
# Shared dependencies that are used across 2 or more packages
# These are declared here to ensure consistent versioning
[workspace.dependencies]
console = "0.15"
futures-util = "0.3"
lazy_static = "1.4"
# Serde dependencies, supporting user-facing formats: json, yaml, toml
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_yaml = "0.9"
toml = { version = "0.7", features = ["preserve_order"] }
# Tokio runtime & async clients
tokio = { version = "1.24", features = ["full"] }
reqwest = { version = "0.11", default-features = false, features = [
"rustls-tls",
] }
[workspace.dependencies.reqwest]
version = "0.11"
default-features = false
features = ["rustls-tls"]
# Profile for building the release binary, with the following options set:
# 1. Optimize for size
# 2. Automatically strip symbols from the binary
# 3. Enable link-time optimization
# 4. Remove extra panic info
[profile.release]
strip = true # Automatically strip symbols from the binary.
opt-level = "z" # Optimize for size.
lto = true # Enable link-time optimization
panic = "abort" # Remove extra panic info
opt-level = "z"
strip = true
lto = true
panic = "abort"

View file

@ -14,7 +14,7 @@ export type FsWriteOptions = {
--[=[
@class FS
Filesystem
]=]
declare fs: {
@ -40,7 +40,7 @@ declare fs: {
@must_use
Reads entries in a directory at `path`.
An error will be thrown in the following situations:
* `path` does not point to an existing directory.
@ -146,9 +146,9 @@ declare fs: {
Throws an error if a file or directory already exists at the target path.
This can be bypassed by passing `true` as the third argument, or a dictionary of options.
Refer to the documentation for `FsWriteOptions` for specific option keys and their values.
An error will be thrown in the following situations:
* The current process lacks permissions to read at `from` or write at `to`.
* The new path exists on a different mount point.
* Some other I/O error occurred.
@ -162,6 +162,8 @@ declare fs: {
type NetMethod = "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS" | "PATCH"
type NetEncodeDecodeFormat = "json" | "yaml" | "toml"
--[=[
@type NetFetchParams
@within Net
@ -231,7 +233,7 @@ export type NetRequest = {
--[=[
@type NetRequest
@within Net
Response type for requests in `net.serve`.
This is a dictionary that may contain one or more of the following values:
@ -280,7 +282,7 @@ export type NetServeHandle = {
@within Net
A reference to a web socket connection.
The web socket may be in either an "open" or a "closed" state, changing its current behavior.
When open:
@ -311,7 +313,7 @@ declare net: {
@within Net
Sends an HTTP request using the given url and / or parameters, and returns a dictionary that describes the response received.
Only throws an error if a miscellaneous network or I/O error occurs, never for unsuccessful status codes.
@param config The URL or request config to use
@ -323,7 +325,7 @@ declare net: {
@must_use
Connects to a web socket at the given URL.
Throws an error if the server at the given URL does not support
web sockets, or if a miscellaneous network or I/O error occurs.
@ -364,6 +366,37 @@ declare net: {
@return The decoded lua value
]=]
jsonDecode: (encoded: string) -> any,
--[=[
@within Net
@must_use
***WARNING:** Unstable API*
*This API is unstable and may change or be removed in the next major version of Lune.*
Encodes the given value using the given format.
@param format The format to use
@param value The value to encode
@param pretty If the encoded string should be human-readable, including things such as newlines and spaces. Only supported for json and toml formats, and defaults to false
@return The encoded string
]=]
encode: (format: NetEncodeDecodeFormat, value: any, pretty: boolean?) -> string,
--[=[
@within Net
@must_use
***WARNING:** Unstable API*
*This API is unstable and may change or be removed in the next major version of Lune.*
Decodes the given string using the given format into a lua value.
@param format The format to use
@param encoded The JSON string to decode
@return The decoded lua value
]=]
decode: (format: NetEncodeDecodeFormat, encoded: string) -> any,
}
type ProcessSpawnOptionsStdio = "inherit" | "default"
@ -415,9 +448,9 @@ declare process: {
--[=[
@within Process
@read_only
The current operating system being used.
Possible values:
* `"linux"`
@ -428,9 +461,9 @@ declare process: {
--[=[
@within Process
@read_only
The architecture of the processor currently being used.
Possible values:
* `"x86_64"`
@ -468,13 +501,13 @@ declare process: {
Exit code 0 is treated as a successful exit, any other value is treated as an error.
Setting the exit code using this function will override any otherwise automatic exit code.
@param code The exit code to set
]=]
exit: (code: number?) -> (),
--[=[
@within Process
Spawns a child process that will run the program `program`, and returns a dictionary that describes the final status and ouput of the child process.
The second argument, `params`, can be passed as a list of string parameters to give to the program.
@ -497,7 +530,7 @@ declare process: {
--[=[
@class Stdio
Standard input / output & utility functions
Standard input / output & utility functions
]=]
declare stdio: {
--[=[
@ -507,9 +540,9 @@ declare stdio: {
Return an ANSI string that can be used to modify the persistent output color.
Pass `"reset"` to get a string that can reset the persistent output color.
### Example usage
```lua
stdio.write(stdio.color("red"))
print("This text will be red")
@ -528,9 +561,9 @@ declare stdio: {
Return an ANSI string that can be used to modify the persistent output style.
Pass `"reset"` to get a string that can reset the persistent output style.
### Example usage
```lua
stdio.write(stdio.style("bold"))
print("This text will be bold")
@ -629,7 +662,7 @@ declare task: {
@within Task
Instantly runs a thread or function.
If the spawned task yields, the thread that spawned the task
will resume, letting the spawned task run in the background.

View file

@ -21,6 +21,7 @@ lazy_static.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_yaml.workspace = true
toml.workspace = true
tokio.workspace = true
reqwest.workspace = true

View file

@ -8,8 +8,8 @@ use tokio::{sync::mpsc, task};
use crate::lua::{
net::{
NetClient, NetClientBuilder, NetLocalExec, NetService, NetWebSocket, RequestConfig,
ServeConfig,
EncodeDecodeConfig, EncodeDecodeFormat, NetClient, NetClientBuilder, NetLocalExec,
NetService, NetWebSocket, RequestConfig, ServeConfig,
},
table::TableBuilder,
task::{TaskScheduler, TaskSchedulerAsyncExt},
@ -25,6 +25,8 @@ pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
lua.set_named_registry_value("net.client", client)?;
// Create the global table for net
TableBuilder::new(lua)?
.with_function("encode", net_encode)?
.with_function("decode", net_decode)?
.with_function("jsonEncode", net_json_encode)?
.with_function("jsonDecode", net_json_decode)?
.with_async_function("request", net_request)?
@ -42,17 +44,32 @@ fn create_user_agent_header() -> String {
format!("{github_owner}-{github_repo}-cli")
}
fn net_json_encode(_: &'static Lua, (val, pretty): (LuaValue, Option<bool>)) -> LuaResult<String> {
if let Some(true) = pretty {
serde_json::to_string_pretty(&val).map_err(LuaError::external)
} else {
serde_json::to_string(&val).map_err(LuaError::external)
}
fn net_encode<'a>(
lua: &'static Lua,
(format, val, pretty): (EncodeDecodeFormat, LuaValue<'a>, Option<bool>),
) -> LuaResult<LuaString<'a>> {
let config = EncodeDecodeConfig::from((format, pretty.unwrap_or_default()));
config.serialize_to_string(lua, val)
}
fn net_json_decode(lua: &'static Lua, json: String) -> LuaResult<LuaValue> {
let json: serde_json::Value = serde_json::from_str(&json).map_err(LuaError::external)?;
lua.to_value(&json)
fn net_decode<'a>(
lua: &'static Lua,
(format, str): (EncodeDecodeFormat, LuaString<'a>),
) -> LuaResult<LuaValue<'a>> {
let config = EncodeDecodeConfig::from(format);
config.deserialize_from_string(lua, str)
}
fn net_json_encode<'a>(
lua: &'static Lua,
(val, pretty): (LuaValue<'a>, Option<bool>),
) -> LuaResult<LuaString<'a>> {
EncodeDecodeConfig::from((EncodeDecodeFormat::Json, pretty.unwrap_or_default()))
.serialize_to_string(lua, val)
}
fn net_json_decode<'a>(lua: &'static Lua, json: LuaString<'a>) -> LuaResult<LuaValue<'a>> {
EncodeDecodeConfig::from(EncodeDecodeFormat::Json).deserialize_from_string(lua, json)
}
async fn net_request<'a>(lua: &'static Lua, config: RequestConfig<'a>) -> LuaResult<LuaTable<'a>> {

View file

@ -1,9 +1,11 @@
mod client;
mod config;
mod response;
mod serde;
mod server;
mod websocket;
pub use self::serde::{EncodeDecodeConfig, EncodeDecodeFormat};
pub use client::{NetClient, NetClientBuilder};
pub use config::{RequestConfig, ServeConfig};
pub use response::{NetServeResponse, NetServeResponseKind};

View file

@ -0,0 +1,123 @@
use mlua::prelude::*;
use serde_json::Value as JsonValue;
use serde_yaml::Value as YamlValue;
use toml::Value as TomlValue;
// Serde config
#[derive(Debug, Clone, Copy)]
pub enum EncodeDecodeFormat {
Json,
Yaml,
Toml,
}
impl<'lua> FromLua<'lua> for EncodeDecodeFormat {
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
if let LuaValue::String(s) = &value {
match s.to_string_lossy().to_ascii_lowercase().trim() {
"json" => Ok(Self::Json),
"yaml" => Ok(Self::Yaml),
"toml" => Ok(Self::Toml),
kind => Err(LuaError::FromLuaConversionError {
from: value.type_name(),
to: "EncodeDecodeFormat",
message: Some(format!(
"Invalid format '{kind}', valid formats are: json, yaml, toml"
)),
}),
}
} else {
Err(LuaError::FromLuaConversionError {
from: value.type_name(),
to: "EncodeDecodeFormat",
message: None,
})
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct EncodeDecodeConfig {
pub format: EncodeDecodeFormat,
pub pretty: bool,
}
impl EncodeDecodeConfig {
pub fn serialize_to_string<'lua>(
self,
lua: &'lua Lua,
value: LuaValue<'lua>,
) -> LuaResult<LuaString<'lua>> {
let bytes = match self.format {
EncodeDecodeFormat::Json => {
if self.pretty {
serde_json::to_vec_pretty(&value).map_err(LuaError::external)?
} else {
serde_json::to_vec(&value).map_err(LuaError::external)?
}
}
EncodeDecodeFormat::Yaml => {
let mut writer = Vec::with_capacity(128);
serde_yaml::to_writer(&mut writer, &value).map_err(LuaError::external)?;
writer
}
EncodeDecodeFormat::Toml => {
let s = if self.pretty {
toml::to_string_pretty(&value).map_err(LuaError::external)?
} else {
toml::to_string(&value).map_err(LuaError::external)?
};
s.as_bytes().to_vec()
}
};
lua.create_string(&bytes)
}
pub fn deserialize_from_string<'lua>(
self,
lua: &'lua Lua,
string: LuaString<'lua>,
) -> LuaResult<LuaValue<'lua>> {
let bytes = string.as_bytes();
match self.format {
EncodeDecodeFormat::Json => {
let value: JsonValue = serde_json::from_slice(bytes).map_err(LuaError::external)?;
lua.to_value(&value)
}
EncodeDecodeFormat::Yaml => {
let value: YamlValue = serde_yaml::from_slice(bytes).map_err(LuaError::external)?;
lua.to_value(&value)
}
EncodeDecodeFormat::Toml => {
if let Ok(s) = string.to_str() {
let value: TomlValue = toml::from_str(s).map_err(LuaError::external)?;
lua.to_value(&value)
} else {
Err(LuaError::RuntimeError(
"TOML must be valid utf-8".to_string(),
))
}
}
}
}
}
impl From<EncodeDecodeFormat> for EncodeDecodeConfig {
fn from(format: EncodeDecodeFormat) -> Self {
Self {
format,
pretty: false,
}
}
}
impl From<(EncodeDecodeFormat, bool)> for EncodeDecodeConfig {
fn from(value: (EncodeDecodeFormat, bool)) -> Self {
Self {
format: value.0,
pretty: value.1,
}
}
}

View file

@ -156,7 +156,7 @@ pub fn pretty_format_value(
if is_empty {
write!(buffer, "{}", STYLE_DIM.apply_to(" }"))?;
} else {
write!(buffer, "\n{}", STYLE_DIM.apply_to("}"))?;
write!(buffer, "\n{depth_indent}{}", STYLE_DIM.apply_to("}"))?;
}
}
}