Răsfoiți Sursa

Add Url::mutate_query_pairs

Simon Sapin 10 ani în urmă
părinte
comite
6f716e1830
2 a modificat fișierele cu 65 adăugiri și 23 ștergeri
  1. 4 4
      src/form_urlencoded.rs
  2. 61 19
      src/lib.rs

+ 4 - 4
src/form_urlencoded.rs

@@ -6,7 +6,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! Parser and serializer for the [`application/x-www-form-urlencoded` format](
+//! Parser and serializer for the [`application/x-www-form-urlencoded` syntax](
 //! http://url.spec.whatwg.org/#application/x-www-form-urlencoded),
 //! as used by HTML forms.
 //!
@@ -19,7 +19,7 @@ use std::borrow::{Borrow, Cow};
 use std::str;
 
 
-/// Convert a byte string in the `application/x-www-form-urlencoded` format
+/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
 /// into a iterator of (name, value) pairs.
 ///
 /// Use `parse(input.as_bytes())` to parse a `&str` string.
@@ -35,7 +35,7 @@ pub fn parse(input: &[u8]) -> Parse {
 }
 
 
-/// Convert a byte string in the `application/x-www-form-urlencoded` format
+/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
 /// into a iterator of (name, value) pairs.
 ///
 /// Use `parse(input.as_bytes())` to parse a `&str` string.
@@ -197,7 +197,7 @@ impl<'a> Serializer<'a> {
     /// for the given range of the given string.
     ///
     /// If the range is non-empty, the corresponding slice of the string is assumed
-    /// to already be in `application/x-www-form-urlencoded` format.
+    /// to already be in `application/x-www-form-urlencoded` syntax.
     pub fn new(string: &'a mut String, start_position: usize) -> Self {
         &string[start_position..];  // Panic if out of bounds
         Serializer {

+ 61 - 19
src/lib.rs

@@ -450,6 +450,13 @@ impl Url {
         }
     }
 
+    /// Parse the URL’s query string, if any, as `application/x-www-form-urlencoded`
+    /// and return an iterator of (key, value) pairs.
+    #[inline]
+    pub fn query_pairs(&self) -> form_urlencoded::Parse {
+        form_urlencoded::parse(self.query().unwrap_or("").as_bytes())
+    }
+
     /// Return this URL’s fragment identifier, if any.
     ///
     /// **Note:** the parser did *not* percent-encode this component,
@@ -487,24 +494,66 @@ impl Url {
 
     /// Change this URL’s query string.
     pub fn set_query(&mut self, query: Option<&str>) {
+        self.set_query_internal(|url| {
+            // Remove any previous query
+            if let Some(start) = url.query_start.take() {
+                debug_assert!(url.byte_at(start) == b'?');
+                url.serialization.truncate(start as usize);
+            }
+            // Write the new query, if any
+            if let Some(input) = query {
+                url.query_start = Some(to_u32(url.serialization.len()).unwrap());
+                url.serialization.push('?');
+                let scheme_end = url.scheme_end;
+                url.mutate(|parser| parser.parse_query(scheme_end, input));
+            }
+        })
+    }
+
+    /// Change this URL’s query string, viewed as a sequence of name/value pairs
+    /// in `application/x-www-form-urlencoded` syntax.
+    ///
+    /// Example:
+    ///
+    /// ```rust
+    /// # use url::Url;
+    /// let mut url = Url::parse("https://example.net?...#nav").unwrap();
+    /// assert_eq!(url.query(), Some("..."));
+    /// url.mutate_query_pairs(|query| {
+    ///     query.clear();
+    ///     query.append_pair("foo", "bar & baz");
+    /// });
+    /// assert_eq!(url.query(), Some("foo=bar+%26+baz"));
+    /// assert_eq!(url.as_str(), "https://example.net/?foo=bar+%26+baz#nav");
+    /// url.mutate_query_pairs(|query| {
+    ///     query.append_pair("saison", "Été+hiver");
+    /// });
+    /// assert_eq!(url.query(), Some("foo=bar+%26+baz&saison=%C3%89t%C3%A9%2Bhiver"));
+    /// ```
+    pub fn mutate_query_pairs<F: FnOnce(&mut form_urlencoded::Serializer)>(&mut self, f: F) {
+        self.set_query_internal(|url| {
+            let query_start;
+            if let Some(start) = url.query_start {
+                debug_assert!(url.byte_at(start) == b'?');
+                query_start = start as usize;
+            } else {
+                query_start = url.serialization.len();
+                url.query_start = Some(to_u32(query_start).unwrap());
+                url.serialization.push('?');
+            }
+            let query_start = query_start + "?".len();
+            f(&mut form_urlencoded::Serializer::new(&mut url.serialization, query_start))
+        })
+    }
+
+    fn set_query_internal<F: FnOnce(&mut Url)>(&mut self, f: F) {
         // Stash any fragment
         let fragment = self.fragment_start.map(|start| {
             let f = self.slice(start..).to_owned();
             self.serialization.truncate(start as usize);
             f
         });
-        // Remove any previous query
-        if let Some(start) = self.query_start {
-            debug_assert!(self.byte_at(start) == b'?');
-            self.serialization.truncate(start as usize);
-        }
-        // Write the new one
-        if let Some(input) = query {
-            self.query_start = Some(to_u32(self.serialization.len()).unwrap());
-            self.serialization.push('?');
-            let scheme_end = self.scheme_end;
-            self.mutate(|parser| parser.parse_query(scheme_end, input));
-        }
+        f(self);
         // Restore the fragment, if any
         if let Some(ref fragment) = fragment {
             self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
@@ -987,13 +1036,6 @@ impl Url {
         Err(())
     }
 
-    /// Parse the URL’s query string, if any, as `application/x-www-form-urlencoded`
-    /// and return an iterator of (key, value) pairs.
-    #[inline]
-    pub fn query_pairs(&self) -> form_urlencoded::Parse {
-        form_urlencoded::parse(self.query().unwrap_or("").as_bytes())
-    }
-
     // Private helper methods:
 
     #[inline]