mod_installer/
weidu_parser.rs1use std::{
2 sync::{
3 Arc,
4 atomic::{AtomicUsize, Ordering},
5 mpsc::{Receiver, Sender, TryRecvError},
6 },
7 thread,
8};
9
10use config::{args::Options, parser_config::ParserConfig, state::State};
11
12use crate::utils::sleep;
13
14#[derive(Debug)]
15enum ParserState {
16 CollectingQuestion,
17 WaitingForMoreQuestionContent,
18 LookingForInterestingOutput,
19}
20
21pub(crate) fn parse_raw_output(
22 options: &Options,
23 sender: Sender<State>,
24 receiver: Receiver<String>,
25 parser_config: Arc<ParserConfig>,
26 wait_count: Arc<AtomicUsize>,
27) {
28 let mut current_state = ParserState::LookingForInterestingOutput;
29 let mut buffer = vec![];
30 let mut question = vec![];
31 let mut grace_ticks: usize = 3;
32 sender
33 .send(State::InProgress)
34 .expect("Failed to send process start event");
35 let options = options.clone();
36 thread::spawn(move || {
37 loop {
38 match receiver.try_recv() {
39 Ok(string) => {
40 log::info!("{string}");
41 let installer_state = parser_config.detect_weidu_finished_state(&string);
42 if installer_state != State::InProgress {
43 sender
44 .send(installer_state)
45 .expect("Failed to send process error event");
46 break;
47 }
48 buffer.push(string.clone());
49 match current_state {
50 ParserState::CollectingQuestion
51 | ParserState::WaitingForMoreQuestionContent => {
52 if parser_config.useful_status_words.contains(&string) {
53 log::debug!(
54 "Weidu seems to know an answer for the last question, ignoring it"
55 );
56 current_state = ParserState::LookingForInterestingOutput;
57 question.clear();
58 } else {
59 log::debug!("Appending line '{string}' to user question");
60 question.push(string);
61 current_state = ParserState::CollectingQuestion;
62 }
63 }
64 ParserState::LookingForInterestingOutput => {
65 if parser_config.string_looks_like_question(&string) {
66 log::debug!(
67 "Changing parser state to '{:?}' due to line {}",
68 ParserState::CollectingQuestion,
69 string
70 );
71 current_state = ParserState::CollectingQuestion;
72 let min_index = buffer.len().saturating_sub(options.lookback);
73 for history in buffer.get(min_index..).unwrap_or_default() {
74 question.push(history.clone());
75 }
76 }
77 }
78 }
79 }
80 Err(TryRecvError::Empty) => match current_state {
81 ParserState::CollectingQuestion if grace_ticks > 0 => {
82 log::debug!("Collecting question, with grace of {grace_ticks} remaining");
83 sleep(options.tick);
84 grace_ticks -= 1;
85 }
86 ParserState::CollectingQuestion => {
87 log::debug!(
88 "Changing parser state to '{:?}'",
89 ParserState::WaitingForMoreQuestionContent
90 );
91 current_state = ParserState::WaitingForMoreQuestionContent;
92 grace_ticks = 3;
93 }
94 ParserState::WaitingForMoreQuestionContent => {
95 log::debug!("No new weidu output, sending question to user");
96 sender
97 .send(State::RequiresInput {
98 question: question.join(""),
99 })
100 .expect("Failed to send question");
101 current_state = ParserState::LookingForInterestingOutput;
102 question.clear();
103 continue;
104 }
105 _ if wait_count.load(Ordering::Relaxed) >= options.timeout => {
106 sender
107 .send(State::TimedOut)
108 .expect("Could send timeout error");
109 }
110 _ => {}
111 },
112 Err(TryRecvError::Disconnected) => {
113 sender
114 .send(State::Completed)
115 .expect("Failed to send process end event");
116 break;
117 }
118 }
119 }
120 });
121}