Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / strings.rs
5.7 kB 170 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2024 The Gleam contributors 3 4use ecow::EcoString; 5use itertools::Itertools; 6 7use crate::ast::Endianness; 8 9/// Converts any escape sequences from the given string to their correct 10/// bytewise UTF-8 representation and returns the resulting string. 11pub fn convert_string_escape_chars(str: &EcoString) -> EcoString { 12 let mut filtered_str = EcoString::new(); 13 let mut str_iter = str.chars().peekable(); 14 loop { 15 match str_iter.next() { 16 Some('\\') => match str_iter.next() { 17 // Check for Unicode escape sequence, e.g. \u{00012FF} 18 Some('u') => { 19 if str_iter.peek() != Some(&'{') { 20 // Invalid Unicode escape sequence 21 filtered_str.push('u'); 22 continue; 23 } 24 25 // Consume the left brace after peeking 26 let _ = str_iter.next(); 27 28 let codepoint_str = str_iter 29 .peeking_take_while(char::is_ascii_hexdigit) 30 .collect::<String>(); 31 32 if codepoint_str.is_empty() || str_iter.peek() != Some(&'}') { 33 // Invalid Unicode escape sequence 34 filtered_str.push_str("u{"); 35 filtered_str.push_str(&codepoint_str); 36 continue; 37 } 38 39 let codepoint = u32::from_str_radix(&codepoint_str, 16) 40 .ok() 41 .and_then(char::from_u32); 42 43 if let Some(codepoint) = codepoint { 44 // Consume the right brace after peeking 45 let _ = str_iter.next(); 46 47 // Consider this codepoint's length instead of 48 // that of the Unicode escape sequence itself 49 filtered_str.push(codepoint); 50 } else { 51 // Invalid Unicode escape sequence 52 // (codepoint value not in base 16 or too large) 53 filtered_str.push_str("u{"); 54 filtered_str.push_str(&codepoint_str); 55 } 56 } 57 Some('n') => filtered_str.push('\n'), 58 Some('r') => filtered_str.push('\r'), 59 Some('f') => filtered_str.push('\u{C}'), 60 Some('t') => filtered_str.push('\t'), 61 Some('"') => filtered_str.push('\"'), 62 Some('\\') => filtered_str.push('\\'), 63 Some(c) => filtered_str.push(c), 64 None => break, 65 }, 66 Some(c) => filtered_str.push(c), 67 None => break, 68 } 69 } 70 filtered_str 71} 72 73pub fn to_snake_case(string: &str) -> EcoString { 74 let mut snake_case = EcoString::with_capacity(string.len()); 75 let mut is_word_boundary = true; 76 77 for char in string.chars() { 78 match char { 79 '_' | ' ' => { 80 is_word_boundary = true; 81 continue; 82 } 83 _ if char.is_uppercase() => { 84 is_word_boundary = true; 85 } 86 _ => {} 87 } 88 89 if is_word_boundary { 90 // We don't want to push an underscore at the start of the string, 91 // even if it starts with a capital letter or other delimiter. 92 if !snake_case.is_empty() { 93 snake_case.push('_'); 94 } 95 is_word_boundary = false; 96 } 97 snake_case.push(char.to_ascii_lowercase()); 98 } 99 100 snake_case 101} 102 103pub fn to_upper_camel_case(string: &str) -> EcoString { 104 let mut pascal_case = EcoString::with_capacity(string.len()); 105 let mut chars = string.chars(); 106 107 while let Some(char) = chars.next() { 108 if char == '_' { 109 let Some(next) = chars.next() else { break }; 110 pascal_case.push(next.to_ascii_uppercase()); 111 } else { 112 pascal_case.push(char); 113 } 114 } 115 116 pascal_case 117} 118 119/// Converts a string into its UTF-16 representation in bytes 120pub fn string_to_utf16_bytes(string: &str, endianness: Endianness) -> Vec<u8> { 121 let mut bytes = Vec::with_capacity(string.len() * 2); 122 123 let mut character_buffer = [0, 0]; 124 for character in string.chars() { 125 let segments = character.encode_utf16(&mut character_buffer); 126 127 for segment in segments { 128 let segment_bytes = match endianness { 129 Endianness::Big => segment.to_be_bytes(), 130 Endianness::Little => segment.to_le_bytes(), 131 }; 132 133 bytes.push(segment_bytes[0]); 134 bytes.push(segment_bytes[1]); 135 } 136 } 137 138 bytes 139} 140 141/// Converts a string into its UTF-32 representation in bytes 142pub fn string_to_utf32_bytes(string: &str, endianness: Endianness) -> Vec<u8> { 143 let mut bytes = Vec::with_capacity(string.len() * 4); 144 145 for character in string.chars() { 146 let character_bytes = match endianness { 147 Endianness::Big => (character as u32).to_be_bytes(), 148 Endianness::Little => (character as u32).to_le_bytes(), 149 }; 150 bytes.extend(character_bytes); 151 } 152 153 bytes 154} 155 156/// Gets the number of UTF-16 codepoints it would take to encode a given string. 157pub fn length_utf16(string: &str) -> usize { 158 let mut length = 0; 159 160 for char in string.chars() { 161 length += char.len_utf16() 162 } 163 164 length 165} 166 167/// Gets the number of UTF-32 codepoints in a string 168pub fn length_utf32(string: &str) -> usize { 169 string.chars().count() 170}