1 use paste::paste;
2 
3 use crate::protocol::packet::PacketBuf;
4 use crate::target::Target;
5 
6 pub(self) mod prelude {
7     pub use super::ParseCommand;
8     pub use crate::common::*;
9     pub use crate::protocol::common::*;
10     pub use crate::protocol::packet::PacketBuf;
11     pub use core::convert::{TryFrom, TryInto};
12 }
13 
14 pub trait ParseCommand<'a>: Sized {
15     /// Try to parse a packet from the packet buffer.
from_packet(buf: PacketBuf<'a>) -> Option<Self>16     fn from_packet(buf: PacketBuf<'a>) -> Option<Self>;
17 }
18 
19 macro_rules! commands {
20     (
21         $(
22             $ext:ident $(use $lt:lifetime)? {
23                 $($name:literal => $mod:ident::$command:ident$(<$lifetime:lifetime>)?,)*
24             }
25         )*
26     ) => {paste! {
27         $($(
28             #[allow(non_snake_case, non_camel_case_types)]
29             pub mod $mod;
30         )*)*
31 
32         pub mod ext {
33             $(
34                 #[allow(non_camel_case_types)]
35                 pub enum [<$ext:camel>] $(<$lt>)? {
36                     $($command(super::$mod::$command<$($lifetime)?>),)*
37                 }
38             )*
39         }
40 
41         /// GDB commands
42         pub enum Command<'a> {
43             $(
44                 [<$ext:camel>](ext::[<$ext:camel>]$(<$lt>)?),
45             )*
46             Unknown(&'a str),
47         }
48 
49         impl<'a> Command<'a> {
50             pub fn from_packet(
51                 target: &mut impl Target,
52                 buf: PacketBuf<'a>
53             ) -> Result<Command<'a>, CommandParseError<'a>> {
54                 if buf.as_body().is_empty() {
55                     return Err(CommandParseError::Empty);
56                 }
57 
58                 let body = buf.as_body();
59 
60                 // This scoped extension trait enables using `base` as an
61                 // `$ext`, even through the `base` method on `Target` doesn't
62                 // return an Option.
63                 trait Hack { fn base(&mut self) -> Option<()> { Some(()) } }
64                 impl<T: Target> Hack for T {}
65 
66                 $(
67                 if target.$ext().is_some() {
68                     // TODO?: use tries for more efficient longest prefix matching
69                     #[allow(clippy::string_lit_as_bytes)]
70                     match body {
71                         $(_ if body.starts_with($name.as_bytes()) => {
72                             crate::__dead_code_marker!($name, "prefix_match");
73 
74                             let buf = buf.trim_start_body_bytes($name.len());
75                             let cmd = $mod::$command::from_packet(buf)
76                                 .ok_or(CommandParseError::MalformedCommand($name))?;
77 
78                             return Ok(
79                                 Command::[<$ext:camel>](
80                                     ext::[<$ext:camel>]::$command(cmd)
81                                 )
82                             )
83                         })*
84                         _ => {},
85                     }
86                 }
87                 )*
88 
89                 Ok(Command::Unknown(buf.into_body_str()))
90             }
91         }
92     }};
93 }
94 
95 /// Command parse error
96 // TODO?: add more granular errors to command parsing code
97 pub enum CommandParseError<'a> {
98     Empty,
99     /// catch-all
100     MalformedCommand(&'a str),
101 }
102 
103 commands! {
104     base use 'a {
105         "?" => question_mark::QuestionMark,
106         "c" => _c::c<'a>,
107         "D" => _d_upcase::D,
108         "g" => _g::g,
109         "G" => _g_upcase::G<'a>,
110         "H" => _h_upcase::H,
111         "k" => _k::k,
112         "m" => _m::m<'a>,
113         "M" => _m_upcase::M<'a>,
114         "p" => _p::p,
115         "P" => _p_upcase::P<'a>,
116         "qAttached" => _qAttached::qAttached,
117         "qfThreadInfo" => _qfThreadInfo::qfThreadInfo,
118         "QStartNoAckMode" => _QStartNoAckMode::QStartNoAckMode,
119         "qsThreadInfo" => _qsThreadInfo::qsThreadInfo,
120         "qSupported" => _qSupported::qSupported<'a>,
121         "qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead,
122         "s" => _s::s<'a>,
123         "T" => _t_upcase::T,
124         "vCont" => _vCont::vCont<'a>,
125         "vKill" => _vKill::vKill,
126         "z" => _z::z<'a>,
127         "Z" => _z_upcase::Z<'a>,
128     }
129 
130     extended_mode use 'a {
131         "!" => exclamation_mark::ExclamationMark,
132         "QDisableRandomization" => _QDisableRandomization::QDisableRandomization,
133         "QEnvironmentHexEncoded" => _QEnvironmentHexEncoded::QEnvironmentHexEncoded<'a>,
134         "QEnvironmentReset" => _QEnvironmentReset::QEnvironmentReset,
135         "QEnvironmentUnset" => _QEnvironmentUnset::QEnvironmentUnset<'a>,
136         "QSetWorkingDir" => _QSetWorkingDir::QSetWorkingDir<'a>,
137         "QStartupWithShell" => _QStartupWithShell::QStartupWithShell,
138         "R" => _r_upcase::R,
139         "vAttach" => _vAttach::vAttach,
140         "vRun" => _vRun::vRun<'a>,
141     }
142 
143     monitor_cmd use 'a {
144         "qRcmd" => _qRcmd::qRcmd<'a>,
145     }
146 
147     section_offsets {
148         "qOffsets" => _qOffsets::qOffsets,
149     }
150 }
151