[librsvg: 26/48] defs::Reference - encapsulate the micro-parser for uri#fragment_id



commit 09a38917ff5a7e292238928c74be57b7fa5d039b
Author: Federico Mena Quintero <federico gnome org>
Date:   Mon Sep 10 13:34:01 2018 -0500

    defs::Reference - encapsulate the micro-parser for uri#fragment_id
    
    We have an implicit way of doing that in Defs.lookup(); we'll move
    that little bit of logic to a separate function.

 rsvg_internals/src/defs.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)
---
diff --git a/rsvg_internals/src/defs.rs b/rsvg_internals/src/defs.rs
index 1fbd4bd7..f0589320 100644
--- a/rsvg_internals/src/defs.rs
+++ b/rsvg_internals/src/defs.rs
@@ -61,6 +61,39 @@ impl Defs {
     }
 }
 
+/// Represents a possibly non-canonical URI with an optional fragment identifier
+///
+/// Sometimes in SVG element references (e.g. the `href` in the `<feImage>` element) we
+/// must decide between referencing an external file, or using a plain fragment identifier
+/// like `href="#foo"` as a reference to an SVG element in the same file as the one being
+/// processes.  This enum makes that distinction.
+#[derive(Debug, PartialEq)]
+pub enum Reference<'a> {
+    PlainUri(&'a str),
+    FragmentId(&'a str),
+    UriWithFragmentId(&'a str, &'a str),
+}
+
+impl<'a> Reference<'a> {
+    pub fn parse(s: &str) -> Result<Reference, ()> {
+        let (uri, fragment) = match s.rfind('#') {
+            None => (Some(s), None),
+            Some(p) if p == 0 => (None, Some(&s[1..])),
+            Some(p) => (Some(&s[..p]), Some(&s[(p + 1)..])),
+        };
+
+        match (uri, fragment) {
+            (None, Some(f)) if f.len() == 0 => Err(()),
+            (None, Some(f)) => Ok(Reference::FragmentId(f)),
+            (Some(u), _) if u.len() == 0 => Err(()),
+            (Some(u), None) => Ok(Reference::PlainUri(u)),
+            (Some(_u), Some(f)) if f.len() == 0 => Err(()),
+            (Some(u), Some(f)) => Ok(Reference::UriWithFragmentId(u, f)),
+            (_, _) => Err(()),
+        }
+    }
+}
+
 #[no_mangle]
 pub extern "C" fn rsvg_defs_new(handle: *const RsvgHandle) -> *mut RsvgDefs {
     Box::into_raw(Box::new(Defs::new(handle))) as *mut RsvgDefs
@@ -92,3 +125,28 @@ pub extern "C" fn rsvg_defs_lookup(
         None => ptr::null(),
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn reference_kinds() {
+        assert_eq!(Reference::parse("uri"), Ok(Reference::PlainUri("uri")));
+        assert_eq!(
+            Reference::parse("#fragment"),
+            Ok(Reference::FragmentId("fragment"))
+        );
+        assert_eq!(
+            Reference::parse("uri#fragment"),
+            Ok(Reference::UriWithFragmentId("uri", "fragment"))
+        );
+    }
+
+    #[test]
+    fn reference_errors() {
+        assert!(Reference::parse("").is_err());
+        assert!(Reference::parse("#").is_err());
+        assert!(Reference::parse("uri#").is_err());
+    }
+}


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