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

Blightmud / Blightmud / 13499026546

24 Feb 2025 01:20PM UTC coverage: 75.293%. Remained the same
13499026546

push

github

web-flow
Merge pull request #1192 from Blightmud/dependabot/cargo/log-0.4.26

build(deps): bump log from 0.4.25 to 0.4.26

7320 of 9722 relevant lines covered (75.29%)

425.88 hits per line

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

92.08
/src/session.rs
1
use anyhow::Result;
2
use libmudtelnet::{
3
    compatibility::CompatibilityTable, telnet::op_command as cmd, telnet::op_option as opt, Parser,
4
};
5
use log::debug;
6
use std::sync::{atomic::AtomicBool, mpsc::Sender, Arc, Mutex};
7

8
use crate::{
9
    event::QuitMethod,
10
    io::{LogWriter, Logger},
11
    lua::{LuaScript, LuaScriptBuilder},
12
    net::MudConnection,
13
    net::BUFFER_SIZE,
14
    net::{OutputBuffer, TelnetMode},
15
    timer::TimerEvent,
16
    tts::TTSController,
17
    ui::CommandBuffer,
18
    Event,
19
};
20

21
use crate::net::CertificateValidation;
22
#[cfg(test)]
23
use mockall::automock;
24

25
#[derive(Clone)]
26
pub struct Session {
27
    pub connection: Arc<Mutex<MudConnection>>,
28
    pub main_writer: Sender<Event>,
29
    pub timer_writer: Sender<TimerEvent>,
30
    pub telnet_parser: Arc<Mutex<Parser>>,
31
    pub output_buffer: Arc<Mutex<OutputBuffer>>,
32
    pub prompt_input: Arc<Mutex<String>>,
33
    pub lua_script: Arc<Mutex<LuaScript>>,
34
    pub logger: Arc<Mutex<dyn LogWriter + Send>>,
35
    pub tts_ctrl: Arc<Mutex<TTSController>>,
36
    pub command_buffer: Arc<Mutex<CommandBuffer>>,
37
    pub echo_input: Arc<AtomicBool>,
38
}
39

40
#[cfg_attr(test, automock)]
×
41
impl Session {
42
    pub fn connect(
77✔
43
        &mut self,
77✔
44
        host: &str,
77✔
45
        port: u16,
77✔
46
        tls: bool,
77✔
47
        tls_validation: CertificateValidation,
77✔
48
    ) -> bool {
77✔
49
        let mut connected = false;
77✔
50
        let mut conn_id = 0u16;
77✔
51
        if let Ok(mut connection) = self.connection.lock() {
77✔
52
            connected = match connection.connect(host, port, tls, tls_validation) {
77✔
53
                Ok(_) => {
54
                    conn_id = connection.id;
77✔
55
                    true
77✔
56
                }
57
                Err(err) => {
×
58
                    debug!("Failed to connect: {}", err);
×
59
                    self.main_writer
×
60
                        .send(Event::Error(format!("{err}")))
×
61
                        .unwrap();
×
62
                    false
×
63
                }
64
            };
65
        }
×
66
        if connected {
77✔
67
            self.main_writer
77✔
68
                .send(Event::StartLogging(host.to_string(), false))
77✔
69
                .unwrap();
77✔
70
            self.main_writer.send(Event::Connected(conn_id)).unwrap();
77✔
71
        }
77✔
72
        connected
77✔
73
    }
77✔
74

75
    pub fn disconnect(&mut self) {
147✔
76
        let mut connection = self.connection.lock().unwrap();
147✔
77
        if connection.connected() {
147✔
78
            connection.disconnect().ok();
70✔
79
            if let Ok(mut output_buffer) = self.output_buffer.lock() {
70✔
80
                output_buffer.clear()
70✔
81
            }
×
82

83
            if let Ok(mut parser) = self.telnet_parser.lock() {
70✔
84
                parser.options.reset_states();
70✔
85
            };
70✔
86

87
            self.stop_logging();
70✔
88
        }
77✔
89
    }
147✔
90

91
    pub fn try_disconnect(&mut self) {
169✔
92
        if let Ok(mut connection) = self.connection.try_lock() {
169✔
93
            if connection.connected() {
169✔
94
                connection.disconnect().ok();
×
95
                if let Ok(mut output_buffer) = self.output_buffer.lock() {
×
96
                    output_buffer.clear()
×
97
                }
×
98

99
                if let Ok(mut parser) = self.telnet_parser.lock() {
×
100
                    parser.options.reset_states();
×
101
                };
×
102

103
                self.stop_logging();
×
104
            }
169✔
105
        }
×
106
    }
169✔
107

108
    pub fn connected(&self) -> bool {
609✔
109
        let connection = self.connection.lock().unwrap();
609✔
110
        connection.connected()
609✔
111
    }
609✔
112

113
    pub fn host(&self) -> String {
154✔
114
        let connection = self.connection.lock().unwrap();
154✔
115
        connection.host.clone()
154✔
116
    }
154✔
117

118
    pub fn port(&self) -> u16 {
154✔
119
        let connection = self.connection.lock().unwrap();
154✔
120
        connection.port
154✔
121
    }
154✔
122

123
    pub fn tls(&self) -> bool {
14✔
124
        let connection = self.connection.lock().unwrap();
14✔
125
        connection.tls
14✔
126
    }
14✔
127

128
    pub fn verify_cert(&self) -> bool {
14✔
129
        let connection = self.connection.lock().unwrap();
14✔
130
        match connection.tls_validation {
14✔
131
            CertificateValidation::Enabled => true,
14✔
132
            CertificateValidation::DangerousDisabled => false,
×
133
        }
134
    }
14✔
135

136
    pub fn start_logging(&self, host: &str) {
1✔
137
        if let Ok(mut logger) = self.logger.lock() {
1✔
138
            self.main_writer
1✔
139
                .send(Event::Info(format!("Started logging for: {host}")))
1✔
140
                .unwrap();
1✔
141
            logger.start_logging(host).ok();
1✔
142
        }
1✔
143
    }
1✔
144

145
    pub fn stop_logging(&self) {
71✔
146
        if let Ok(mut logger) = self.logger.lock() {
71✔
147
            self.main_writer
71✔
148
                .send(Event::Info("Logging stopped".to_string()))
71✔
149
                .unwrap();
71✔
150
            logger.stop_logging().ok();
71✔
151
        }
71✔
152
    }
71✔
153

154
    pub fn send_event(&mut self, event: Event) {
1✔
155
        self.main_writer.send(event).unwrap();
1✔
156
    }
1✔
157

158
    pub fn close(&mut self) -> Result<()> {
85✔
159
        self.try_disconnect();
85✔
160
        self.main_writer.send(Event::Quit(QuitMethod::System))?;
85✔
161
        self.timer_writer.send(TimerEvent::Quit)?;
85✔
162
        self.tts_ctrl.lock().unwrap().shutdown();
85✔
163
        Ok(())
85✔
164
    }
85✔
165
}
166

167
#[derive(Clone)]
168
pub struct SessionBuilder {
169
    main_writer: Option<Sender<Event>>,
170
    timer_writer: Option<Sender<TimerEvent>>,
171
    screen_dimensions: Option<(u16, u16)>,
172
    tts_enabled: bool,
173
    reader_mode: bool,
174
    save_history: bool,
175
    headless: bool,
176
    echo_input: bool,
177
}
178

179
impl SessionBuilder {
180
    pub fn new() -> Self {
95✔
181
        Self {
95✔
182
            main_writer: None,
95✔
183
            timer_writer: None,
95✔
184
            screen_dimensions: None,
95✔
185
            tts_enabled: false,
95✔
186
            reader_mode: false,
95✔
187
            save_history: false,
95✔
188
            headless: false,
95✔
189
            echo_input: true,
95✔
190
        }
95✔
191
    }
95✔
192

193
    pub fn main_writer(mut self, main_writer: Sender<Event>) -> Self {
95✔
194
        self.main_writer = Some(main_writer);
95✔
195
        self
95✔
196
    }
95✔
197

198
    pub fn timer_writer(mut self, timer_writer: Sender<TimerEvent>) -> Self {
95✔
199
        self.timer_writer = Some(timer_writer);
95✔
200
        self
95✔
201
    }
95✔
202

203
    pub fn screen_dimensions(mut self, dimensions: (u16, u16)) -> Self {
95✔
204
        self.screen_dimensions = Some(dimensions);
95✔
205
        self
95✔
206
    }
95✔
207

208
    pub fn tts_enabled(mut self, enabled: bool) -> Self {
84✔
209
        self.tts_enabled = enabled;
84✔
210
        self
84✔
211
    }
84✔
212

213
    pub fn save_history(mut self, enabled: bool) -> Self {
84✔
214
        self.save_history = enabled;
84✔
215
        self
84✔
216
    }
84✔
217

218
    pub fn headless(mut self, headless: bool) -> Self {
84✔
219
        self.headless = headless;
84✔
220
        self
84✔
221
    }
84✔
222

223
    pub fn reader_mode(mut self, reader_mode: bool) -> Self {
84✔
224
        self.reader_mode = reader_mode;
84✔
225
        self
84✔
226
    }
84✔
227

228
    pub fn echo_input(mut self, echo_input: bool) -> Self {
84✔
229
        self.echo_input = echo_input;
84✔
230
        self
84✔
231
    }
84✔
232

233
    pub fn build(self) -> Session {
95✔
234
        let main_writer = self.main_writer.unwrap();
95✔
235
        let timer_writer = self.timer_writer.unwrap();
95✔
236
        let dimensions = self.screen_dimensions.unwrap();
95✔
237
        let tts_enabled = self.tts_enabled;
95✔
238
        let reader_mode = self.reader_mode;
95✔
239
        let headless = self.headless;
95✔
240
        let tts_ctrl = Arc::new(Mutex::new(TTSController::new(tts_enabled, headless)));
95✔
241
        let echo_input = self.echo_input;
95✔
242

95✔
243
        let lua_builder = LuaScriptBuilder::new(main_writer.clone())
95✔
244
            .dimensions(dimensions)
95✔
245
            .tts_enabled(tts_enabled)
95✔
246
            .reader_mode(reader_mode);
95✔
247

95✔
248
        let lua_script = Arc::new(Mutex::new(lua_builder.build()));
95✔
249
        Session {
95✔
250
            connection: Arc::new(Mutex::new(MudConnection::new())),
95✔
251
            main_writer,
95✔
252
            timer_writer,
95✔
253
            telnet_parser: Arc::new(Mutex::new(Parser::with_support_and_capacity(
95✔
254
                BUFFER_SIZE,
95✔
255
                build_compatibility_table(),
95✔
256
            ))),
95✔
257
            output_buffer: Arc::new(Mutex::new(OutputBuffer::new(
95✔
258
                &TelnetMode::UnterminatedPrompt,
95✔
259
            ))),
95✔
260
            prompt_input: Arc::new(Mutex::new(String::new())),
95✔
261
            lua_script: lua_script.clone(),
95✔
262
            logger: Arc::new(Mutex::new(Logger::default())),
95✔
263
            tts_ctrl: tts_ctrl.clone(),
95✔
264
            command_buffer: Arc::new(Mutex::new(CommandBuffer::new(tts_ctrl, lua_script))),
95✔
265
            echo_input: Arc::new(AtomicBool::new(echo_input)),
95✔
266
        }
95✔
267
    }
95✔
268
}
269

270
fn build_compatibility_table() -> CompatibilityTable {
95✔
271
    let mut telnet_compat = CompatibilityTable::default();
95✔
272
    telnet_compat.support(opt::MCCP2);
95✔
273
    telnet_compat.support(opt::EOR);
95✔
274
    telnet_compat.support(opt::ECHO);
95✔
275
    telnet_compat.support(cmd::GA);
95✔
276
    telnet_compat
95✔
277
}
95✔
278

279
#[cfg(test)]
280
mod session_test {
281

282
    use mockall::predicate::eq;
283

284
    use super::*;
285
    use crate::io::MockLogWriter;
286
    use crate::{event::Event, model::Line, timer::TimerEvent};
287
    use std::sync::mpsc::{channel, Receiver, Sender};
288

289
    fn build_session() -> (Session, Receiver<Event>, Receiver<TimerEvent>) {
4✔
290
        let (writer, reader): (Sender<Event>, Receiver<Event>) = channel();
4✔
291
        let (timer_writer, timer_reader): (Sender<TimerEvent>, Receiver<TimerEvent>) = channel();
4✔
292
        let session = SessionBuilder::new()
4✔
293
            .main_writer(writer)
4✔
294
            .timer_writer(timer_writer)
4✔
295
            .screen_dimensions((80, 80))
4✔
296
            .build();
4✔
297

298
        loop {
299
            if reader.try_recv().is_err() {
28✔
300
                break;
4✔
301
            }
24✔
302
        }
303

304
        (session, reader, timer_reader)
4✔
305
    }
4✔
306

307
    #[test]
308
    fn test_session_build() {
1✔
309
        let _ = build_session();
1✔
310
    }
1✔
311

312
    #[test]
313
    fn test_session_send_event() {
1✔
314
        let (mut session, reader, _timer_reader) = build_session();
1✔
315
        session.send_event(Event::Output(Line::from("test test")));
1✔
316
        assert_eq!(reader.recv(), Ok(Event::Output(Line::from("test test"))));
1✔
317
    }
1✔
318

319
    #[test]
320
    fn test_logging() {
1✔
321
        let (mut session, reader, _timer_reader) = build_session();
1✔
322
        let mut logger = MockLogWriter::new();
1✔
323
        logger
1✔
324
            .expect_start_logging()
1✔
325
            .with(eq("mysteryhost"))
1✔
326
            .times(1)
1✔
327
            .returning(|_| Ok(()));
1✔
328
        logger.expect_stop_logging().times(1).returning(|| Ok(()));
1✔
329
        session.logger = Arc::new(Mutex::new(logger));
1✔
330

1✔
331
        session.start_logging("mysteryhost");
1✔
332
        assert_eq!(
1✔
333
            reader.recv(),
1✔
334
            Ok(Event::Info("Started logging for: mysteryhost".to_string()))
1✔
335
        );
1✔
336
        session.stop_logging();
1✔
337
        assert_eq!(
1✔
338
            reader.recv(),
1✔
339
            Ok(Event::Info("Logging stopped".to_string()))
1✔
340
        );
1✔
341
    }
1✔
342

343
    #[test]
344
    fn test_close() {
1✔
345
        let (mut session, reader, timer_reader) = build_session();
1✔
346
        session.close().unwrap();
1✔
347
        assert_eq!(reader.recv(), Ok(Event::Quit(QuitMethod::System)));
1✔
348
        assert_eq!(timer_reader.recv(), Ok(TimerEvent::Quit));
1✔
349
    }
1✔
350
}
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