[librsvg: 13/23] Use a new Language / UserLanguage enum instead of LanguageTags everywhere




commit 7459bc1fc6421971728a72ee5904436bc73bc7b6
Author: Federico Mena Quintero <federico gnome org>
Date:   Fri May 21 13:23:26 2021 -0500

    Use a new Language / UserLanguage enum instead of LanguageTags everywhere

 src/accept_language.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/cond.rs            | 25 ++++++++-------
 src/drawing_ctx.rs     | 40 +++++-------------------
 src/element.rs         | 10 +++---
 src/structure.rs       |  2 +-
 5 files changed, 104 insertions(+), 55 deletions(-)
---
diff --git a/src/accept_language.rs b/src/accept_language.rs
index 4be8ace2..775d9a2c 100644
--- a/src/accept_language.rs
+++ b/src/accept_language.rs
@@ -5,7 +5,31 @@ use locale_config::{LanguageRange, Locale};
 
 use std::str::FromStr;
 
-#[derive(Debug, PartialEq)]
+/// Used to set the language for rendering.
+///
+/// SVG documents can use the `<switch>` element whose children have a `systemLanguage`
+/// attribute; only the first child which has a `systemLanguage` that matches the user's
+/// preferences will be rendered.
+///
+/// This enum, used with `CairoRenderer::with_language`, configures how to obtain the
+/// user's prefererred languages.
+pub enum Language {
+    /// Use the Unix environment variables `LANGUAGE`, `LC_ALL`, `LC_MESSAGES` and `LANG` to obtain the
+    /// user's language.  This uses [`g_get_language_names()`][ggln] underneath.
+    ///
+    /// [ggln]: https://developer.gnome.org/glib/stable/glib-I18N.html#g-get-language-names
+    FromEnvironment,
+    AcceptLanguage(AcceptLanguage),
+}
+
+/// `Language` but with the environment's locale converted to something we can use.
+#[derive(Clone)]
+pub enum UserLanguage {
+    LanguageTags(LanguageTags),
+    AcceptLanguage(AcceptLanguage),
+}
+
+#[derive(Clone, Debug, PartialEq)]
 struct Weight(Option<f32>);
 
 impl Weight {
@@ -14,7 +38,7 @@ impl Weight {
     }
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
 struct Item {
     tag: LanguageTag,
     weight: Weight,
@@ -23,7 +47,7 @@ struct Item {
 /// Stores a parsed version of an HTTP Accept-Language header.
 ///
 /// https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.5
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
 pub struct AcceptLanguage(Box<[Item]>);
 
 /// Errors when parsing an `AcceptLanguage`.
@@ -65,6 +89,10 @@ impl AcceptLanguage {
     pub fn iter(&self) -> impl Iterator<Item = (&LanguageTag, f32)> {
         self.0.iter().map(|item| (&item.tag, item.weight.numeric()))
     }
+
+    fn any_matches(&self, tag: &LanguageTag) -> bool {
+        self.iter().any(|(self_tag, _weight)| tag.matches(self_tag))
+    }
 }
 
 impl Item {
@@ -179,6 +207,54 @@ impl LanguageTags {
     }
 }
 
+impl UserLanguage {
+    pub fn new(language: &Language) -> UserLanguage {
+        match *language {
+            Language::FromEnvironment => UserLanguage::LanguageTags(
+                LanguageTags::from_locale(&locale_from_environment())
+                    .map_err(|s| {
+                        rsvg_log!("could not convert locale to language tags: {}", s);
+                    })
+                    .unwrap_or_else(|_| LanguageTags::empty()),
+            ),
+
+            Language::AcceptLanguage(ref a) => UserLanguage::AcceptLanguage(a.clone()),
+        }
+    }
+
+    pub fn any_matches(&self, tags: &LanguageTags) -> bool {
+        match *self {
+            UserLanguage::LanguageTags(ref language_tags) => {
+                tags.iter().any(|tag| language_tags.any_matches(tag))
+            }
+            UserLanguage::AcceptLanguage(ref accept_language) => {
+                tags.iter().any(|tag| accept_language.any_matches(tag))
+            }
+        }
+    }
+}
+
+/// Gets the user's preferred locale from the environment and
+/// translates it to a `Locale` with `LanguageRange` fallbacks.
+///
+/// The `Locale::current()` call only contemplates a single language,
+/// but glib is smarter, and `g_get_langauge_names()` can provide
+/// fallbacks, for example, when LC_MESSAGES="en_US.UTF-8:de" (USA
+/// English and German).  This function converts the output of
+/// `g_get_language_names()` into a `Locale` with appropriate
+/// fallbacks.
+fn locale_from_environment() -> Locale {
+    let mut locale = Locale::invariant();
+
+    for name in glib::get_language_names() {
+        if let Ok(range) = LanguageRange::from_unix(&name) {
+            locale.add(&range);
+        }
+    }
+
+    locale
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/src/cond.rs b/src/cond.rs
index 1751657f..401f6534 100644
--- a/src/cond.rs
+++ b/src/cond.rs
@@ -7,7 +7,7 @@ use std::str::FromStr;
 
 use language_tags::LanguageTag;
 
-use crate::accept_language::LanguageTags;
+use crate::accept_language::{LanguageTags, UserLanguage};
 use crate::error::*;
 
 // No extensions at the moment.
@@ -111,8 +111,8 @@ impl SystemLanguage {
     }
 
     /// Evaluate a systemLanguage value for conditional processing.
-    pub fn eval(&self, locale_tags: &LanguageTags) -> bool {
-        self.0.iter().any(|tag| locale_tags.any_matches(tag))
+    pub fn eval(&self, user_language: &UserLanguage) -> bool {
+        user_language.any_matches(&self.0)
     }
 }
 
@@ -165,8 +165,7 @@ mod tests {
     #[test]
     fn system_language() {
         let locale = Locale::new("de,en-US").unwrap();
-
-        let locale_tags = LanguageTags::from_locale(&locale).unwrap();
+        let user_language = UserLanguage::LanguageTags(LanguageTags::from_locale(&locale).unwrap());
 
         assert!(SystemLanguage::from_attribute("").is_err());
 
@@ -175,56 +174,56 @@ mod tests {
         assert_eq!(
             SystemLanguage::from_attribute("fr")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             false
         );
 
         assert_eq!(
             SystemLanguage::from_attribute("en")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             false
         );
 
         assert_eq!(
             SystemLanguage::from_attribute("de")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             true
         );
 
         assert_eq!(
             SystemLanguage::from_attribute("en-US")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             true
         );
 
         assert_eq!(
             SystemLanguage::from_attribute("en-GB")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             false
         );
 
         assert_eq!(
             SystemLanguage::from_attribute("DE")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             true
         );
 
         assert_eq!(
             SystemLanguage::from_attribute("de-LU")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             true
         );
 
         assert_eq!(
             SystemLanguage::from_attribute("fr, de")
                 .unwrap()
-                .eval(&locale_tags),
+                .eval(&user_language),
             true
         );
     }
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index 42df5fa2..75003eca 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -1,7 +1,6 @@
 //! The main context structure which drives the drawing process.
 
 use float_cmp::approx_eq;
-use locale_config::{LanguageRange, Locale};
 use once_cell::sync::Lazy;
 use pango::FontMapExt;
 use regex::{Captures, Regex};
@@ -10,7 +9,7 @@ use std::cell::RefCell;
 use std::convert::TryFrom;
 use std::rc::{Rc, Weak};
 
-use crate::accept_language::LanguageTags;
+use crate::accept_language::{Language, UserLanguage};
 use crate::aspect_ratio::AspectRatio;
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
@@ -151,7 +150,7 @@ pub struct DrawingCtx {
     cr_stack: Rc<RefCell<Vec<cairo::Context>>>,
     cr: cairo::Context,
 
-    locale_tags: LanguageTags,
+    user_language: UserLanguage,
 
     viewport_stack: Rc<RefCell<Vec<Viewport>>>,
 
@@ -253,27 +252,6 @@ impl Drop for DrawingCtx {
     }
 }
 
-/// Gets the user's preferred locale from the environment and
-/// translates it to a `Locale` with `LanguageRange` fallbacks.
-///
-/// The `Locale::current()` call only contemplates a single language,
-/// but glib is smarter, and `g_get_langauge_names()` can provide
-/// fallbacks, for example, when LC_MESSAGES="en_US.UTF-8:de" (USA
-/// English and German).  This function converts the output of
-/// `g_get_language_names()` into a `Locale` with appropriate
-/// fallbacks.
-fn locale_from_environment() -> Locale {
-    let mut locale = Locale::invariant();
-
-    for name in glib::get_language_names() {
-        if let Ok(range) = LanguageRange::from_unix(&name) {
-            locale.add(&range);
-        }
-    }
-
-    locale
-}
-
 impl DrawingCtx {
     fn new(
         cr: &cairo::Context,
@@ -289,18 +267,14 @@ impl DrawingCtx {
 
         let viewport_stack = vec![initial_viewport];
 
-        let locale_tags = LanguageTags::from_locale(&locale_from_environment())
-            .map_err(|s| {
-                rsvg_log!("could not convert locale to language tags: {}", s);
-            })
-            .unwrap_or_else(|_| LanguageTags::empty());
+        let user_language = UserLanguage::new(&Language::FromEnvironment);
 
         DrawingCtx {
             initial_viewport,
             dpi,
             cr_stack: Rc::new(RefCell::new(Vec::new())),
             cr: cr.clone(),
-            locale_tags,
+            user_language,
             viewport_stack: Rc::new(RefCell::new(viewport_stack)),
             drawsub_stack,
             measuring,
@@ -324,7 +298,7 @@ impl DrawingCtx {
             dpi: self.dpi,
             cr_stack,
             cr,
-            locale_tags: self.locale_tags.clone(),
+            user_language: self.user_language.clone(),
             viewport_stack: self.viewport_stack.clone(),
             drawsub_stack: Vec::new(),
             measuring: self.measuring,
@@ -332,8 +306,8 @@ impl DrawingCtx {
         }
     }
 
-    pub fn locale_tags(&self) -> &LanguageTags {
-        &self.locale_tags
+    pub fn user_language(&self) -> &UserLanguage {
+        &self.user_language
     }
 
     pub fn toplevel_viewport(&self) -> Rect {
diff --git a/src/element.rs b/src/element.rs
index 5618241b..c6b60194 100644
--- a/src/element.rs
+++ b/src/element.rs
@@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet};
 use std::fmt;
 use std::ops::Deref;
 
-use crate::accept_language::LanguageTags;
+use crate::accept_language::UserLanguage;
 use crate::bbox::BoundingBox;
 use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
 use crate::css::{Declaration, Origin};
@@ -177,7 +177,7 @@ impl<T: SetAttributes + Draw> ElementInner<T> {
         self.values = values.clone();
     }
 
-    fn get_cond(&self, locale_tags: &LanguageTags) -> bool {
+    fn get_cond(&self, user_language: &UserLanguage) -> bool {
         self.required_extensions
             .as_ref()
             .map(|v| v.eval())
@@ -190,7 +190,7 @@ impl<T: SetAttributes + Draw> ElementInner<T> {
             && self
                 .system_language
                 .as_ref()
-                .map(|v| v.eval(locale_tags))
+                .map(|v| v.eval(user_language))
                 .unwrap_or(true)
     }
 
@@ -532,8 +532,8 @@ impl Element {
         call_inner!(self, set_computed_values, values);
     }
 
-    pub fn get_cond(&self, locale_tags: &LanguageTags) -> bool {
-        call_inner!(self, get_cond, locale_tags)
+    pub fn get_cond(&self, user_language: &UserLanguage) -> bool {
+        call_inner!(self, get_cond, user_language)
     }
 
     pub fn get_transform(&self) -> Transform {
diff --git a/src/structure.rs b/src/structure.rs
index 8a386f1b..aafebd23 100644
--- a/src/structure.rs
+++ b/src/structure.rs
@@ -87,7 +87,7 @@ impl Draw for Switch {
             &mut |an, dc| {
                 if let Some(child) = node.children().filter(|c| c.is_element()).find(|c| {
                     let elt = c.borrow_element();
-                    elt.get_cond(&dc.locale_tags()) && !elt.is_in_error()
+                    elt.get_cond(&dc.user_language()) && !elt.is_in_error()
                 }) {
                     child.draw(an, &CascadedValues::new(cascaded, &child), dc, clipping)
                 } else {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]