Skip to main content

rustbac_core/services/
object_management.rs

1use crate::apdu::ConfirmedRequestHeader;
2use crate::encoding::{
3    primitives::{decode_unsigned, encode_ctx_object_id, encode_ctx_unsigned},
4    reader::Reader,
5    tag::Tag,
6    writer::Writer,
7};
8use crate::types::{ObjectId, ObjectType};
9use crate::{DecodeError, EncodeError};
10
11pub const SERVICE_CREATE_OBJECT: u8 = 0x0A;
12pub const SERVICE_DELETE_OBJECT: u8 = 0x0B;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum CreateObjectSpecifier {
16    ObjectType(ObjectType),
17    ObjectId(ObjectId),
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct CreateObjectRequest {
22    pub specifier: CreateObjectSpecifier,
23    pub invoke_id: u8,
24}
25
26impl CreateObjectRequest {
27    pub fn by_type(object_type: ObjectType, invoke_id: u8) -> Self {
28        Self {
29            specifier: CreateObjectSpecifier::ObjectType(object_type),
30            invoke_id,
31        }
32    }
33
34    pub fn by_id(object_id: ObjectId, invoke_id: u8) -> Self {
35        Self {
36            specifier: CreateObjectSpecifier::ObjectId(object_id),
37            invoke_id,
38        }
39    }
40
41    pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
42        ConfirmedRequestHeader {
43            segmented: false,
44            more_follows: false,
45            segmented_response_accepted: true,
46            max_segments: 0,
47            max_apdu: 5,
48            invoke_id: self.invoke_id,
49            sequence_number: None,
50            proposed_window_size: None,
51            service_choice: SERVICE_CREATE_OBJECT,
52        }
53        .encode(w)?;
54
55        match self.specifier {
56            CreateObjectSpecifier::ObjectType(object_type) => {
57                encode_ctx_unsigned(w, 0, object_type.to_u16() as u32)?
58            }
59            CreateObjectSpecifier::ObjectId(object_id) => {
60                encode_ctx_object_id(w, 1, object_id.raw())?
61            }
62        }
63        Ok(())
64    }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct CreateObjectAck {
69    pub object_id: ObjectId,
70}
71
72impl CreateObjectAck {
73    pub fn decode_after_header(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
74        let object_id = match Tag::decode(r)? {
75            Tag::Context { tag_num: 0, len } => {
76                if len != 4 {
77                    return Err(DecodeError::InvalidLength);
78                }
79                ObjectId::from_raw(decode_unsigned(r, len as usize)?)
80            }
81            _ => return Err(DecodeError::InvalidTag),
82        };
83        Ok(Self { object_id })
84    }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub struct DeleteObjectRequest {
89    pub object_id: ObjectId,
90    pub invoke_id: u8,
91}
92
93impl DeleteObjectRequest {
94    pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
95        ConfirmedRequestHeader {
96            segmented: false,
97            more_follows: false,
98            segmented_response_accepted: false,
99            max_segments: 0,
100            max_apdu: 5,
101            invoke_id: self.invoke_id,
102            sequence_number: None,
103            proposed_window_size: None,
104            service_choice: SERVICE_DELETE_OBJECT,
105        }
106        .encode(w)?;
107        encode_ctx_object_id(w, 0, self.object_id.raw())
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::{
114        CreateObjectAck, CreateObjectRequest, DeleteObjectRequest, SERVICE_CREATE_OBJECT,
115        SERVICE_DELETE_OBJECT,
116    };
117    use crate::apdu::{ComplexAckHeader, ConfirmedRequestHeader};
118    use crate::encoding::primitives::encode_ctx_object_id;
119    use crate::encoding::{reader::Reader, writer::Writer};
120    use crate::types::{ObjectId, ObjectType};
121
122    #[test]
123    fn encode_create_object_request() {
124        let req = CreateObjectRequest::by_type(ObjectType::AnalogValue, 3);
125        let mut buf = [0u8; 64];
126        let mut w = Writer::new(&mut buf);
127        req.encode(&mut w).unwrap();
128        let mut r = Reader::new(w.as_written());
129        let hdr = ConfirmedRequestHeader::decode(&mut r).unwrap();
130        assert_eq!(hdr.service_choice, SERVICE_CREATE_OBJECT);
131    }
132
133    #[test]
134    fn encode_delete_object_request() {
135        let req = DeleteObjectRequest {
136            object_id: ObjectId::new(ObjectType::AnalogValue, 9),
137            invoke_id: 5,
138        };
139        let mut buf = [0u8; 64];
140        let mut w = Writer::new(&mut buf);
141        req.encode(&mut w).unwrap();
142        let mut r = Reader::new(w.as_written());
143        let hdr = ConfirmedRequestHeader::decode(&mut r).unwrap();
144        assert_eq!(hdr.service_choice, SERVICE_DELETE_OBJECT);
145    }
146
147    #[test]
148    fn decode_create_object_ack() {
149        let mut buf = [0u8; 64];
150        let mut w = Writer::new(&mut buf);
151        ComplexAckHeader {
152            segmented: false,
153            more_follows: false,
154            invoke_id: 3,
155            sequence_number: None,
156            proposed_window_size: None,
157            service_choice: SERVICE_CREATE_OBJECT,
158        }
159        .encode(&mut w)
160        .unwrap();
161        encode_ctx_object_id(&mut w, 0, ObjectId::new(ObjectType::AnalogValue, 9).raw()).unwrap();
162        let mut r = Reader::new(w.as_written());
163        let _ack_hdr = ComplexAckHeader::decode(&mut r).unwrap();
164        let ack = CreateObjectAck::decode_after_header(&mut r).unwrap();
165        assert_eq!(ack.object_id, ObjectId::new(ObjectType::AnalogValue, 9));
166    }
167}