caniuse_serde/
CanIUse.rs

1// This file is part of caniuse-serde. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/caniuse-serde/master/COPYRIGHT. No part of predicator, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
2// Copyright © 2017 The developers of caniuse-serde. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/caniuse-serde/master/COPYRIGHT.
3
4
5/// A database of data relating to caniuse.com
6/// Not used directly, but references should be passed to methods on AgentName, FeatureName, EraName, and, less usefully, Status and ParentCategory.
7#[derive(Deserialize, Debug, Clone)]
8pub struct CanIUse
9{
10	agents: HashMap<AgentName, AgentDetail>,
11	statuses: HashMap<Status, String>,
12	#[serde(rename = "cats")] child_categories: HashMap<ParentCategory, Vec<Category>>,
13	#[serde(deserialize_with = "CanIUse::updated_deserialize")] updated: DateTime<Utc>,
14	#[serde(rename = "data")] features: HashMap<FeatureName, FeatureDetail>,
15}
16
17impl Default for CanIUse
18{
19	/// Defaults to the up-to-date version of the caniuse.com database shipped embedded in this crate.
20	#[inline(always)]
21	fn default() -> Self
22	{
23		match include_str!("data-2.0.json").parse()
24		{
25			Err(error) => panic!("Invalid data embedded: {}", error),
26			Ok(canIUse) => canIUse
27		}
28	}
29}
30
31impl FromStr for CanIUse
32{
33	type Err = ::serde_json::error::Error;
34	
35	/// Deserialize a CanIUse database from a UTF-8 string representing the contents of a `data-2.0.json` file (typically in `fulldata-json/`).
36	#[inline(always)]
37	fn from_str(can_i_use_database_json: &str) -> Result<Self, Self::Err>
38	{
39		::serde_json::from_str(can_i_use_database_json)
40	}
41}
42
43impl CanIUse
44{
45	/// Deserialize a CanIUse database from a file path to a `data-2.0.json` file (typically in `fulldata-json/`).
46	#[inline(always)]
47	pub fn from_path<P: AsRef<Path>>(can_i_use_database_file_path: P) -> Result<Self, Box<::std::error::Error>>
48	{
49		Self::from_reader(File::open(can_i_use_database_file_path)?)
50	}
51	
52	/// Deserialize a CanIUse database from a readable stream of raw JSON bytes.
53	#[inline(always)]
54	pub fn from_reader<R: Read>(reader_of_stream_of_can_i_use_json_bytes: R) -> Result<Self, Box<::std::error::Error>>
55	{
56		Ok(serde_json::from_reader(reader_of_stream_of_can_i_use_json_bytes)?)
57	}
58	
59	/// Deserialize a CanIUse database from a slice of raw JSON bytes.
60	#[inline(always)]
61	pub fn from_slice(raw_can_i_use_json_bytes: &[u8]) -> Result<Self, ::serde_json::error::Error>
62	{
63		Ok(serde_json::from_slice(raw_can_i_use_json_bytes)?)
64	}
65	
66	/// A timestamp of when this particular database was last updated.
67	#[inline(always)]
68	pub fn last_updated(&self) -> DateTime<Utc>
69	{
70		self.updated
71	}
72	
73	/// An iterator over the AgentNames known in this caniuse.com database
74	#[inline(always)]
75	pub fn known_agent_names(&self) -> AgentNameIterator
76	{
77		AgentNameIterator(self.agents.keys())
78	}
79	
80	/// An iterator over the AgentNames known in this caniuse.com database
81	#[inline(always)]
82	pub fn known_statuses(&self) -> StatusIterator
83	{
84		StatusIterator(self.statuses.keys())
85	}
86	
87	/// An iterator over the AgentNames known in this caniuse.com database
88	#[inline(always)]
89	pub fn known_parent_categories(&self) -> ParentCategoryIterator
90	{
91		ParentCategoryIterator(self.child_categories.keys())
92	}
93	
94	/// An iterator over the AgentNames known in this caniuse.com database
95	#[inline(always)]
96	pub fn known_feature_names(&self) -> FeatureNameIterator
97	{
98		FeatureNameIterator(self.features.keys())
99	}
100	
101	#[inline(always)]
102	fn agent<'a>(&'a self, agent_name: &'a AgentName) -> Option<Agent<'a>>
103	{
104		self.agents.get(agent_name).map(|agent_detail| Agent
105		{
106			agent_name,
107			agent_detail,
108		})
109	}
110	
111	#[inline(always)]
112	fn feature<'a>(&'a self, feature_name: &'a FeatureName) -> Option<Feature<'a>>
113	{
114		self.features.get(feature_name).map(|feature_detail| Feature
115		{
116			feature_name,
117			feature_detail,
118		})
119	}
120	
121	#[inline(always)]
122	fn status_description<'a>(&'a self, statusName: &Status) -> Option<&'a str>
123	{
124		self.statuses.get(statusName).map(|value| value.as_str())
125	}
126	
127	#[inline(always)]
128	fn child_categories<'a>(&'a self, parentCategory: &ParentCategory) -> Option<&'a [Category]>
129	{
130		self.child_categories.get(parentCategory).map(|value| &value[..])
131	}
132	
133	#[inline(always)]
134	fn updated_deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
135	{
136		struct OurVisitor;
137		
138		impl<'de> Visitor<'de> for OurVisitor
139		{
140			type Value = u64;
141			
142			fn expecting(&self, formatter: &mut Formatter) -> fmt::Result
143			{
144				formatter.write_str("a positive integer")
145			}
146			
147			fn visit_u8<E: de::Error>(self, value: u8) -> Result<Self::Value, E>
148			{
149				Ok(value as Self::Value)
150			}
151			
152			fn visit_u16<E: de::Error>(self, value: u16) -> Result<Self::Value, E>
153			{
154				Ok(value as Self::Value)
155			}
156			
157			fn visit_u32<E: de::Error>(self, value: u32) -> Result<Self::Value, E>
158			{
159				Ok(value as Self::Value)
160			}
161			
162			fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E>
163			{
164				Ok(value)
165			}
166			
167			fn visit_i8<E: de::Error>(self, value: i8) -> Result<Self::Value, E>
168			{
169				if value >= 0
170				{
171					Ok(value as Self::Value)
172				}
173				else
174				{
175					Err(E::custom(format!("i8 should not be negative '{}'", value)))
176				}
177			}
178			
179			fn visit_i16<E: de::Error>(self, value: i16) -> Result<Self::Value, E>
180			{
181				if value >= 0
182				{
183					Ok(value as Self::Value)
184				}
185				else
186				{
187					Err(E::custom(format!("i16 should not be negative '{}'", value)))
188				}
189			}
190			
191			fn visit_i32<E: de::Error>(self, value: i32) -> Result<Self::Value, E>
192			{
193				if value >= 0
194				{
195					Ok(value as Self::Value)
196				}
197				else
198				{
199					Err(E::custom(format!("i32 should not be negative '{}'", value)))
200				}
201			}
202			
203			fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E>
204			{
205				if value >= 0
206				{
207					Ok(value as Self::Value)
208				}
209				else
210				{
211					Err(E::custom(format!("i64 should not be negative '{}'", value)))
212				}
213			}
214		}
215		
216		let time = deserializer.deserialize_u64(OurVisitor)?;
217		// Cast here is deliberate; we deliberately parse expecting a non-negative timestamp
218		let utc = Utc;
219		Ok(utc.timestamp(time as i64, 0))
220	}
221}
222
223lazy_static!
224{
225	/// The up-to-date version of the caniuse.com database shipped embedded in this crate.
226	#[derive(Debug)] pub static ref EmbeddedCanIUseDatabase: CanIUse = CanIUse::default();
227}