• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Blightmud / Blightmud / 14316676040

07 Apr 2025 06:27PM UTC coverage: 74.417% (-0.9%) from 75.283%
14316676040

push

github

web-flow
Merge pull request #1202 from Blightmud/dependabot/cargo/rustls-0.23.25

build(deps): bump rustls from 0.23.23 to 0.23.25

7089 of 9526 relevant lines covered (74.42%)

431.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.69
/src/lua/blight.rs
1
use super::{constants::*, regex::Regex, ui_event::UiEvent};
2
use crate::event::{Event, QuitMethod};
3
use crate::{model::Line, PROJECT_NAME, VERSION};
4
use log::debug;
5
use mlua::{
6
    AnyUserData, FromLua, Function, Result as LuaResult, Table, UserData, UserDataMethods, Variadic,
7
};
8
use std::sync::mpsc::Sender;
9

10
#[derive(Clone, FromLua)]
×
11
pub struct Blight {
12
    main_writer: Sender<Event>,
13
    output_lines: Vec<Line>,
14
    ui_events: Vec<UiEvent>,
15
    pub screen_dimensions: (u16, u16),
16
    pub core_mode: bool,
17
    pub reader_mode: bool,
18
    pub tts_enabled: bool,
19
}
20

21
impl Blight {
22
    pub fn new(writer: Sender<Event>) -> Self {
185✔
23
        Self {
185✔
24
            main_writer: writer,
185✔
25
            output_lines: vec![],
185✔
26
            ui_events: vec![],
185✔
27
            screen_dimensions: (0, 0),
185✔
28
            core_mode: false,
185✔
29
            reader_mode: false,
185✔
30
            tts_enabled: false,
185✔
31
        }
185✔
32
    }
185✔
33

34
    pub fn core_mode(&mut self, mode: bool) {
336✔
35
        self.core_mode = mode;
336✔
36
    }
336✔
37

38
    pub fn get_output_lines(&mut self) -> Vec<Line> {
731✔
39
        let return_lines = self.output_lines.clone();
731✔
40
        self.output_lines.clear();
731✔
41
        return_lines
731✔
42
    }
731✔
43

44
    pub fn get_ui_events(&mut self) -> Vec<UiEvent> {
17✔
45
        let events = self.ui_events.clone();
17✔
46
        self.ui_events.clear();
17✔
47
        events
17✔
48
    }
17✔
49
}
50

51
impl UserData for Blight {
52
    fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
185✔
53
        methods.add_function("output", |ctx, strings: Variadic<String>| {
206✔
54
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
144✔
55
            let mut this = this_aux.borrow_mut::<Blight>()?;
144✔
56
            this.output_lines.push(Line::from(strings.join(" ")));
144✔
57
            Ok(())
144✔
58
        });
144✔
59
        methods.add_function("terminal_dimensions", |ctx, _: ()| {
185✔
60
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
2✔
61
            let this = this_aux.borrow::<Blight>()?;
2✔
62
            Ok(this.screen_dimensions)
2✔
63
        });
2✔
64
        methods.add_function("bind", |ctx, (cmd, callback): (String, mlua::Function)| {
4,461✔
65
            let bind_table: mlua::Table = ctx.named_registry_value(COMMAND_BINDING_TABLE)?;
4,461✔
66
            if cmd.to_lowercase().starts_with("alt-") {
4,461✔
67
                let (_, right) = cmd.split_at(3);
662✔
68
                let mut cmd = "alt".to_string();
662✔
69
                cmd.push_str(right);
662✔
70
                bind_table.set(cmd, callback)?;
662✔
71
            } else {
72
                bind_table.set(cmd.to_lowercase(), callback)?;
3,799✔
73
            }
74
            Ok(())
4,461✔
75
        });
4,461✔
76
        methods.add_function("unbind", |ctx, cmd: String| {
185✔
77
            let bind_table: mlua::Table = ctx.named_registry_value(COMMAND_BINDING_TABLE)?;
1✔
78
            bind_table.set(cmd, mlua::Nil)?;
1✔
79
            Ok(())
1✔
80
        });
1✔
81
        methods.add_function("ui", |ctx, cmd: String| -> mlua::Result<()> {
185✔
82
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
21✔
83
            let mut this = this_aux.borrow_mut::<Blight>()?;
21✔
84
            let event: UiEvent = UiEvent::from(cmd.as_str());
21✔
85
            if let UiEvent::Unknown(cmd) = event {
21✔
86
                this.main_writer
1✔
87
                    .send(Event::Error(format!("Invalid ui command: {cmd}")))
1✔
88
                    .unwrap();
1✔
89
            } else {
20✔
90
                this.ui_events.push(event);
20✔
91
            }
20✔
92
            Ok(())
21✔
93
        });
21✔
94
        methods.add_function("debug", |_, strings: Variadic<String>| {
185✔
95
            debug!("{}", strings.join(" "));
21✔
96
            Ok(())
21✔
97
        });
21✔
98
        methods.add_function("is_core_mode", |ctx, ()| {
5,002✔
99
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
5,002✔
100
            let this = this_aux.borrow::<Blight>()?;
5,002✔
101
            Ok(this.core_mode)
5,002✔
102
        });
5,002✔
103
        methods.add_function("is_reader_mode", |ctx, ()| {
185✔
104
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
7✔
105
            let this = this_aux.borrow::<Blight>()?;
7✔
106
            Ok(this.reader_mode)
7✔
107
        });
7✔
108
        methods.add_function("status_height", |ctx, requested: Option<u16>| {
185✔
109
            let height: u16 = if let Some(height) = requested {
5✔
110
                let height = height.clamp(0, 5);
2✔
111
                let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
2✔
112
                let this = this_aux.borrow::<Blight>()?;
2✔
113
                this.main_writer
2✔
114
                    .send(Event::StatusAreaHeight(height))
2✔
115
                    .unwrap();
2✔
116
                ctx.set_named_registry_value(STATUS_AREA_HEIGHT, height)?;
2✔
117
                height
2✔
118
            } else {
119
                ctx.named_registry_value(STATUS_AREA_HEIGHT)?
3✔
120
            };
121
            Ok(height)
5✔
122
        });
5✔
123
        methods.add_function("status_line", |ctx, (index, line): (usize, String)| {
185✔
124
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
×
125
            let this = this_aux.borrow::<Blight>()?;
×
126
            this.main_writer
×
127
                .send(Event::StatusLine(index, line))
×
128
                .unwrap();
×
129
            Ok(())
×
130
        });
×
131
        methods.add_function("version", |_, _: ()| -> LuaResult<(&str, &str)> {
192✔
132
            Ok((PROJECT_NAME, VERSION))
175✔
133
        });
175✔
134
        methods.add_function("config_dir", |_, ()| -> mlua::Result<String> {
185✔
135
            Ok(crate::CONFIG_DIR.to_string_lossy().to_string())
1✔
136
        });
1✔
137
        methods.add_function("data_dir", |_, ()| -> mlua::Result<String> {
185✔
138
            Ok(crate::DATA_DIR.to_string_lossy().to_string())
1✔
139
        });
1✔
140
        methods.add_function("on_quit", |ctx, func: Function| -> mlua::Result<()> {
185✔
141
            let table: Table = ctx.named_registry_value(BLIGHT_ON_QUIT_LISTENER_TABLE)?;
168✔
142
            table.set(table.raw_len() + 1, func)?;
168✔
143
            Ok(())
168✔
144
        });
168✔
145
        methods.add_function(
185✔
146
            "on_complete",
147
            |ctx, func: mlua::Function| -> mlua::Result<()> {
3✔
148
                let table: Table = ctx.named_registry_value(COMPLETION_CALLBACK_TABLE)?;
3✔
149
                table.set(table.raw_len() + 1, func)?;
3✔
150
                Ok(())
3✔
151
            },
3✔
152
        );
153
        methods.add_function(
185✔
154
            "on_dimensions_change",
155
            |ctx, func: Function| -> mlua::Result<()> {
166✔
156
                let table: Table =
166✔
157
                    ctx.named_registry_value(BLIGHT_ON_DIMENSIONS_CHANGE_LISTENER_TABLE)?;
166✔
158
                table.set(table.raw_len() + 1, func)?;
166✔
159
                Ok(())
166✔
160
            },
166✔
161
        );
162
        methods.add_function("quit", |ctx, ()| {
185✔
163
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
80✔
164
            let this = this_aux.borrow::<Blight>()?;
80✔
165
            this.main_writer
80✔
166
                .send(Event::Quit(QuitMethod::Script))
80✔
167
                .unwrap();
80✔
168
            Ok(())
80✔
169
        });
80✔
170
        methods.add_function("show_help", |ctx, (name, lock_scroll): (String, bool)| {
185✔
171
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
3✔
172
            let this = this_aux.borrow::<Blight>()?;
3✔
173
            this.main_writer
3✔
174
                .send(Event::ShowHelp(name, lock_scroll))
3✔
175
                .unwrap();
3✔
176
            Ok(())
3✔
177
        });
3✔
178
        methods.add_function("find_backward", |ctx, re: Regex| {
185✔
179
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
3✔
180
            let this = this_aux.borrow::<Blight>()?;
3✔
181
            this.main_writer
3✔
182
                .send(Event::FindBackward(re.regex))
3✔
183
                .unwrap();
3✔
184
            Ok(())
3✔
185
        });
3✔
186
        methods.add_function("find_forward", |ctx, re: Regex| {
185✔
187
            let this_aux = ctx.globals().get::<_, AnyUserData>("blight")?;
1✔
188
            let this = this_aux.borrow::<Blight>()?;
1✔
189
            this.main_writer.send(Event::FindForward(re.regex)).unwrap();
1✔
190
            Ok(())
1✔
191
        });
1✔
192
    }
185✔
193
}
194

195
#[cfg(test)]
196
mod test_blight {
197
    use std::sync::mpsc::{channel, Receiver, Sender};
198

199
    use mlua::{AnyUserData, Lua};
200

201
    use crate::event::{Event, QuitMethod};
202
    use crate::lua::UiEvent;
203

204
    use super::Blight;
205
    use crate::lua::constants::{
206
        BLIGHT_ON_DIMENSIONS_CHANGE_LISTENER_TABLE, BLIGHT_ON_QUIT_LISTENER_TABLE,
207
        COMMAND_BINDING_TABLE, COMPLETION_CALLBACK_TABLE, STATUS_AREA_HEIGHT,
208
    };
209
    use crate::{PROJECT_NAME, VERSION};
210

211
    fn get_lua_state() -> (Lua, Receiver<Event>) {
14✔
212
        let (writer, reader): (Sender<Event>, Receiver<Event>) = channel();
14✔
213
        let regex = crate::lua::regex::RegexLib {};
14✔
214
        let blight = Blight::new(writer);
14✔
215
        let lua = Lua::new();
14✔
216
        lua.globals().set("regex", regex).unwrap();
14✔
217
        lua.globals().set("blight", blight).unwrap();
14✔
218
        lua.set_named_registry_value(BLIGHT_ON_QUIT_LISTENER_TABLE, lua.create_table().unwrap())
14✔
219
            .unwrap();
14✔
220
        lua.set_named_registry_value(
14✔
221
            BLIGHT_ON_DIMENSIONS_CHANGE_LISTENER_TABLE,
14✔
222
            lua.create_table().unwrap(),
14✔
223
        )
14✔
224
        .unwrap();
14✔
225
        lua.set_named_registry_value(COMPLETION_CALLBACK_TABLE, lua.create_table().unwrap())
14✔
226
            .unwrap();
14✔
227
        lua.set_named_registry_value(COMMAND_BINDING_TABLE, lua.create_table().unwrap())
14✔
228
            .unwrap();
14✔
229
        lua.set_named_registry_value(STATUS_AREA_HEIGHT, 1u16)
14✔
230
            .unwrap();
14✔
231
        (lua, reader)
14✔
232
    }
14✔
233

234
    #[test]
235
    fn test_config_dir() {
1✔
236
        let (lua, _reader) = get_lua_state();
1✔
237
        assert!(lua
1✔
238
            .load("return blight.config_dir()")
1✔
239
            .call::<_, String>(())
1✔
240
            .unwrap()
1✔
241
            .ends_with(".run/test/config"));
1✔
242
    }
1✔
243

244
    #[test]
245
    fn test_data_dir() {
1✔
246
        let (lua, _reader) = get_lua_state();
1✔
247
        assert!(lua
1✔
248
            .load("return blight.data_dir()")
1✔
249
            .call::<_, String>(())
1✔
250
            .unwrap()
1✔
251
            .ends_with(".run/test/data"));
1✔
252
    }
1✔
253

254
    #[test]
255
    fn test_version() {
1✔
256
        let (lua, _reader) = get_lua_state();
1✔
257
        assert_eq!(
1✔
258
            lua.load("return blight.version()")
1✔
259
                .call::<_, (String, String)>(())
1✔
260
                .unwrap(),
1✔
261
            (PROJECT_NAME.to_string(), VERSION.to_string())
1✔
262
        );
263
    }
1✔
264

265
    #[test]
266
    fn confirm_on_quite_register() {
1✔
267
        let (lua, _reader) = get_lua_state();
1✔
268
        let table: mlua::Table = lua
1✔
269
            .named_registry_value(BLIGHT_ON_QUIT_LISTENER_TABLE)
1✔
270
            .unwrap();
1✔
271
        assert_eq!(table.raw_len(), 0);
1✔
272
        lua.load("blight.on_quit(function () end)").exec().unwrap();
1✔
273
        let table: mlua::Table = lua
1✔
274
            .named_registry_value(BLIGHT_ON_QUIT_LISTENER_TABLE)
1✔
275
            .unwrap();
1✔
276
        assert_eq!(table.raw_len(), 1);
1✔
277
    }
1✔
278

279
    #[test]
280
    fn on_complete() {
1✔
281
        let (lua, _reader) = get_lua_state();
1✔
282
        let table: mlua::Table = lua.named_registry_value(COMPLETION_CALLBACK_TABLE).unwrap();
1✔
283
        assert_eq!(table.raw_len(), 0);
1✔
284
        lua.load("blight.on_complete(function () end)")
1✔
285
            .exec()
1✔
286
            .unwrap();
1✔
287
        let table: mlua::Table = lua.named_registry_value(COMPLETION_CALLBACK_TABLE).unwrap();
1✔
288
        assert_eq!(table.raw_len(), 1);
1✔
289
    }
1✔
290

291
    #[test]
292
    fn on_quit_function() {
1✔
293
        let (lua, _reader) = get_lua_state();
1✔
294
        lua.load("blight.on_quit(function () blight.output(\"on_quit\") end)")
1✔
295
            .exec()
1✔
296
            .unwrap();
1✔
297
        let table: mlua::Table = lua
1✔
298
            .named_registry_value(BLIGHT_ON_QUIT_LISTENER_TABLE)
1✔
299
            .unwrap();
1✔
300
        for pair in table.pairs::<mlua::Value, mlua::Function>() {
1✔
301
            let (_, cb) = pair.unwrap();
1✔
302
            cb.call::<_, ()>(()).unwrap();
1✔
303
        }
1✔
304
        let blight_aux = lua.globals().get::<_, AnyUserData>("blight").unwrap();
1✔
305
        let mut blight = blight_aux.borrow_mut::<Blight>().unwrap();
1✔
306
        let lines = blight.get_output_lines();
1✔
307
        let mut it = lines.iter();
1✔
308
        assert_eq!(it.next().unwrap(), &crate::model::Line::from("on_quit"));
1✔
309
    }
1✔
310

311
    #[test]
312
    fn quit() {
1✔
313
        let (lua, reader) = get_lua_state();
1✔
314
        lua.load("blight.quit()").exec().unwrap();
1✔
315
        assert_eq!(reader.recv(), Ok(Event::Quit(QuitMethod::Script)));
1✔
316
    }
1✔
317

318
    #[test]
319
    fn find() {
1✔
320
        let (lua, reader) = get_lua_state();
1✔
321
        let re = crate::model::Regex::new("test", None).unwrap();
1✔
322
        lua.load(r#"blight.find_forward(regex.new("test"))"#)
1✔
323
            .exec()
1✔
324
            .unwrap();
1✔
325
        assert_eq!(reader.recv(), Ok(Event::FindForward(re.clone())));
1✔
326
        lua.load(r#"blight.find_backward(regex.new("test"))"#)
1✔
327
            .exec()
1✔
328
            .unwrap();
1✔
329
        assert_eq!(reader.recv(), Ok(Event::FindBackward(re)));
1✔
330
    }
1✔
331

332
    #[test]
333
    fn show_help() {
1✔
334
        let (lua, reader) = get_lua_state();
1✔
335
        lua.load("blight.show_help(\"test1\", false)")
1✔
336
            .exec()
1✔
337
            .unwrap();
1✔
338
        assert_eq!(
1✔
339
            reader.recv(),
1✔
340
            Ok(Event::ShowHelp("test1".to_string(), false))
1✔
341
        );
342
        lua.load("blight.show_help(\"test2\", true)")
1✔
343
            .exec()
1✔
344
            .unwrap();
1✔
345
        assert_eq!(
1✔
346
            reader.recv(),
1✔
347
            Ok(Event::ShowHelp("test2".to_string(), true))
1✔
348
        );
349
    }
1✔
350

351
    #[test]
352
    fn confirm_ui_events() {
1✔
353
        let (lua, _) = get_lua_state();
1✔
354
        lua.load("blight.ui(\"step_left\")").exec().unwrap();
1✔
355
        lua.load("blight.ui(\"step_right\")").exec().unwrap();
1✔
356
        lua.load("blight.ui(\"scroll_up\")").exec().unwrap();
1✔
357
        lua.load("blight.ui(\"scroll_down\")").exec().unwrap();
1✔
358

1✔
359
        let mut blight: Blight = lua.globals().get("blight").unwrap();
1✔
360
        assert_eq!(
1✔
361
            blight.get_ui_events(),
1✔
362
            vec![
1✔
363
                UiEvent::StepLeft,
1✔
364
                UiEvent::StepRight,
1✔
365
                UiEvent::ScrollUp,
1✔
366
                UiEvent::ScrollDown
1✔
367
            ]
368
        );
369
    }
1✔
370

371
    #[test]
372
    fn test_bad_ui_event() {
1✔
373
        let (lua, reader) = get_lua_state();
1✔
374
        lua.load("blight.ui(\"schplort\")").exec().unwrap();
1✔
375
        assert_eq!(
1✔
376
            reader.recv(),
1✔
377
            Ok(Event::Error("Invalid ui command: schplort".to_string()))
1✔
378
        );
379
    }
1✔
380

381
    #[test]
382
    fn test_command_bindings() {
1✔
383
        let (lua, _) = get_lua_state();
1✔
384
        lua.load("blight.bind(\"f1\", function () end)")
1✔
385
            .exec()
1✔
386
            .unwrap();
1✔
387
        let bindings: mlua::Table = lua.named_registry_value(COMMAND_BINDING_TABLE).unwrap();
1✔
388
        assert!(bindings.get::<_, mlua::Function>("f1").is_ok());
1✔
389
        lua.load("blight.unbind(\"f1\")").exec().unwrap();
1✔
390
        assert!(bindings.get::<_, mlua::Function>("f1").is_err());
1✔
391
    }
1✔
392

393
    #[test]
394
    fn test_command_bindings_alt_with_capitalized_letter() {
1✔
395
        let (lua, _) = get_lua_state();
1✔
396
        lua.load("blight.bind(\"Alt-H\", function () end)")
1✔
397
            .exec()
1✔
398
            .unwrap();
1✔
399
        let bindings: mlua::Table = lua.named_registry_value(COMMAND_BINDING_TABLE).unwrap();
1✔
400
        assert!(bindings.get::<_, mlua::Function>("alt-H").is_ok());
1✔
401
        assert!(bindings.get::<_, mlua::Function>("alt-h").is_err());
1✔
402
    }
1✔
403

404
    #[test]
405
    fn test_status_height() {
1✔
406
        let (lua, _reader) = get_lua_state();
1✔
407
        let height = lua
1✔
408
            .load("return blight.status_height()")
1✔
409
            .call::<_, u16>(())
1✔
410
            .unwrap();
1✔
411
        assert_eq!(height, 1);
1✔
412
        let height = lua
1✔
413
            .load("return blight.status_height(3)")
1✔
414
            .call::<_, u16>(())
1✔
415
            .unwrap();
1✔
416
        assert_eq!(height, 3);
1✔
417
        let height = lua
1✔
418
            .load("return blight.status_height()")
1✔
419
            .call::<_, u16>(())
1✔
420
            .unwrap();
1✔
421
        assert_eq!(height, 3);
1✔
422
        let height = lua
1✔
423
            .load("return blight.status_height(1)")
1✔
424
            .call::<_, u16>(())
1✔
425
            .unwrap();
1✔
426
        assert_eq!(height, 1);
1✔
427
        let height = lua
1✔
428
            .load("return blight.status_height()")
1✔
429
            .call::<_, u16>(())
1✔
430
            .unwrap();
1✔
431
        assert_eq!(height, 1);
1✔
432
    }
1✔
433
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc