@@ -143,6 +143,30 @@ impl QuoteOffsets {
143143 }
144144}
145145
146+ pub trait IsString : AstToken {
147+ fn quote_offsets ( & self ) -> Option < QuoteOffsets > {
148+ let text = self . text ( ) ;
149+ let offsets = QuoteOffsets :: new ( text) ?;
150+ let o = self . syntax ( ) . text_range ( ) . start ( ) ;
151+ let offsets = QuoteOffsets {
152+ quotes : ( offsets. quotes . 0 + o, offsets. quotes . 1 + o) ,
153+ contents : offsets. contents + o,
154+ } ;
155+ Some ( offsets)
156+ }
157+ fn text_range_between_quotes ( & self ) -> Option < TextRange > {
158+ self . quote_offsets ( ) . map ( |it| it. contents )
159+ }
160+ fn open_quote_text_range ( & self ) -> Option < TextRange > {
161+ self . quote_offsets ( ) . map ( |it| it. quotes . 0 )
162+ }
163+ fn close_quote_text_range ( & self ) -> Option < TextRange > {
164+ self . quote_offsets ( ) . map ( |it| it. quotes . 1 )
165+ }
166+ }
167+
168+ impl IsString for ast:: String { }
169+
146170impl ast:: String {
147171 pub fn is_raw ( & self ) -> bool {
148172 self . text ( ) . starts_with ( 'r' )
@@ -187,32 +211,49 @@ impl ast::String {
187211 ( false , false ) => Some ( Cow :: Owned ( buf) ) ,
188212 }
189213 }
190-
191- pub fn quote_offsets ( & self ) -> Option < QuoteOffsets > {
192- let text = self . text ( ) ;
193- let offsets = QuoteOffsets :: new ( text) ?;
194- let o = self . syntax ( ) . text_range ( ) . start ( ) ;
195- let offsets = QuoteOffsets {
196- quotes : ( offsets. quotes . 0 + o, offsets. quotes . 1 + o) ,
197- contents : offsets. contents + o,
198- } ;
199- Some ( offsets)
200- }
201- pub fn text_range_between_quotes ( & self ) -> Option < TextRange > {
202- self . quote_offsets ( ) . map ( |it| it. contents )
203- }
204- pub fn open_quote_text_range ( & self ) -> Option < TextRange > {
205- self . quote_offsets ( ) . map ( |it| it. quotes . 0 )
206- }
207- pub fn close_quote_text_range ( & self ) -> Option < TextRange > {
208- self . quote_offsets ( ) . map ( |it| it. quotes . 1 )
209- }
210214}
211215
216+ impl IsString for ast:: ByteString { }
217+
212218impl ast:: ByteString {
213219 pub fn is_raw ( & self ) -> bool {
214220 self . text ( ) . starts_with ( "br" )
215221 }
222+
223+ pub fn value ( & self ) -> Option < Cow < ' _ , [ u8 ] > > {
224+ if self . is_raw ( ) {
225+ let text = self . text ( ) ;
226+ let text =
227+ & text[ self . text_range_between_quotes ( ) ? - self . syntax ( ) . text_range ( ) . start ( ) ] ;
228+ return Some ( Cow :: Borrowed ( text. as_bytes ( ) ) ) ;
229+ }
230+
231+ let text = self . text ( ) ;
232+ let text = & text[ self . text_range_between_quotes ( ) ? - self . syntax ( ) . text_range ( ) . start ( ) ] ;
233+
234+ let mut buf: Vec < u8 > = Vec :: new ( ) ;
235+ let mut text_iter = text. chars ( ) ;
236+ let mut has_error = false ;
237+ unescape_literal ( text, Mode :: ByteStr , & mut |char_range, unescaped_char| match (
238+ unescaped_char,
239+ buf. capacity ( ) == 0 ,
240+ ) {
241+ ( Ok ( c) , false ) => buf. push ( c as u8 ) ,
242+ ( Ok ( c) , true ) if char_range. len ( ) == 1 && Some ( c) == text_iter. next ( ) => ( ) ,
243+ ( Ok ( c) , true ) => {
244+ buf. reserve_exact ( text. len ( ) ) ;
245+ buf. extend_from_slice ( & text[ ..char_range. start ] . as_bytes ( ) ) ;
246+ buf. push ( c as u8 ) ;
247+ }
248+ ( Err ( _) , _) => has_error = true ,
249+ } ) ;
250+
251+ match ( has_error, buf. capacity ( ) == 0 ) {
252+ ( true , _) => None ,
253+ ( false , true ) => Some ( Cow :: Borrowed ( text. as_bytes ( ) ) ) ,
254+ ( false , false ) => Some ( Cow :: Owned ( buf) ) ,
255+ }
256+ }
216257}
217258
218259#[ derive( Debug ) ]
0 commit comments