1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::str::FromStr;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WaitMode {
	Prompt,
	Immediate,
	Expect(usize, String),
}
impl Default for WaitMode { fn default() -> WaitMode { WaitMode::Prompt }}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ScriptCommand {
	pub wait_mode: WaitMode,
	pub skip_newline: bool,
	pub timeout: Option<u64>,
	pub command: String,
}
impl ScriptCommand {
	pub fn basic<S: Into<String>>(s: S) -> ScriptCommand {
		ScriptCommand {
			command: s.into(),
			..Default::default()
		}
	}

	pub fn immediate(&self) -> bool {
		self.wait_mode == WaitMode::Immediate
	}
	pub fn timeout(&self) -> Option<u64> {
		self.timeout
	}
	pub fn skip_newline(&self) -> bool {
		self.skip_newline
	}
}

impl FromStr for ScriptCommand {
	type Err = String;
	fn from_str(s: &str) -> Result<ScriptCommand, Self::Err> {
		let mut rest = s;
		//let mut modifiers = Vec::new();
		//let mut wait_mode = WaitMode::Prompt;
		let mut scmd = ScriptCommand::default();
		loop {
			if rest.starts_with('~') {
				rest = &rest[1..];
				scmd.wait_mode = WaitMode::Immediate;
			} else if rest.starts_with('!') {
				rest = &rest[1..];
				scmd.skip_newline = true;
			} else if rest.starts_with('`') {
				let ending = rest.char_indices()
					.skip(1)
					.find(|&(_, c)| c == '`');
				match ending {
					None => return Err(s.into()),
					Some((i, _)) => {
						let inner = &rest[1..i];
						match inner.char_indices().find(|(_, c)| *c == ':') {
							None => return Err(s.into()),
							Some((i, _)) => {
								let (timeout, expect) = (&inner[..i], &inner[i+1..]);
								let num = match timeout.parse() {
									Err(_) => return Err(s.into()),
									Ok(n) => n,
								};
								scmd.wait_mode = WaitMode::Expect(num, expect.into());
							}
						}
						
						rest = &rest[i+1..];
						
					}
				}
			} else if rest.starts_with('%') {
				let ending = rest.char_indices()
					.skip(1)
					.find(|(_, c)| *c == '%');
				match ending {
					None => return Err(s.into()),
					Some((i, _)) => {
						let inner = &rest[1..i];
						
						let num: u64 = match inner.parse() {
							Err(_) => return Err(s.into()),
							Ok(n) => n,
						};
						scmd.timeout = Some(num);

						rest = &rest[i+1..];
					}
				}
			} else if rest.starts_with('\\') && rest.starts_with(|c| matches!(c, '!' | '~')) {
				// skip past the escape
				rest = &rest[1..];
			} else {
				break;
			}
		}
		scmd.command = rest.into();
		Ok(scmd)
	}
}

struct LoginPair {
	username: String,
	password: String,
}


enum CtrlMessage {
	GlobalAuth(LoginPair),
	AuthConsole(LoginPair),
	AuthEnable(LoginPair),
}