mod_installer/
weidu_parser.rs

1use std::{
2    sync::{
3        atomic::{AtomicUsize, Ordering},
4        mpsc::{Receiver, Sender, TryRecvError},
5        Arc, RwLock,
6    },
7    thread,
8};
9
10use crate::{config::parser_config::ParserConfig, state::State, utils::sleep};
11
12#[derive(Debug)]
13enum ParserState {
14    CollectingQuestion,
15    WaitingForMoreQuestionContent,
16    LookingForInterestingOutput,
17}
18
19pub(crate) fn parse_raw_output(
20    sender: Sender<State>,
21    receiver: Receiver<String>,
22    parser_config: Arc<ParserConfig>,
23    wait_count: Arc<AtomicUsize>,
24    log: Arc<RwLock<String>>,
25    timeout: usize,
26    tick: u64,
27) {
28    let mut current_state = ParserState::LookingForInterestingOutput;
29    let mut question = String::new();
30    sender
31        .send(State::InProgress)
32        .expect("Failed to send process start event");
33    thread::spawn(move || loop {
34        match receiver.try_recv() {
35            Ok(string) => match current_state {
36                ParserState::CollectingQuestion | ParserState::WaitingForMoreQuestionContent => {
37                    if let Ok(mut writer) = log.write() {
38                        writer.push_str(&string);
39                    }
40                    if parser_config.useful_status_words.contains(&string) {
41                        log::debug!(
42                            "Weidu seems to know an answer for the last question, ignoring it"
43                        );
44                        current_state = ParserState::LookingForInterestingOutput;
45                        question.clear();
46                    } else {
47                        log::debug!("Appending line '{string}' to user question");
48                        question.push_str(&string);
49                        current_state = ParserState::CollectingQuestion;
50                    }
51                }
52                ParserState::LookingForInterestingOutput => {
53                    if let Ok(mut writer) = log.write() {
54                        writer.push_str(&string);
55                    }
56                    let installer_state = parser_config.detect_weidu_finished_state(&string);
57                    if installer_state != State::InProgress {
58                        sender
59                            .send(installer_state)
60                            .expect("Failed to send process error event");
61                        break;
62                    } else if parser_config.string_looks_like_question(&string) {
63                        log::debug!(
64                            "Changing parser state to '{:?}' due to line {}",
65                            ParserState::CollectingQuestion,
66                            string
67                        );
68                        current_state = ParserState::CollectingQuestion;
69                        question.push_str(string.as_str());
70                    }
71                    if !string.trim().is_empty() {
72                        log::trace!("{string}");
73                    }
74                }
75            },
76            Err(TryRecvError::Empty) => {
77                match current_state {
78                    ParserState::CollectingQuestion => {
79                        log::debug!(
80                            "Changing parser state to '{:?}'",
81                            ParserState::WaitingForMoreQuestionContent
82                        );
83                        current_state = ParserState::WaitingForMoreQuestionContent;
84                    }
85                    ParserState::WaitingForMoreQuestionContent => {
86                        log::debug!("No new weidu output, sending question to user");
87                        sender
88                            .send(State::RequiresInput { question })
89                            .expect("Failed to send question");
90                        current_state = ParserState::LookingForInterestingOutput;
91                        question = String::new();
92                    }
93                    _ => {
94                        if wait_count.load(Ordering::Relaxed) >= timeout {
95                            sender
96                                .send(State::TimedOut)
97                                .expect("Could send timeout error");
98                        }
99                        // there is no new weidu output and we are not waiting for any, so there is nothing to do
100                    }
101                }
102                sleep(tick);
103            }
104            Err(TryRecvError::Disconnected) => {
105                sender
106                    .send(State::Completed)
107                    .expect("Failed to send process end event");
108                break;
109            }
110        }
111    });
112}