wgtk/net/element/
entity.rs

1//! Base traits and functionalities for method (calls) and property codecs.
2
3use std::io::{self, Write, Read};
4
5use crate::net::bundle::{BundleElementWriter, TopElementReader, BundleElement, BundleResult};
6use crate::util::io::*;
7
8use super::{ElementLength, ElementIdRange, Element, TopElement};
9
10
11/// A trait to be implemented on enumerations of method calls.
12pub trait MethodCall: Sized {
13
14    /// Return the total number of exposed methods for this type of methods.
15    fn count() -> u16;
16
17    /// Return the index of the method.
18    fn index(&self) -> u16;
19
20    /// Return the length for a given method index.
21    fn len(index: u16) -> ElementLength;
22
23    /// Encode the method with the given writer.
24    fn encode(&self, write: &mut impl Write) -> io::Result<()>;
25
26    /// Decode the method with the given reader, length and for a specific index.
27    fn decode(read: &mut impl Read, len: usize, index: u16) -> io::Result<Self>;
28
29}
30
31
32/// Trait to implement on extension elements that is used together with
33/// [`MethodCallWrapper`] to encode or decode generic method call elements.
34pub trait MethodCallExt: TopElement<Config = ()> {
35    const ID_RANGE: ElementIdRange;
36}
37
38
39/// This special bundle's element can be used to call a method either on
40/// the server or on the client. Its interface is quite complicated, so
41/// it's only internal and must be used through public functions.
42pub struct MethodCallWrapper<M, P>
43where
44    M: MethodCall,
45    P: MethodCallExt,
46{
47    /// The actual wrapped method call.
48    pub method: M,
49    /// The extension element that is encoded or decoded just before the
50    /// actual method call.
51    pub ext: P,
52}
53
54impl<M, P> MethodCallWrapper<M, P>
55where
56    M: MethodCall,
57    P: MethodCallExt,
58{
59
60    /// The length type to use if now particular length is required, this length 
61    /// defaults to a callback which returns a length depending on the id and the
62    /// use of sub-slot to represent the method's id.
63    pub const DEFAULT_LEN: ElementLength = ElementLength::Callback(|id| {
64        
65        // This element's length is determined by this callback, so we are relying
66        // on the element's id. If this id is a full-slot, we get the length of the
67        // method call, but if a sub-slot is needed for encoding the sub-id, the 
68        // length of the element is always 16-bits variable.
69        if let Some(exposed_id) = P::ID_RANGE.to_exposed_id_checked(M::count(), id) {
70            M::len(exposed_id)
71        } else {
72            ElementLength::Variable16
73        }
74
75    });
76
77    pub fn new(method: M, prefix: P) -> Self {
78        Self { method, ext: prefix }
79    }
80
81    pub fn write(self, mut writer: BundleElementWriter) {
82
83        let (
84            element_id, 
85            sub_id
86        ) = P::ID_RANGE.from_exposed_id(M::count(), self.method.index());
87    
88        writer.write(element_id, self, &(0, sub_id));
89
90    }
91
92    pub fn read(reader: TopElementReader) -> BundleResult<BundleElement<Self>> {
93        let element_id = reader.id();
94        reader.read::<Self>(&(element_id, None))
95    }
96
97}
98
99impl<M, P> Element for MethodCallWrapper<M, P>
100where
101    M: MethodCall,
102    P: MethodCallExt,
103{
104
105    /// This contains (elt_id, sub_id):
106    /// - Element id is required when decoding;
107    /// - Sub id optional when encoding.
108    type Config = (u8, Option<u8>);
109
110    fn encode(&self, write: &mut impl Write, config: &Self::Config) -> io::Result<()> {
111
112        self.ext.encode(write, &())?;
113
114        // Write the sub-id if required.
115        if let Some(sub_id) = config.1 {
116            write.write_u8(sub_id)?;
117        }
118
119        self.method.encode(write)
120
121    }
122
123    fn decode(read: &mut impl Read, mut len: usize, config: &Self::Config) -> io::Result<Self> {
124
125        // Decode the prefix with a counter reader in order to adjust the length afterward.
126        let mut prefix_read = IoCounter::new(&mut *read);
127        let prefix = P::decode(&mut prefix_read, len, &())?;
128        len -= prefix_read.count();
129
130        let mut sub_id_err = None;
131        let exposed_id = P::ID_RANGE.to_exposed_id(M::count(), config.0, || {
132            // Sub-id is needed.
133            len -= 1;
134            match read.read_u8() {
135                Ok(n) => n,
136                Err(e) => {
137                    sub_id_err = Some(e);
138                    0  // Return zero, this value will not be used anyway.
139                }
140            }
141        });
142
143        // Propagate error.
144        if let Some(e) = sub_id_err {
145            return Err(e);
146        }
147
148        M::decode(read, len, exposed_id).map(|method| Self { 
149            method, 
150            ext: prefix,
151        })
152
153    }
154
155}
156
157impl<M, P> TopElement for MethodCallWrapper<M, P>
158where
159    M: MethodCall,
160    P: MethodCallExt,
161{
162    const LEN: ElementLength = P::LEN;
163}