1use crate::error::{Error, InternalError};
2use heapless::{String, Vec};
3
4pub trait AtatResp {}
17
18pub trait AtatUrc {
19 type Response: Clone;
21
22 fn parse(resp: &[u8]) -> Option<Self::Response>;
24}
25
26pub trait AtatCmd {
64 type Response: AtatResp;
66
67 const MAX_LEN: usize;
69
70 const CAN_ABORT: bool = false;
72
73 const MAX_TIMEOUT_MS: u32 = 1000;
75
76 const ATTEMPTS: u8 = 1;
79
80 const REATTEMPT_ON_PARSE_ERR: bool = true;
83
84 const EXPECTS_RESPONSE_CODE: bool = true;
88
89 fn write(&self, buf: &mut [u8]) -> usize;
91
92 fn parse(&self, resp: Result<&[u8], InternalError>) -> Result<Self::Response, Error>;
94}
95
96impl<T, const L: usize> AtatResp for Vec<T, L> where T: AtatResp {}
97
98impl<const L: usize> AtatResp for String<L> {}
99
100impl<const L: usize> AtatCmd for String<L> {
101 type Response = String<256>;
102 const MAX_LEN: usize = L;
103
104 fn write(&self, buf: &mut [u8]) -> usize {
105 let bytes = self.as_bytes();
106 let len = bytes.len();
107 buf[..len].copy_from_slice(bytes);
108 len
109 }
110
111 fn parse(&self, resp: Result<&[u8], InternalError>) -> Result<Self::Response, Error> {
112 let utf8_string =
113 core::str::from_utf8(resp.map_err(Error::from)?).map_err(|_| Error::Parse)?;
114 String::try_from(utf8_string).map_err(|_| Error::Parse)
115 }
116}
117
118#[cfg(all(test, feature = "derive"))]
119mod test {
120 use super::*;
121 use crate as atat;
122 use atat_derive::{AtatEnum, AtatResp};
123 use heapless::String;
124
125 #[derive(Debug, Clone, PartialEq, AtatEnum)]
126 pub enum PDPContextStatus {
127 Deactivated = 0,
129 Activated = 1,
131 }
132
133 #[derive(Debug, Clone, AtatResp, PartialEq)]
134 pub struct PDPContextState {
135 #[at_arg(position = 0)]
136 pub cid: u8,
137 #[at_arg(position = 1)]
138 pub status: PDPContextStatus,
139 }
140
141 #[derive(Debug, Clone, AtatResp, PartialEq)]
142 pub struct PDPContextDefinition {
143 #[at_arg(position = 0)]
144 pub cid: u8,
145 #[at_arg(position = 1)]
146 pub pdp_type: String<6>,
147 #[at_arg(position = 2)]
148 pub apn: String<99>,
149 #[at_arg(position = 3)]
150 pub pdp_addr: String<99>,
151 #[at_arg(position = 4)]
152 pub d_comp: u8,
153 #[at_arg(position = 5)]
154 pub h_comp: u8,
155 #[at_arg(position = 6)]
156 pub ipv4_addr_alloc: Option<u8>,
157 #[at_arg(position = 7)]
158 pub emergency_indication: Option<u8>,
159 #[at_arg(position = 8)]
160 pub p_cscf_discovery: Option<u8>,
161 #[at_arg(position = 9)]
162 pub im_cn_signalling_flag_ind: Option<u8>,
163 }
166
167 #[test]
168 fn single_multi_response() {
169 let mut v = Vec::<_, 1>::from_slice(&[PDPContextState {
170 cid: 1,
171 status: PDPContextStatus::Deactivated,
172 }])
173 .unwrap();
174
175 let mut resp: Vec<PDPContextState, 1> = serde_at::from_slice(b"+CGACT: 1,0\r\n").unwrap();
176
177 assert_eq!(resp.pop(), v.pop());
178 assert_eq!(resp.pop(), None);
179 }
180
181 #[test]
182 fn multi_response() {
183 let mut v = Vec::<_, 3>::from_slice(&[
184 PDPContextState {
185 cid: 1,
186 status: PDPContextStatus::Deactivated,
187 },
188 PDPContextState {
189 cid: 2,
190 status: PDPContextStatus::Activated,
191 },
192 PDPContextState {
193 cid: 3,
194 status: PDPContextStatus::Deactivated,
195 },
196 ])
197 .unwrap();
198
199 let mut resp: Vec<PDPContextState, 3> =
200 serde_at::from_slice(b"+CGACT: 1,0\r\n+CGACT: 2,1\r\n+CGACT: 3,0").unwrap();
201
202 assert_eq!(resp.pop(), v.pop());
203 assert_eq!(resp.pop(), v.pop());
204 assert_eq!(resp.pop(), v.pop());
205 assert_eq!(resp.pop(), None);
206 }
207
208 #[test]
209 fn multi_response_advanced() {
210 let mut v = Vec::<_, 3>::from_slice(&[
211 PDPContextDefinition {
212 cid: 2,
213 pdp_type: String::try_from("IP").unwrap(),
214 apn: String::try_from("em").unwrap(),
215 pdp_addr: String::try_from("100.92.188.66").unwrap(),
216 d_comp: 0,
217 h_comp: 0,
218 ipv4_addr_alloc: Some(0),
219 emergency_indication: Some(0),
220 p_cscf_discovery: Some(0),
221 im_cn_signalling_flag_ind: Some(0),
222 },
223 PDPContextDefinition {
224 cid: 1,
225 pdp_type: String::try_from("IP").unwrap(),
226 apn: String::try_from("STATREAL").unwrap(),
227 pdp_addr: String::try_from("0.0.0.0").unwrap(),
228 d_comp: 0,
229 h_comp: 0,
230 ipv4_addr_alloc: None,
231 emergency_indication: None,
232 p_cscf_discovery: None,
233 im_cn_signalling_flag_ind: None,
234 },
235 PDPContextDefinition {
236 cid: 3,
237 pdp_type: String::try_from("IP").unwrap(),
238 apn: String::try_from("tim.ibox.it").unwrap(),
239 pdp_addr: String::try_from("0.0.0.0").unwrap(),
240 d_comp: 0,
241 h_comp: 0,
242 ipv4_addr_alloc: None,
243 emergency_indication: None,
244 p_cscf_discovery: None,
245 im_cn_signalling_flag_ind: None,
246 },
247 ])
248 .unwrap();
249
250 let mut resp: Vec<PDPContextDefinition, 3> =
251 serde_at::from_slice(b"+CGDCONT: 2,\"IP\",\"em\",\"100.92.188.66\",0,0,0,0,0,0\r\n+CGDCONT: 1,\"IP\",\"STATREAL\",\"0.0.0.0\",0,0\r\n+CGDCONT: 3,\"IP\",\"tim.ibox.it\",\"0.0.0.0\",0,0").unwrap();
252
253 assert_eq!(resp.pop(), v.pop());
254 assert_eq!(resp.pop(), v.pop());
255 assert_eq!(resp.pop(), v.pop());
256 assert_eq!(resp.pop(), None);
257 }
258}