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) {
27    let mut current_state = ParserState::LookingForInterestingOutput;
28    let mut question = String::new();
29    sender
30        .send(State::InProgress)
31        .expect("Failed to send process start event");
32    thread::spawn(move || loop {
33        match receiver.try_recv() {
34            Ok(string) => match current_state {
35                ParserState::CollectingQuestion | ParserState::WaitingForMoreQuestionContent => {
36                    if let Ok(mut writer) = log.write() {
37                        writer.push_str(&string);
38                    }
39                    if parser_config.useful_status_words.contains(&string) {
40                        log::debug!(
41                            "Weidu seems to know an answer for the last question, ignoring it"
42                        );
43                        current_state = ParserState::LookingForInterestingOutput;
44                        question.clear();
45                    } else {
46                        log::debug!("Appending line '{}' to user question", string);
47                        question.push_str(&string);
48                        current_state = ParserState::CollectingQuestion;
49                    }
50                }
51                ParserState::LookingForInterestingOutput => {
52                    if let Ok(mut writer) = log.write() {
53                        writer.push_str(&string);
54                    }
55                    if let Some(weidu_finished_state) =
56                        parser_config.detect_weidu_finished_state(&string)
57                    {
58                        sender
59                            .send(weidu_finished_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(100);
103            }
104            Err(TryRecvError::Disconnected) => {
105                sender
106                    .send(State::Completed)
107                    .expect("Failed to send provess end event");
108                break;
109            }
110        }
111    });
112}