Skip to content

Commit abf5dec

Browse files
committed
Add LuaHeaderMapExt trait
Add `Headers:to_json` Lua method
1 parent cbc3f21 commit abf5dec

File tree

5 files changed

+128
-36
lines changed

5 files changed

+128
-36
lines changed

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ vendored = ["mlua/vendored"]
2020
json = ["mlua/serde", "dep:ouroboros", "dep:serde", "dep:serde_json"]
2121
regex = ["dep:regex", "dep:ouroboros", "dep:quick_cache"]
2222
yaml = ["mlua/serde", "dep:ouroboros", "dep:serde", "dep:serde_yaml"]
23-
http = ["dep:http"]
23+
http = ["dep:http", "dep:http-serde-ext"]
2424
task = ["async"]
2525

2626
[dependencies]
27-
mlua = { version = "0.11" }
27+
bytes = "1"
28+
mlua = { version = "0.11", features = ["error-send"] }
2829
ouroboros = { version = "0.18", optional = true }
2930
serde = { version = "1.0", optional = true }
3031
serde_json = { version = "1.0", optional = true }
@@ -35,6 +36,7 @@ quick_cache = { version = "0.6", optional = true }
3536

3637
# http
3738
http = { version = "1.3", optional = true }
39+
http-serde-ext = { version = "1.0", optional = true }
3840

3941
# tokio
4042
tokio = { version = "1", features = ["full"], optional = true }

src/http/headers.rs

Lines changed: 96 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use mlua::{
55
};
66

77
#[derive(Clone)]
8-
pub(crate) struct Headers(HeaderMap);
8+
pub(crate) struct Headers(pub(crate) HeaderMap);
99

1010
impl UserData for Headers {
1111
fn register(registry: &mut mlua::UserDataRegistry<Self>) {
@@ -15,42 +15,27 @@ impl UserData for Headers {
1515
});
1616

1717
registry.add_method("get", |lua, this, name: LuaString| {
18-
let name = name.to_str()?;
19-
(this.0.get(&*name))
20-
.map(|v| lua.create_string(v.as_ref()))
21-
.transpose()
18+
LuaHeaderMapExt::get(&this.0, lua, &name)
2219
});
2320

2421
registry.add_method("get_all", |lua, this, name: LuaString| {
25-
let name = name.to_str()?;
26-
(this.0.get_all(&*name))
27-
.iter()
28-
.map(|v| lua.create_string(v.as_ref()))
29-
.collect::<Result<Vec<_>>>()
22+
LuaHeaderMapExt::get_all(&this.0, lua, &name)
3023
});
3124

3225
registry.add_method("get_count", |_, this, name: LuaString| {
33-
let name = name.to_str()?;
34-
Ok(this.0.get_all(&*name).iter().count())
26+
LuaHeaderMapExt::get_count(&this.0, &name)
3527
});
3628

3729
registry.add_method_mut("set", |_, this, (name, value): (LuaString, LuaString)| {
38-
let name = HeaderName::from_lua(name)?;
39-
let value = HeaderValue::from_lua(value)?;
40-
this.0.insert(name, value);
41-
Ok(())
30+
LuaHeaderMapExt::set(&mut this.0, &name, &value)
4231
});
4332

4433
registry.add_method_mut("add", |_, this, (name, value): (LuaString, LuaString)| {
45-
let name = HeaderName::from_lua(name)?;
46-
let value = HeaderValue::from_lua(value)?;
47-
Ok(this.0.append(name, value))
34+
LuaHeaderMapExt::add(&mut this.0, &name, &value)
4835
});
4936

5037
registry.add_method_mut("remove", |_, this, name: LuaString| {
51-
let name = HeaderName::from_lua(name)?;
52-
this.0.remove(&name);
53-
Ok(())
38+
LuaHeaderMapExt::remove(&mut this.0, &name)
5439
});
5540

5641
registry.add_method("count", |_, this, ()| Ok(this.0.len()));
@@ -95,6 +80,15 @@ impl UserData for Headers {
9580
Ok(table)
9681
});
9782

83+
#[cfg(feature = "json")]
84+
registry.add_method("to_json", |lua, this, ()| {
85+
let mut writer = Vec::new();
86+
// TODO: pretty, sorted
87+
let mut serializer = serde_json::Serializer::new(&mut writer);
88+
lua_try!(http_serde_ext::header_map::serialize(&this.0, &mut serializer));
89+
Ok(Ok(lua.create_string(writer)?))
90+
});
91+
9892
// Index
9993
registry.add_meta_method(MetaMethod::Index, |lua, this, key: LuaString| {
10094
let key = key.to_str()?;
@@ -108,19 +102,19 @@ impl UserData for Headers {
108102
registry.add_meta_method_mut(
109103
MetaMethod::NewIndex,
110104
|_, this, (key, value): (LuaString, Either<Option<LuaString>, Table>)| {
111-
let key = HeaderName::from_lua(key)?;
105+
let key = HeaderName::from_lua(&key)?;
112106
match value {
113107
Either::Left(None) => {
114108
this.0.remove(&key);
115109
}
116110
Either::Left(Some(v)) => {
117-
let value = HeaderValue::from_lua(v)?;
111+
let value = HeaderValue::from_lua(&v)?;
118112
this.0.insert(key, value);
119113
}
120114
Either::Right(t) => {
121115
this.0.remove(&key);
122116
for (i, v) in t.sequence_values::<LuaString>().enumerate() {
123-
let value = HeaderValue::from_lua(v?)?;
117+
let value = HeaderValue::from_lua(&v?)?;
124118
if i == 0 {
125119
this.0.insert(key.clone(), value);
126120
continue;
@@ -132,6 +126,12 @@ impl UserData for Headers {
132126
Ok(())
133127
},
134128
);
129+
130+
// Len
131+
registry.add_meta_method(MetaMethod::Len, |_, this, ()| Ok(this.0.len()));
132+
133+
#[cfg(feature = "luau")]
134+
registry.enable_namecall();
135135
}
136136
}
137137

@@ -141,15 +141,15 @@ impl FromLua for Headers {
141141
Value::Table(table) => {
142142
let mut headers = HeaderMap::new();
143143
table.for_each::<LuaString, Value>(|key, value| {
144-
let name = HeaderName::from_lua(key)?;
144+
let name = HeaderName::from_lua(&key)?;
145145
// Maybe `value` is a list of values
146146
if let Value::Table(values) = value {
147147
for value in values.sequence_values::<LuaString>() {
148-
headers.append(name.clone(), HeaderValue::from_lua(value?)?);
148+
headers.append(name.clone(), HeaderValue::from_lua(&value?)?);
149149
}
150150
} else {
151151
let value = lua.unpack::<LuaString>(value)?;
152-
headers.append(name, HeaderValue::from_lua(value)?);
152+
headers.append(name, HeaderValue::from_lua(&value)?);
153153
}
154154
Ok(())
155155
})?;
@@ -196,22 +196,84 @@ fn set_headers_metatable(lua: &Lua, headers: &Table) -> Result<()> {
196196
headers.set_metatable(Some(metatable))
197197
}
198198

199-
pub(crate) trait LuaHeaderExt {
200-
fn from_lua(value: LuaString) -> Result<Self>
199+
pub(crate) trait LuaHeaderValueExt {
200+
fn from_lua(value: &LuaString) -> Result<Self>
201201
where
202202
Self: Sized;
203203
}
204204

205-
impl LuaHeaderExt for HeaderName {
205+
impl LuaHeaderValueExt for HeaderName {
206206
#[inline]
207-
fn from_lua(value: LuaString) -> Result<Self> {
207+
fn from_lua(value: &LuaString) -> Result<Self> {
208208
HeaderName::from_bytes(&value.as_bytes()).into_lua_err()
209209
}
210210
}
211211

212-
impl LuaHeaderExt for HeaderValue {
212+
impl LuaHeaderValueExt for HeaderValue {
213213
#[inline]
214-
fn from_lua(value: LuaString) -> Result<Self> {
214+
fn from_lua(value: &LuaString) -> Result<Self> {
215215
HeaderValue::from_bytes(&value.as_bytes()).into_lua_err()
216216
}
217217
}
218+
219+
pub(crate) trait LuaHeaderMapExt {
220+
fn get(&self, lua: &Lua, name: &LuaString) -> Result<Option<LuaString>>;
221+
222+
fn get_all(&self, lua: &Lua, name: &LuaString) -> Result<Vec<LuaString>>;
223+
224+
fn get_count(&self, name: &LuaString) -> Result<usize>;
225+
226+
fn set(&mut self, name: &LuaString, value: &LuaString) -> Result<()>;
227+
228+
fn add(&mut self, name: &LuaString, value: &LuaString) -> Result<()>;
229+
230+
fn remove(&mut self, name: &LuaString) -> Result<()>;
231+
}
232+
233+
impl LuaHeaderMapExt for HeaderMap {
234+
#[inline]
235+
fn get(&self, lua: &Lua, name: &LuaString) -> Result<Option<LuaString>> {
236+
let name = name.to_str()?;
237+
self.get(&*name)
238+
.map(|v| lua.create_string(v.as_ref()))
239+
.transpose()
240+
}
241+
242+
#[inline]
243+
fn get_all(&self, lua: &Lua, name: &LuaString) -> Result<Vec<LuaString>> {
244+
let name = name.to_str()?;
245+
self.get_all(&*name)
246+
.iter()
247+
.map(|v| lua.create_string(v.as_ref()))
248+
.collect()
249+
}
250+
251+
#[inline]
252+
fn get_count(&self, name: &LuaString) -> Result<usize> {
253+
let name = name.to_str()?;
254+
Ok(self.get_all(&*name).iter().count())
255+
}
256+
257+
#[inline]
258+
fn set(&mut self, name: &LuaString, value: &LuaString) -> Result<()> {
259+
let name = HeaderName::from_lua(name)?;
260+
let value = HeaderValue::from_lua(value)?;
261+
self.insert(name, value);
262+
Ok(())
263+
}
264+
265+
#[inline]
266+
fn add(&mut self, name: &LuaString, value: &LuaString) -> Result<()> {
267+
let name = HeaderName::from_lua(name)?;
268+
let value = HeaderValue::from_lua(value)?;
269+
self.append(name, value);
270+
Ok(())
271+
}
272+
273+
#[inline]
274+
fn remove(&mut self, name: &LuaString) -> Result<()> {
275+
let name = HeaderName::from_lua(name)?;
276+
self.remove(&name);
277+
Ok(())
278+
}
279+
}

src/http/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use mlua::{Lua, Result, Table};
22

3+
pub(crate) use headers::{Headers, LuaHeaderMapExt};
4+
35
/// A loader for the `http` module.
46
fn loader(lua: &Lua) -> Result<Table> {
57
let t = lua.create_table()?;

src/macros.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,18 @@ macro_rules! opt_param {
2929
}
3030
}
3131
};
32+
33+
($ty:ty, $table:expr, $name:expr) => {
34+
match ($table.as_ref())
35+
.map(|t| t.raw_get::<Option<$ty>>($name))
36+
.transpose()
37+
{
38+
Ok(Some(v)) => Ok(v),
39+
Ok(None) => Ok(None),
40+
Err(err) => {
41+
use mlua::ErrorContext as _;
42+
Err(err.with_context(|_| format!("invalid `{}`", $name)))
43+
}
44+
}
45+
};
3246
}

tests/lua/http/headers_tests.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,15 @@ testing:test("Headers errors", function(t)
104104
t.assert_eq(ok, false)
105105
t.assert_match(err, "failed to parse header value")
106106
end)
107+
108+
testing:test("Headers to_json", function(t)
109+
local headers = http.Headers.new({
110+
["Set-Cookie"] = { "id=123", "token=abc" },
111+
})
112+
if headers.to_json == nil then
113+
t.skip("`json` feature is not enabled")
114+
return
115+
end
116+
local json = headers:to_json()
117+
t.assert_eq(json, '{"set-cookie":["id=123","token=abc"]}')
118+
end)

0 commit comments

Comments
 (0)