caniuse_serde/
AgentNameAndVersionSet.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/// Encapsulates choices of Agent and Version of that agent
6#[derive(Default, Debug, Clone)]
7pub struct AgentNameAndVersionSet(HashSet<(AgentName, Version)>);
8
9impl Deref for AgentNameAndVersionSet
10{
11	type Target = HashSet<(AgentName, Version)>;
12	
13	/// Dereferences to HashSet<(AgentName, Version)>
14	#[inline(always)]
15	fn deref(&self) -> &Self::Target
16	{
17		&self.0
18	}
19}
20
21impl AgentNameAndVersionSet
22{
23	/// Find support for implementations of a feature; useful for downstream applications, eg to find prefixes to autoprefix CSS with
24	#[inline(always)]
25	pub fn support_for_a_feature<'a, F: FnMut(&Agent, &Version, &Support)>(&self, can_i_use: &'a CanIUse, feature_name: &'a FeatureName, mut support_user: F)
26	{
27		self.feature(can_i_use, feature_name, |agent, version, support|
28		{
29			if let Some(Some(ref support)) = support
30			{
31				support_user(agent, version, support)
32			}
33		})
34	}
35	
36	/// Find out about a feature
37	#[inline(always)]
38	pub fn feature<'a, F: FnMut(&Agent, &Version, Option<Option<Support>>)>(&self, can_i_use: &'a CanIUse, feature_name: &'a FeatureName, mut feature_implementation_user: F)
39	{
40		if let Some(feature) = feature_name.feature(can_i_use)
41		{
42			for &(ref agent_name, ref version) in self.0.iter()
43			{
44				if let Some(agent) = agent_name.agent(can_i_use)
45				{
46					feature_implementation_user(&agent, version, feature.implementation(agent_name, version));
47				}
48			}
49		}
50	}
51	
52	/// Constructor to use if one of the methods below isn't suitable
53	#[inline(always)]
54	pub fn new(values: HashSet<(AgentName, Version)>) -> Self
55	{
56		AgentNameAndVersionSet(values)
57	}
58	
59	/// A sensible set of choices for an international website in multiple languages
60	#[inline(always)]
61	pub fn a_sensible_set_of_choices_for_an_international_website_in_multiple_languages(can_i_use: &CanIUse, maximum_release_age_from_can_i_use_database_last_updated: Duration, minimum_usage_threshold: UsagePercentage, regional_usages: &[&RegionalUsage]) -> Self
62	{
63		let obsolete_browsers_still_in_use = Self::obsolete_browsers_still_in_use();
64		let browsers_which_underwent_a_major_change_of_rendering_engine = Self::browsers_which_underwent_a_major_change_of_rendering_engine();
65		let automatically_updated_browsers = Self::automatically_updated_browsers();
66		let long_term_releases_of_automatically_updated_browsers = Self::long_term_releases_of_automatically_updated_browsers();
67		let regionally_significant_occasionally_automatically_updated_browsers = Self::regionally_significant_occasionally_automatically_updated_browsers();
68		
69		Self::sensible_choices(can_i_use, maximum_release_age_from_can_i_use_database_last_updated, minimum_usage_threshold, regional_usages, obsolete_browsers_still_in_use, browsers_which_underwent_a_major_change_of_rendering_engine, automatically_updated_browsers, long_term_releases_of_automatically_updated_browsers, regionally_significant_occasionally_automatically_updated_browsers)
70	}
71	
72	/// A sensible set of rules that makes sure:-
73	/// - obsolete but still-used browsers are included
74	/// - browsers with a major change of rendering engine but still-used are included
75	/// - automatically updated or long-term supported browsers are included
76	/// - regionally significant and not necessarily frequently updated browsers are included
77	pub fn sensible_choices(can_i_use: &CanIUse, maximum_release_age_from_can_i_use_database_last_updated: Duration, minimum_usage_threshold: UsagePercentage, regional_usages: &[&RegionalUsage], obsolete_browsers_still_in_use: Self, browsers_which_underwent_a_major_change_of_rendering_engine: Self, automatically_updated_browsers: HashSet<AgentName>, long_term_releases_of_automatically_updated_browsers: HashSet<AgentName>, regionally_significant_occasionally_automatically_updated_browsers: HashSet<AgentName>) -> Self
78	{
79		let mut result = Self::default();
80		
81		for regional_usage in regional_usages.iter()
82		{
83			result.add_just_these_versions_by_usage_percentage(&obsolete_browsers_still_in_use, regional_usage, minimum_usage_threshold);
84			result.add_just_these_versions_by_usage_percentage(&browsers_which_underwent_a_major_change_of_rendering_engine, regional_usage, minimum_usage_threshold);
85			result.add_any_current_or_older_version_by_usage_percentage(&regionally_significant_occasionally_automatically_updated_browsers, regional_usage, minimum_usage_threshold, can_i_use);
86		}
87		
88		let oldest_release_date = match can_i_use.last_updated().checked_sub_signed(maximum_release_age_from_can_i_use_database_last_updated)
89		{
90			Some(oldest_release_date) => oldest_release_date,
91			None => Utc.timestamp(0, 0),
92		};
93		
94		result.add_any_current_or_older_version_by_age(&automatically_updated_browsers, oldest_release_date, can_i_use);
95		result.add_any_current_or_older_version_by_age(&long_term_releases_of_automatically_updated_browsers, oldest_release_date, can_i_use);
96		
97		result
98	}
99	
100	/// Adds browser-version combination if it exceeds minimum usage threshold for region
101	pub fn add_just_these_versions_by_usage_percentage(&mut self, obsolete_or_changed_rendering_engine: &Self, regional_usage: &RegionalUsage, minimum_usage_threshold: UsagePercentage)
102	{
103		for &(ref agent_name, ref version) in obsolete_or_changed_rendering_engine.0.iter()
104		{
105			if let Some(Some(&Some(actual_usage))) = regional_usage.usage_of_version(agent_name, version)
106			{
107				if actual_usage >= minimum_usage_threshold
108				{
109					self.0.insert((agent_name.clone(), version.clone()));
110				}
111			}
112		}
113	}
114	
115	/// Adds browser-version combination for any current or older version if it exceeds or equals minimum usage threshold for region
116	pub fn add_any_current_or_older_version_by_usage_percentage(&mut self, agent_names: &HashSet<AgentName>, regional_usage: &RegionalUsage, minimum_usage_threshold: UsagePercentage, can_i_use: &CanIUse)
117	{
118		for agent_name in agent_names.iter()
119		{
120			if let Some(agent) = agent_name.agent(can_i_use)
121			{
122				agent.version_details_for_current_and_older_versions().for_each(|(version, _version_detail)|
123				{
124					if let Some(Some(&Some(actual_usage))) = regional_usage.usage_of_version(agent_name, version)
125					{
126						if actual_usage >= minimum_usage_threshold
127						{
128							self.0.insert((agent_name.clone(), version.clone()));
129						}
130					}
131				})
132			}
133		}
134	}
135	
136	/// Adds browser-version combination if it exceeds or equals oldest release date
137	pub fn add_any_current_or_older_version_by_age(&mut self, agent_names: &HashSet<AgentName>, oldest_release_date: DateTime<Utc>, can_i_use: &CanIUse)
138	{
139		for agent_name in agent_names.iter()
140		{
141			if let Some(agent) = agent_name.agent(can_i_use)
142			{
143				agent.version_details_for_current_and_older_versions().for_each(|(version, version_detail)|
144				{
145					if let Some(release_date) = version_detail.release_date()
146					{
147						if release_date >= oldest_release_date
148						{
149							self.0.insert((agent_name.clone(), version.clone()));
150						}
151					}
152				})
153			}
154		}
155	}
156	
157	/// Obsolete browsers still in use.
158	/// We need to support the last version of these until its percentage usage falls below X%.
159	/// The percentage usage (X%) should be for a sub-set of the world (ie target audience continents or countries).
160	/// Returns a list of (Agent, Last-Known-Version) pairs.
161	#[inline(always)]
162	pub fn obsolete_browsers_still_in_use() -> Self
163	{
164		use self::AgentName::*;
165		
166		AgentNameAndVersionSet
167		(
168			hashset!
169			(
170				(MicrosoftInternetExplorer, Version::major(11)),
171				(Blackberry, Version::major(10)),
172				(MicrosoftInternetExplorerMobile, Version::major(11)),
173			)
174		)
175	}
176	
177	/// Browsers which underwent a major change of rendering engine.
178	/// We need to support the last version of these until its percentage usage falls below X%.
179	/// The percentage usage (X%) should be for a sub-set of the world (ie target audience continents or countries).
180	/// Returns a list of (Agent, Last-Known-Version-before-change-of-rendering-engine) pairs.
181	#[inline(always)]
182	pub fn browsers_which_underwent_a_major_change_of_rendering_engine() -> Self
183	{
184		use self::AgentName::*;
185		
186		AgentNameAndVersionSet
187		(
188			hashset!
189			(
190				(Opera, Version::major_minor(12, 1)),
191				(GoogleAndroidBrowserAndWebComponent, Version::major_minor_revision(4, 4, 4)),
192				(OperaMobile, Version::major_minor(12, 1)),
193			)
194		)
195	}
196	
197	/// Browsers which are regularly updated, automatically and so which do not 'hang around'.
198	/// These browsers have short-lived, sub-yearly versions
199	/// They are probably best discovered by matching for all released versions after a specific release date (eg 2 years ago)
200	/// Using a percentage isn't wise as usage of each version will change rapidly (from near zero to a few percentage points, then to near zero again), and certainly likely to change more rapidly than static website rebuilds.
201	#[inline(always)]
202	pub fn automatically_updated_browsers() -> HashSet<AgentName>
203	{
204		use self::AgentName::*;
205		
206		hashset!
207		(
208			MicrosoftEdge,
209			MozillaFirefox,
210			GoogleChrome,
211			AppleSafari,
212			Opera,
213			AppleSafariIOs,
214			GoogleAndroidBrowserAndWebComponent,
215			OperaMobile,
216			GoogleChromeAndroid,
217			MozillaFirefoxAndroid,
218		)
219	}
220	
221	/// Long-Term Releases of Automatically Updated Browsers.
222	/// These browsers have occasional long-term releases which are intended to be supported for a year or more.
223	/// Usage percentages for these may be very low globally, and they may be 9 or more release versions 'out-of-date', but they represent an important audience.
224	/// In practice the length of time each long term release is supported for changes with each release, even though vendors have 'long term release policies'.
225	/// This is because policies change in the long interval between long-term releases.
226	/// These browsers are problematic to identify as the caniuse.com database omits them.
227	/// Some long-term release versions differ slightly in supported features, particularly those of a more experimental nature, to their related short-term release cousins (even though they may share the same major version number).
228	/// For Firefox, ESR releases are supposedly for one year (actually, 54 weeks, '9-cycles', with a 12-week ('2-cycle') overlap between releases (a cycle is a Firefox release cycle, typically 6 weeks), but, as always for these sorts of releases, the policy has changed several times.
229	#[inline(always)]
230	pub fn long_term_releases_of_automatically_updated_browsers() -> HashSet<AgentName>
231	{
232		use self::AgentName::*;
233		
234		hashset!
235		(
236			MozillaFirefox,
237		)
238	}
239	
240	/// Regionally significant, occasionally automatically updated browsers.
241	/// Support of these browsers is particularly important for the Indian and Asian markets.
242	/// Many cheaper smart phones come with them (I've used them, too).
243	/// Vendors frequently don't upgrade old firmware installed versions and some older versions may persist and have higher usage for some time than newer ones.
244	/// All of them currently are just more dated versions of the Webkit rendering engine than Chrome.
245	/// These browsers are probably best supported with a 'above X% rule', where X is for any version.
246	#[inline(always)]
247	pub fn regionally_significant_occasionally_automatically_updated_browsers() -> HashSet<AgentName>
248	{
249		use self::AgentName::*;
250		
251		hashset!
252		(
253			UcBrowserAndroid,
254			SamsungBrowserAndroid,
255			QqBrowserAndroid,
256			BaiduBrowserAndroid,
257		)
258	}
259}