1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-26 16:34:23 +03:00

add wezterm.json_parse and wezterm.json_encode

This commit is contained in:
Wez Furlong 2022-07-10 09:30:54 -07:00
parent c8cb99a512
commit 4a4787ff41
8 changed files with 214 additions and 0 deletions

11
Cargo.lock generated
View File

@ -1096,6 +1096,7 @@ dependencies = [
"dirs-next",
"env_logger",
"filesystem",
"json",
"lazy_static",
"libc",
"log",
@ -1835,6 +1836,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "json"
version = "0.1.0"
dependencies = [
"anyhow",
"config",
"luahelper",
"serde_json",
]
[[package]]
name = "k9"
version = "0.11.5"

View File

@ -23,6 +23,7 @@ As features stabilize some brief notes about them will accumulate here.
* You may now use [wezterm.format](config/lua/wezterm/format.md) (or otherwise use strings with escape sequences) in the labels of the [Launcher Menu](config/launch.md#the-launcher-menu).
* You may now specify `assume_emoji_presentation = true` (or `false`) in [wezterm.font()](config/lua/wezterm/font.md) and [wezterm.font_with_fallback()](config/lua/wezterm/font_with_fallback.md)
* Wayland: `zwp_text_input_v3` is now supported, which enables IME to work in wezterm if your compositor also implements this protocol.
* New [wezterm.json_parse()](config/lua/wezterm/json_parse.md) and [wezterm.json_encode()](config/lua/wezterm/json_encode.md) functions for working with JSON.
#### Fixed
* [ActivateKeyTable](config/lua/keyassignment/ActivateKeyTable.md)'s `replace_current` field was not actually optional. Made it optional. [#2179](https://github.com/wez/wezterm/issues/2179)

View File

@ -0,0 +1,10 @@
# `wezterm.json_encode(value)`
*Since: nightly builds only*
Encodes the supplied lua value as json:
```
> wezterm.json_encode({foo = "bar"})
"{\"foo\":\"bar\"}"
```

View File

@ -0,0 +1,12 @@
# `wezterm.json_parse(string)`
*Since: nightly builds only*
Parses the supplied string as json and returns the equivalent lua values:
```
> wezterm.json_parse('{"foo":"bar"}')
{
"foo": "bar",
}
```

View File

@ -21,6 +21,7 @@ termwiz-funcs = { path = "../lua-api-crates/termwiz-funcs" }
logging = { path = "../lua-api-crates/logging" }
mux-lua = { path = "../lua-api-crates/mux" }
filesystem = { path = "../lua-api-crates/filesystem" }
json = { path = "../lua-api-crates/json" }
share-data = { path = "../lua-api-crates/share-data" }
ssh-funcs = { path = "../lua-api-crates/ssh-funcs" }
spawn-funcs = { path = "../lua-api-crates/spawn-funcs" }

View File

@ -165,6 +165,7 @@ fn register_lua_modules() {
logging::register,
mux_lua::register,
filesystem::register,
json::register,
ssh_funcs::register,
spawn_funcs::register,
share_data::register,

View File

@ -0,0 +1,12 @@
[package]
name = "json"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
config = { path = "../../config" }
luahelper = { path = "../../luahelper" }
serde_json = "1.0.82"

View File

@ -0,0 +1,166 @@
use config::lua::get_or_create_module;
use config::lua::mlua::{self, Lua, ToLua, Value as LuaValue};
use serde_json::{Map, Value as JValue};
use std::collections::HashSet;
pub fn register(lua: &Lua) -> anyhow::Result<()> {
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
wezterm_mod.set("json_parse", lua.create_function(json_parse)?)?;
wezterm_mod.set("json_encode", lua.create_function(json_encode)?)?;
Ok(())
}
fn json_value_to_lua_value<'lua>(lua: &'lua Lua, value: JValue) -> mlua::Result<LuaValue> {
Ok(match value {
JValue::Null => LuaValue::Nil,
JValue::Bool(b) => LuaValue::Boolean(b),
JValue::Number(n) => match n.as_i64() {
Some(n) => LuaValue::Integer(n),
None => match n.as_f64() {
Some(n) => LuaValue::Number(n),
None => {
return Err(mlua::Error::external(format!(
"cannot represent {n:#?} as either i64 or f64"
)))
}
},
},
JValue::String(s) => s.to_lua(lua)?,
JValue::Array(arr) => {
let tbl = lua.create_table_with_capacity(arr.len() as i32, 0)?;
for (idx, value) in arr.into_iter().enumerate() {
tbl.set(idx + 1, json_value_to_lua_value(lua, value)?)?;
}
LuaValue::Table(tbl)
}
JValue::Object(map) => {
let tbl = lua.create_table_with_capacity(0, map.len() as i32)?;
for (key, value) in map.into_iter() {
let key = key.to_lua(lua)?;
let value = json_value_to_lua_value(lua, value)?;
tbl.set(key, value)?;
}
LuaValue::Table(tbl)
}
})
}
fn json_parse<'lua>(lua: &'lua Lua, text: String) -> mlua::Result<LuaValue> {
let value =
serde_json::from_str(&text).map_err(|err| mlua::Error::external(format!("{err:#}")))?;
json_value_to_lua_value(lua, value)
}
fn lua_value_to_json_value(value: LuaValue, visited: &mut HashSet<usize>) -> mlua::Result<JValue> {
let ptr = value.to_pointer() as usize;
if visited.contains(&ptr) {
// Skip this one, as we've seen it before.
// Treat it as a Null value.
return Ok(JValue::Null);
}
visited.insert(ptr);
Ok(match value {
LuaValue::Nil => JValue::Null,
LuaValue::String(s) => JValue::String(s.to_str()?.to_string()),
LuaValue::Boolean(b) => JValue::Bool(b),
LuaValue::Integer(i) => JValue::Number(i.into()),
LuaValue::Number(i) => {
if let Some(n) = serde_json::value::Number::from_f64(i) {
JValue::Number(n)
} else {
return Err(mlua::Error::FromLuaConversionError {
from: "number",
to: "JsonValue",
message: Some(format!("unable to represent {i} as json float")),
});
}
}
// Handle our special Null userdata case and map it to Null
LuaValue::LightUserData(ud) if ud.0.is_null() => JValue::Null,
LuaValue::LightUserData(_) | LuaValue::UserData(_) => {
return Err(mlua::Error::FromLuaConversionError {
from: "userdata",
to: "JsonValue",
message: None,
})
}
LuaValue::Function(_) => {
return Err(mlua::Error::FromLuaConversionError {
from: "function",
to: "JsonValue",
message: None,
})
}
LuaValue::Thread(_) => {
return Err(mlua::Error::FromLuaConversionError {
from: "thread",
to: "JsonValue",
message: None,
})
}
LuaValue::Error(e) => return Err(e),
LuaValue::Table(table) => {
if let Ok(true) = table.contains_key(1) {
let mut array = vec![];
let pairs = table.clone();
for value in table.sequence_values() {
array.push(lua_value_to_json_value(value?, visited)?);
}
for pair in pairs.pairs::<LuaValue, LuaValue>() {
let (key, _value) = pair?;
match &key {
LuaValue::Integer(n) if *n >= 1 && *n as usize <= array.len() => {
// Ok!
}
_ => {
let type_name = key.type_name();
let key = luahelper::ValuePrinter(key);
return Err(mlua::Error::FromLuaConversionError {
from: type_name,
to: "numeric array index",
message: Some(format!(
"Unexpected key {key:?} for array style table"
)),
});
}
}
}
JValue::Array(array.into())
} else {
let mut obj = Map::default();
for pair in table.pairs::<LuaValue, LuaValue>() {
let (key, value) = pair?;
let key_type = key.type_name();
let key = match lua_value_to_json_value(key, visited)? {
JValue::String(s) => s,
_ => {
return Err(mlua::Error::FromLuaConversionError {
from: key_type,
to: "string",
message: Some("json object keys must be strings".to_string()),
});
}
};
let lua_type = value.type_name();
let value = lua_value_to_json_value(value, visited).map_err(|e| {
mlua::Error::FromLuaConversionError {
from: lua_type,
to: "value",
message: Some(format!("while processing {key:?}: {}", e.to_string())),
}
})?;
obj.insert(key, value);
}
JValue::Object(obj.into())
}
}
})
}
fn json_encode<'lua>(_: &'lua Lua, value: LuaValue) -> mlua::Result<String> {
let mut visited = HashSet::new();
let json = lua_value_to_json_value(value, &mut visited)?;
serde_json::to_string(&json).map_err(|err| mlua::Error::external(format!("{err:#}")))
}