relevancy/
rs.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 */
5
6use crate::{Error, Result};
7use remote_settings::{RemoteSettingsClient, RemoteSettingsRecord};
8use serde::Deserialize;
9/// The Remote Settings collection name.
10pub(crate) const REMOTE_SETTINGS_COLLECTION: &str = "content-relevance";
11
12/// A trait for a client that downloads records from Remote Settings.
13///
14/// This trait lets tests use a mock client.
15pub(crate) trait RelevancyRemoteSettingsClient {
16    /// Fetches records from the Suggest Remote Settings collection.
17    fn get_records(&self) -> Result<Vec<RemoteSettingsRecord>>;
18
19    /// Fetches a record's attachment from the Suggest Remote Settings
20    /// collection.
21    fn get_attachment(&self, location: &RemoteSettingsRecord) -> Result<Vec<u8>>;
22
23    /// Close any open resources
24    fn close(&self);
25}
26
27impl RelevancyRemoteSettingsClient for RemoteSettingsClient {
28    fn get_records(&self) -> Result<Vec<RemoteSettingsRecord>> {
29        self.sync()?;
30        Ok(self
31            .get_records(false)
32            .expect("RemoteSettingsClient::get_records() returned None after `sync()` called"))
33    }
34
35    fn get_attachment(&self, record: &RemoteSettingsRecord) -> Result<Vec<u8>> {
36        Ok(self.get_attachment(record)?)
37    }
38
39    fn close(&self) {
40        self.shutdown()
41    }
42}
43
44impl<T: RelevancyRemoteSettingsClient> RelevancyRemoteSettingsClient for &T {
45    fn get_records(&self) -> Result<Vec<RemoteSettingsRecord>> {
46        (*self).get_records()
47    }
48
49    fn get_attachment(&self, record: &RemoteSettingsRecord) -> Result<Vec<u8>> {
50        (*self).get_attachment(record)
51    }
52
53    fn close(&self) {
54        (*self).close();
55    }
56}
57
58impl<T: RelevancyRemoteSettingsClient> RelevancyRemoteSettingsClient for std::sync::Arc<T> {
59    fn get_records(&self) -> Result<Vec<RemoteSettingsRecord>> {
60        (**self).get_records()
61    }
62
63    fn get_attachment(&self, record: &RemoteSettingsRecord) -> Result<Vec<u8>> {
64        (**self).get_attachment(record)
65    }
66
67    fn close(&self) {
68        (**self).close()
69    }
70}
71
72#[derive(Clone, Debug, Deserialize)]
73pub struct RelevancyRecord {
74    #[allow(dead_code)]
75    #[serde(rename = "type")]
76    pub record_type: String,
77    pub record_custom_details: RecordCustomDetails,
78}
79
80// Custom details related to category of the record.
81#[derive(Clone, Debug, Deserialize)]
82pub struct RecordCustomDetails {
83    pub category_to_domains: CategoryToDomains,
84}
85
86/// Category information related to the record.
87#[derive(Clone, Debug, Deserialize)]
88pub struct CategoryToDomains {
89    #[allow(dead_code)]
90    pub version: i32,
91    #[allow(dead_code)]
92    pub category: String,
93    pub category_code: i32,
94}
95
96/// A downloaded Remote Settings attachment that contains domain data.
97#[derive(Clone, Debug, Deserialize)]
98pub struct RelevancyAttachmentData {
99    pub domain: String,
100}
101
102/// Deserialize one of these types from a JSON value
103pub fn from_json<T: serde::de::DeserializeOwned>(value: serde_json::Value) -> Result<T> {
104    serde_path_to_error::deserialize(value).map_err(|e| Error::RemoteSettingsParseError {
105        type_name: std::any::type_name::<T>().to_owned(),
106        path: e.path().to_string(),
107        error: e.into_inner(),
108    })
109}
110
111/// Deserialize one of these types from a slice of JSON data
112pub fn from_json_slice<T: serde::de::DeserializeOwned>(value: &[u8]) -> Result<T> {
113    let json_value =
114        serde_json::from_slice(value).map_err(|e| Error::RemoteSettingsParseError {
115            type_name: std::any::type_name::<T>().to_owned(),
116            path: "<while parsing JSON>".to_owned(),
117            error: e,
118        })?;
119    from_json(json_value)
120}
121
122#[cfg(test)]
123pub mod test {
124    use super::*;
125
126    // Type that implements RelevancyRemoteSettingsClient, but panics if the methods are actually
127    // called.  This is used in tests that need to construct a `RelevancyStore`, but don't want to
128    // construct an actual remote settings client.
129    pub struct NullRelavancyRemoteSettingsClient;
130
131    impl RelevancyRemoteSettingsClient for NullRelavancyRemoteSettingsClient {
132        fn get_records(&self) -> Result<Vec<RemoteSettingsRecord>> {
133            panic!("NullRelavancyRemoteSettingsClient::get_records was called")
134        }
135
136        fn get_attachment(&self, _record: &RemoteSettingsRecord) -> Result<Vec<u8>> {
137            panic!("NullRelavancyRemoteSettingsClient::get_records was called")
138        }
139
140        fn close(&self) {}
141    }
142}