瀏覽代碼

Add a path setter

Simon Sapin 10 年之前
父節點
當前提交
5425385451
共有 2 個文件被更改,包括 41 次插入10 次删除
  1. 35 5
      src/lib.rs
  2. 6 5
      src/parser.rs

+ 35 - 5
src/lib.rs

@@ -471,6 +471,38 @@ impl Url {
         }
     }
 
+    /// Change this URL’s path.
+    pub fn set_path(&mut self, path: &str) {
+        let (old_after_path_pos, after_path) = match (self.query_start, self.fragment_start) {
+            (Some(i), _) | (None, Some(i)) => (i, self.slice(i..).to_owned()),
+            (None, None) => (to_u32(self.serialization.len()).unwrap(), String::new())
+        };
+        let non_relative = self.non_relative();
+        let scheme_type = parser::SchemeType::from(self.scheme());
+        self.serialization.truncate(self.path_start as usize);
+        self.mutate(|parser| {
+            if non_relative {
+                if path.starts_with('/') {
+                    parser.serialization.push_str("%2F");
+                    parser.parse_non_relative_path(&path[1..]);
+                } else {
+                    parser.parse_non_relative_path(path);
+                }
+            } else {
+                let mut has_host = true;  // FIXME
+                parser.parse_path_start(scheme_type, &mut has_host, path);
+            }
+        });
+        let new_after_path_pos = to_u32(self.serialization.len()).unwrap();
+        let adjust = |index: &mut u32| {
+            *index -= old_after_path_pos;
+            *index += new_after_path_pos;
+        };
+        if let Some(ref mut index) = self.query_start { adjust(index) }
+        if let Some(ref mut index) = self.fragment_start { adjust(index) }
+        self.serialization.push_str(&after_path)
+    }
+
     /// Remove the last segment of this URL’s path.
     ///
     /// If this URL is non-relative, do nothing and return `Err`.
@@ -510,9 +542,9 @@ impl Url {
             (Some(i), _) | (None, Some(i)) => {
                 let s = self.slice(i..).to_owned();
                 self.serialization.truncate(i as usize);
-                Some(s)
+                s
             },
-            (None, None) => None
+            (None, None) => String::new()
         };
         let scheme_type = parser::SchemeType::from(self.scheme());
         let path_start = self.path_start as usize;
@@ -525,9 +557,7 @@ impl Url {
         let offset = to_u32(self.serialization.len()).unwrap() - self.path_start;
         if let Some(ref mut index) = self.query_start { *index += offset }
         if let Some(ref mut index) = self.fragment_start { *index += offset }
-        if let Some(ref after_path) = after_path {
-            self.serialization.push_str(after_path)
-        }
+        self.serialization.push_str(&after_path);
         Ok(())
     }
 

+ 6 - 5
src/parser.rs

@@ -729,7 +729,7 @@ impl<'a> Parser<'a> {
         return Ok((opt_port, &input[end..]))
     }
 
-    fn parse_path_start<'i>(&mut self, scheme_type: SchemeType, has_host: &mut bool,
+    pub fn parse_path_start<'i>(&mut self, scheme_type: SchemeType, has_host: &mut bool,
                             mut input: &'i str)
                             -> &'i str {
         // Path start state
@@ -760,12 +760,13 @@ impl<'a> Parser<'a> {
             end = input.len();
             while let Some((i, c, next_i)) = iter.next() {
                 match c {
-                    '/' => {
+                    '/' if self.context != Context::PathSegmentSetter => {
                         ends_with_slash = true;
                         end = i;
                         break
                     },
-                    '\\' if scheme_type.is_special() => {
+                    '\\' if self.context != Context::PathSegmentSetter &&
+                            scheme_type.is_special() => {
                         self.syntax_violation("backslash");
                         ends_with_slash = true;
                         end = i;
@@ -851,10 +852,10 @@ impl<'a> Parser<'a> {
 
     }
 
-    fn parse_non_relative_path<'i>(&mut self, input: &'i str) -> &'i str {
+    pub fn parse_non_relative_path<'i>(&mut self, input: &'i str) -> &'i str {
         for (i, c, next_i) in input.char_ranges() {
             match c {
-                '?' | '#' => return &input[i..],
+                '?' | '#' if self.context == Context::UrlParser => return &input[i..],
                 '\t' | '\n' | '\r' => self.syntax_violation("invalid character"),
                 _ => {
                     self.check_url_code_point(input, i, c);