use super::prelude::*; // TODO?: instead of parsing lazily when invoked, parse the strings into a // compressed binary representations that can be stuffed back into the packet // buffer, and return an iterator over the binary data that's _guaranteed_ to be // valid. This would clean up some of the code in the vCont handler. // // The interesting part would be to see whether or not the simplified error // handing code will compensate for all the new code required to pre-validate // the data... #[derive(Debug)] pub enum vCont<'a> { Query, Actions(Actions<'a>), } impl<'a> ParseCommand<'a> for vCont<'a> { fn from_packet(buf: PacketBuf<'a>) -> Option { let body = buf.into_body(); match body as &[u8] { b"?" => Some(vCont::Query), _ => Some(vCont::Actions(Actions::new_from_buf(body))), } } } #[derive(Debug)] pub enum Actions<'a> { Buf(ActionsBuf<'a>), FixedStep(SpecificThreadId), FixedCont(SpecificThreadId), } impl<'a> Actions<'a> { fn new_from_buf(buf: &'a [u8]) -> Actions<'a> { Actions::Buf(ActionsBuf(buf)) } pub fn new_step(tid: SpecificThreadId) -> Actions<'a> { Actions::FixedStep(tid) } pub fn new_continue(tid: SpecificThreadId) -> Actions<'a> { Actions::FixedCont(tid) } pub fn iter(&self) -> impl Iterator>> + '_ { match self { Actions::Buf(x) => EitherIter::A(x.iter()), Actions::FixedStep(x) => EitherIter::B(core::iter::once(Some(VContAction { kind: VContKind::Step, thread: Some(*x), }))), Actions::FixedCont(x) => EitherIter::B(core::iter::once(Some(VContAction { kind: VContKind::Continue, thread: Some(*x), }))), } } } #[derive(Debug)] pub struct ActionsBuf<'a>(&'a [u8]); impl<'a> ActionsBuf<'a> { fn iter(&self) -> impl Iterator>> + '_ { self.0.split(|b| *b == b';').skip(1).map(|act| { let mut s = act.split(|b| *b == b':'); let kind = s.next()?; let thread = match s.next() { Some(s) => Some(SpecificThreadId::try_from(ThreadId::try_from(s).ok()?).ok()?), None => None, }; Some(VContAction { kind: VContKind::from_bytes(kind)?, thread, }) }) } } #[derive(Debug, Copy, Clone)] pub struct VContAction<'a> { pub kind: VContKind<'a>, pub thread: Option, } #[derive(Debug, Copy, Clone)] pub enum VContKind<'a> { Continue, ContinueWithSig(u8), RangeStep(HexString<'a>, HexString<'a>), Step, StepWithSig(u8), Stop, } impl<'a> VContKind<'a> { fn from_bytes(s: &[u8]) -> Option { use self::VContKind::*; let res = match s { [b'c'] => Continue, [b's'] => Step, [b't'] => Stop, [b'C', sig @ ..] => ContinueWithSig(decode_hex(sig).ok()?), [b'S', sig @ ..] => StepWithSig(decode_hex(sig).ok()?), [b'r', range @ ..] => { let mut range = range.split(|b| *b == b','); RangeStep(HexString(range.next()?), HexString(range.next()?)) } _ => return None, }; Some(res) } } /// Helper type to unify iterators that output the same type. Returned as an /// opaque type from `Actions::iter()`. enum EitherIter { A(A), B(B), } impl Iterator for EitherIter where A: Iterator, B: Iterator, { type Item = T; fn next(&mut self) -> Option { match self { EitherIter::A(a) => a.next(), EitherIter::B(b) => b.next(), } } }