use super::{ErrorKind, Response, Result};
use pest::iterators::Pairs;
use pest::Parser;
use tokio::{
io::{AsyncBufReadExt, BufReader},
process::ChildStdout,
sync::mpsc::Sender,
};
struct Decoder(Vec<u8>);
#[derive(pest_derive::Parser)]
#[grammar = "pinentry/responses.pest"]
struct LineParser;
pub(super) async fn listen(stream: ChildStdout, channel: Sender<Response>) -> Result<()> {
let mut reader = BufReader::new(stream).lines();
let mut decoder = Decoder::new();
loop {
if channel.is_closed() {
println!("🛈 Pinentry: Response channel closed");
break;
}
if let Some(line) = reader.next_line().await? {
let response = decoder.decode(line)?;
channel.send(response).await?;
}
}
Ok(())
}
impl Decoder {
fn new() -> Self {
Self(Vec::new())
}
fn decode(&mut self, line: String) -> Result<Response> {
let parsed_line = LineParser::parse(Rule::Response, &line)?
.next()
.unwrap()
.into_inner()
.next()
.ok_or(ErrorKind::EmptyResponse)?;
let fields = parsed_line.clone().into_inner();
Ok(match parsed_line.as_rule() {
Rule::Ok => self.ok(fields),
Rule::Error => self.error(fields),
Rule::Data => self.data(fields),
Rule::Comment => Self::comment(fields),
Rule::Status => Self::status(fields),
Rule::Enquiry => Self::enquiry(fields),
_ => Err(ErrorKind::UnexpectedResponse(line.clone()))?,
})
}
fn ok(&mut self, fields: Pairs<Rule>) -> Response {
let msg = fields.tail();
if self.0.len() == 0 {
Response::Success(msg)
} else {
let resp = Response::DataOK(self.0.clone(), msg);
self.0 = Vec::new();
resp
}
}
fn error(&mut self, mut fields: Pairs<Rule>) -> Response {
let erc = fields.next().unwrap().as_str().parse().unwrap();
let msg = fields.tail();
if self.0.len() == 0 {
Response::Failure(erc, msg)
} else {
let resp = Response::DataFail(self.0.clone(), erc, msg);
self.0 = Vec::new();
resp
}
}
fn data(&mut self, fields: Pairs<Rule>) -> Response {
self.0.append(&mut fields.tail().into_bytes());
Response::DataBuffered
}
fn comment(fields: Pairs<Rule>) -> Response {
Response::Comment(fields.tail())
}
fn status(mut fields: Pairs<Rule>) -> Response {
let keyword = fields.next().unwrap().as_str().to_string();
let msg = fields.tail();
Response::Status(keyword, msg)
}
fn enquiry(mut fields: Pairs<Rule>) -> Response {
let keyword = fields.next().unwrap().as_str().to_string();
let params = fields.tail();
Response::Enquiry(keyword, params)
}
}
trait ConvertTail {
fn tail(self) -> String;
}
impl<'i> ConvertTail for Pairs<'i, Rule> {
fn tail(mut self) -> String {
self.next().map_or(format!(""), |p| p.as_str().to_string())
}
}