dsf_core/service/
publisher.rs

1use core::ops::Add;
2
3#[cfg(feature = "std")]
4use std::time::{Duration, SystemTime};
5
6#[cfg(feature = "alloc")]
7use alloc::prelude::v1::*;
8
9use crate::base::{Base, Body, Header, PrivateOptions};
10use crate::error::Error;
11use crate::options::Options;
12use crate::page::{Page, PageInfo, PageOptions};
13use crate::service::Service;
14use crate::types::*;
15
16/// Publisher trait allows services to generate primary, data, and secondary pages
17/// as well as to encode (and sign and optionally encrypt) generated pages
18pub trait Publisher {
19    /// Generates a primary page to publish for the given service and encodes it into the provided buffer
20    fn publish_primary<T: AsRef<[u8]> + AsMut<[u8]>>(
21        &mut self,
22        buff: T,
23    ) -> Result<(usize, Page), Error>;
24
25    /// Create a data object for publishing with the provided options and encodes it into the provided buffer
26    fn publish_data<T: AsRef<[u8]> + AsMut<[u8]>>(
27        &mut self,
28        options: DataOptions,
29        buff: T,
30    ) -> Result<(usize, Page), Error>;
31
32    /// Create a secondary page for publishing with the provided options and encodes it into the provided buffer
33    fn publish_secondary<T: AsRef<[u8]> + AsMut<[u8]>>(
34        &mut self,
35        id: &Id,
36        options: SecondaryOptions,
37        buff: T,
38    ) -> Result<(usize, Page), Error>;
39}
40
41#[derive(Clone)]
42pub struct SecondaryOptions {
43    /// Application ID of primary service
44    pub application_id: u16,
45
46    /// Page object kind
47    pub page_kind: Kind,
48
49    /// Page version
50    /// This is monotonically increased for any successive publishing of the same page
51    pub version: u16,
52
53    /// Page body
54    pub body: Body,
55
56    /// Page publish time
57    pub issued: Option<DateTime>,
58
59    /// Page expiry time
60    pub expiry: Option<DateTime>,
61
62    /// Public options attached to the page
63    pub public_options: Vec<Options>,
64
65    /// Private options attached to the page
66    pub private_options: Vec<Options>,
67}
68
69impl Default for SecondaryOptions {
70    fn default() -> Self {
71        Self {
72            application_id: 0,
73            page_kind: PageKind::Generic.into(),
74            version: 0,
75            body: Body::None,
76            issued: None,
77            expiry: None,
78            public_options: vec![],
79            private_options: vec![],
80        }
81    }
82}
83
84#[derive(Clone)]
85pub struct DataOptions {
86    /// Data object kind
87    pub data_kind: Kind,
88
89    /// Data object body
90    pub body: Body,
91
92    /// Data publish time
93    pub issued: Option<DateTime>,
94
95    /// Data expiry time
96    pub expiry: Option<DateTime>,
97
98    /// Public options attached to the data object
99    pub public_options: Vec<Options>,
100
101    /// Private options attached to the data object
102    pub private_options: Vec<Options>,
103}
104
105impl Default for DataOptions {
106    fn default() -> Self {
107        Self {
108            data_kind: DataKind::Generic.into(),
109            body: Body::None,
110            issued: None,
111            expiry: None,
112            public_options: vec![],
113            private_options: vec![],
114        }
115    }
116}
117
118impl Publisher for Service {
119    /// Publish generates a page to publishing for the given service.
120    fn publish_primary<T: AsRef<[u8]> + AsMut<[u8]>>(
121        &mut self,
122        buff: T,
123    ) -> Result<(usize, Page), Error> {
124        let mut flags = Flags::default();
125        if self.encrypted {
126            flags |= Flags::ENCRYPTED;
127        }
128
129        let header = Header {
130            application_id: self.application_id,
131            kind: self.kind.into(),
132            index: self.version,
133            flags,
134            ..Default::default()
135        };
136
137        let page_options = PageOptions {
138            // TODO: re-enable page issue / expiry for no-std
139            #[cfg(feature = "std")]
140            issued: Some(SystemTime::now().into()),
141            #[cfg(feature = "std")]
142            expiry: Some(
143                SystemTime::now()
144                    .add(Duration::from_secs(24 * 60 * 60))
145                    .into(),
146            ),
147            ..Default::default()
148        };
149
150        // Build page
151        let mut p = Page::new(
152            self.id.clone(),
153            header,
154            PageInfo::primary(self.public_key.clone()),
155            self.body.clone(),
156            page_options,
157        );
158
159        self.encode(&mut p, buff).map(|n| (n, p))
160    }
161
162    /// Secondary generates a secondary page using this service to be attached to / stored at the provided service ID
163    fn publish_secondary<T: AsRef<[u8]> + AsMut<[u8]>>(
164        &mut self,
165        id: &Id,
166        options: SecondaryOptions,
167        buff: T,
168    ) -> Result<(usize, Page), Error> {
169        let mut flags = Flags::SECONDARY;
170        if self.encrypted {
171            flags |= Flags::ENCRYPTED;
172        }
173
174        flags |= Flags::SECONDARY;
175
176        assert!(options.page_kind.is_page());
177
178        let header = Header {
179            application_id: self.application_id,
180            kind: options.page_kind,
181            flags,
182            index: options.version,
183            ..Default::default()
184        };
185
186        let page_options = PageOptions {
187            public_options: options.public_options,
188            private_options: PrivateOptions::Cleartext(options.private_options),
189            // TODO: Re-enable issued time
190            #[cfg(feature = "std")]
191            issued: Some(SystemTime::now().into()),
192            expiry: options.expiry,
193            ..Default::default()
194        };
195
196        let mut page = Page::new(
197            id.clone(),
198            header,
199            PageInfo::secondary(self.id.clone()),
200            options.body,
201            page_options,
202        );
203
204        self.encode(&mut page, buff).map(|n| (n, page))
205    }
206
207    fn publish_data<T: AsRef<[u8]> + AsMut<[u8]>>(
208        &mut self,
209        options: DataOptions,
210        buff: T,
211    ) -> Result<(usize, Page), Error> {
212        let mut flags = Flags::default();
213        if self.encrypted {
214            flags |= Flags::ENCRYPTED;
215        }
216
217        assert!(options.data_kind.is_data());
218
219        self.data_index += 1;
220
221        let header = Header {
222            application_id: self.application_id,
223            kind: options.data_kind,
224            flags,
225            index: self.data_index,
226            ..Default::default()
227        };
228
229        let page_options = PageOptions {
230            public_options: options.public_options,
231            private_options: PrivateOptions::Cleartext(options.private_options),
232            #[cfg(feature = "std")]
233            issued: Some(SystemTime::now().into()),
234            ..Default::default()
235        };
236
237        let mut p = Page::new(
238            self.id.clone(),
239            header,
240            PageInfo::Data(()),
241            options.body,
242            page_options,
243        );
244
245        self.encode(&mut p, buff).map(|n| (n, p))
246    }
247}
248
249impl Service {
250    // Encode a page to the provided buffer, updating the internal signature state
251    fn encode<T: AsRef<[u8]> + AsMut<[u8]>>(
252        &mut self,
253        page: &mut Page,
254        buff: T,
255    ) -> Result<usize, Error> {
256        // Map page to base object
257        let mut b = Base::from(&*page);
258
259        // Attach previous signature
260        b.parent = self.last_sig.clone();
261
262        // Encode and sign object
263        let n = b.encode(self.private_key.as_ref(), self.secret_key.as_ref(), buff)?;
264
265        // Update service last_sig
266        self.last_sig = b.signature;
267
268        // Attach page sig
269        page.signature = self.last_sig.clone();
270
271        // TODO: should we attach the raw object here..?
272
273        Ok(n)
274    }
275}