1 use std::{fmt, ops::Range}; 2 3 use crate::{ 4 Buffer, ParseError, 5 err::{perr, ParseErrorKind::*}, 6 escape::{scan_raw_string, unescape_string}, 7 parse::first_byte_or_empty, 8 }; 9 10 11 /// A string or raw string literal, e.g. `"foo"`, `"Grüße"` or `r#"ac"df"#`. 12 /// 13 /// See [the reference][ref] for more information. 14 /// 15 /// [ref]: https://doc.rust-lang.org/reference/tokens.html#string-literals 16 #[derive(Debug, Clone, PartialEq, Eq)] 17 pub struct StringLit<B: Buffer> { 18 /// The raw input. 19 raw: B, 20 21 /// The string value (with all escaped unescaped), or `None` if there were 22 /// no escapes. In the latter case, `input` is the string value. 23 value: Option<String>, 24 25 /// The number of hash signs in case of a raw string literal, or `None` if 26 /// it's not a raw string literal. 27 num_hashes: Option<u32>, 28 } 29 30 impl<B: Buffer> StringLit<B> { 31 /// Parses the input as a (raw) string literal. Returns an error if the 32 /// input is invalid or represents a different kind of literal. parse(input: B) -> Result<Self, ParseError>33 pub fn parse(input: B) -> Result<Self, ParseError> { 34 match first_byte_or_empty(&input)? { 35 b'r' | b'"' => Self::parse_impl(input), 36 _ => Err(perr(0, InvalidStringLiteralStart)), 37 } 38 } 39 40 /// Returns the string value this literal represents (where all escapes have 41 /// been turned into their respective values). value(&self) -> &str42 pub fn value(&self) -> &str { 43 self.value.as_deref().unwrap_or(&self.raw[self.inner_range()]) 44 } 45 46 /// Like `value` but returns a potentially owned version of the value. 47 /// 48 /// The return value is either `Cow<'static, str>` if `B = String`, or 49 /// `Cow<'a, str>` if `B = &'a str`. into_value(self) -> B::Cow50 pub fn into_value(self) -> B::Cow { 51 let inner_range = self.inner_range(); 52 let Self { raw, value, .. } = self; 53 value.map(B::Cow::from).unwrap_or_else(|| raw.cut(inner_range).into_cow()) 54 } 55 56 /// Returns whether this literal is a raw string literal (starting with 57 /// `r`). is_raw_string(&self) -> bool58 pub fn is_raw_string(&self) -> bool { 59 self.num_hashes.is_some() 60 } 61 62 /// Returns the raw input that was passed to `parse`. raw_input(&self) -> &str63 pub fn raw_input(&self) -> &str { 64 &self.raw 65 } 66 67 /// Returns the raw input that was passed to `parse`, potentially owned. into_raw_input(self) -> B68 pub fn into_raw_input(self) -> B { 69 self.raw 70 } 71 72 /// The range within `self.raw` that excludes the quotes and potential `r#`. inner_range(&self) -> Range<usize>73 fn inner_range(&self) -> Range<usize> { 74 match self.num_hashes { 75 None => 1..self.raw.len() - 1, 76 Some(n) => 1 + n as usize + 1..self.raw.len() - n as usize - 1, 77 } 78 } 79 80 /// Precondition: input has to start with either `"` or `r`. parse_impl(input: B) -> Result<Self, ParseError>81 pub(crate) fn parse_impl(input: B) -> Result<Self, ParseError> { 82 if input.starts_with('r') { 83 let (value, num_hashes) = scan_raw_string::<char>(&input, 1)?; 84 Ok(Self { 85 raw: input, 86 value, 87 num_hashes: Some(num_hashes), 88 }) 89 } else { 90 let value = unescape_string::<char>(&input, 1)?; 91 Ok(Self { 92 raw: input, 93 value, 94 num_hashes: None, 95 }) 96 } 97 } 98 } 99 100 impl StringLit<&str> { 101 /// Makes a copy of the underlying buffer and returns the owned version of 102 /// `Self`. into_owned(self) -> StringLit<String>103 pub fn into_owned(self) -> StringLit<String> { 104 StringLit { 105 raw: self.raw.to_owned(), 106 value: self.value, 107 num_hashes: self.num_hashes, 108 } 109 } 110 } 111 112 impl<B: Buffer> fmt::Display for StringLit<B> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 114 f.pad(&self.raw) 115 } 116 } 117 118 119 #[cfg(test)] 120 mod tests; 121