lib.rs (5950B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use nsstring::{nsCString, nsString}; 6 use thin_vec::ThinVec; 7 pub mod fragment_directive_impl; 8 9 /// This struct contains the percent-decoded parts of a text directive. 10 /// All parts besides `start` are optional (which is indicated by an empty string). 11 /// 12 /// This struct uses Gecko String types, whereas the parser internally uses Rust types. 13 /// Therefore, conversion functions are provided. 14 #[repr(C)] 15 pub struct TextDirective { 16 prefix: nsString, 17 start: nsString, 18 end: nsString, 19 suffix: nsString, 20 } 21 22 impl TextDirective { 23 /// Creates a `FragmentDirectiveElement` object from a `FragmentDirectiveElementInternal` object 24 /// (which uses Rust string types). 25 fn from_rust_type(element: &fragment_directive_impl::TextDirective) -> Self { 26 Self { 27 prefix: element 28 .prefix() 29 .as_ref() 30 .map_or_else(nsString::new, |token| nsString::from(token.value())), 31 start: element 32 .start() 33 .as_ref() 34 .map_or_else(nsString::new, |token| nsString::from(token.value())), 35 end: element 36 .end() 37 .as_ref() 38 .map_or_else(nsString::new, |token| nsString::from(token.value())), 39 suffix: element 40 .suffix() 41 .as_ref() 42 .map_or_else(nsString::new, |token| nsString::from(token.value())), 43 } 44 } 45 46 /// Converts the contents of this object into Rust types. 47 /// Returns `None` if the given fragment is not valid. 48 /// The only invalid condition is a fragment that is missing the `start` token. 49 fn to_rust_type(&self) -> Option<fragment_directive_impl::TextDirective> { 50 fragment_directive_impl::TextDirective::from_parts( 51 self.prefix.to_string(), 52 self.start.to_string(), 53 self.end.to_string(), 54 self.suffix.to_string(), 55 ) 56 } 57 } 58 59 /// Result of the `parse_fragment_directive()` function. 60 /// 61 /// The result contains the original given URL without the fragment directive, 62 /// a unsanitized string version of the extracted fragment directive, 63 /// and an array of the parsed text directives. 64 #[repr(C)] 65 pub struct ParsedFragmentDirectiveResult { 66 hash_without_fragment_directive: nsCString, 67 fragment_directive: nsCString, 68 text_directives: ThinVec<TextDirective>, 69 } 70 71 /// Parses the fragment directive from a given URL fragment. 72 /// 73 /// This function writes the result data into `result`. 74 /// The result consists of 75 /// - the input url fragment without the fragment directive, 76 /// - the fragment directive as unparsed string, 77 /// - a list of the parsed and percent-decoded text directives. 78 /// 79 /// Directives which are unknown will be ignored. 80 /// If new directive types are added in the future, they should also be considered here. 81 /// This function returns false if no fragment directive is found. If there is any 82 /// fragment directive (even if invalid), this function returns true. 83 #[no_mangle] 84 pub extern "C" fn parse_fragment_directive( 85 hash: &nsCString, 86 result: &mut ParsedFragmentDirectiveResult, 87 ) -> bool { 88 // sanitize inputs 89 result.hash_without_fragment_directive = nsCString::new(); 90 result.fragment_directive = nsCString::new(); 91 result.text_directives.clear(); 92 93 let url_as_rust_string = hash.to_utf8(); 94 if let Some((stripped_hash, fragment_directive, text_directives)) = 95 fragment_directive_impl::parse_fragment_directive_and_remove_it_from_hash( 96 &url_as_rust_string, 97 ) 98 { 99 result 100 .hash_without_fragment_directive 101 .assign(&stripped_hash); 102 result.fragment_directive.assign(&fragment_directive); 103 result.text_directives.extend( 104 text_directives 105 .iter() 106 .map(|text_directive| TextDirective::from_rust_type(text_directive)), 107 ); 108 return true; 109 } 110 false 111 } 112 113 /// Creates a percent-encoded fragment directive string from a given list of `FragmentDirectiveElement`s. 114 /// 115 /// The returned string has this form: 116 /// `:~:text=[prefix1-,]start1[,end1][,-suffix1]&text=[prefix2-,]start2[,end2][,-suffix2]` 117 /// 118 /// Invalid `FragmentDirectiveElement`s are ignored, where "invalid" means that no `start` token is provided. 119 /// If there are no valid `FragmentDirectiveElement`s, an empty string is returned. 120 #[no_mangle] 121 pub extern "C" fn create_fragment_directive( 122 text_directives: &ThinVec<TextDirective>, 123 fragment_directive: &mut nsCString, 124 ) -> bool { 125 let directives_rust = Vec::from_iter( 126 text_directives 127 .iter() 128 .filter_map(|fragment| fragment.to_rust_type()), 129 ); 130 if let Some(fragment_directive_rust) = 131 fragment_directive_impl::create_fragment_directive_string(&directives_rust) 132 { 133 fragment_directive.assign(&fragment_directive_rust); 134 return true; 135 } 136 137 false 138 } 139 140 /// Creates a percent-encoded text directive string for a single text directive. 141 /// The returned string has the form `text=[prefix-,]start[,end][,-suffix]`. 142 /// If the provided `TextDirective` is invalid (i.e. it has no `start` attribute), 143 /// the outparam `directive_string` is empty and the function returns false. 144 #[no_mangle] 145 pub extern "C" fn create_text_directive( 146 text_directive: &TextDirective, 147 directive_string: &mut nsCString, 148 ) -> bool { 149 if let Some(text_directive_rust) = text_directive.to_rust_type() { 150 if let Some(text_directive_string_rust) = 151 fragment_directive_impl::create_text_directive_string(&text_directive_rust) 152 { 153 directive_string.assign(&text_directive_string_rust); 154 return true; 155 } 156 } 157 false 158 }