11use std:: ffi:: OsStr ;
2- use std:: fmt:: Write ;
32use std:: fs:: { self , File } ;
43use std:: io:: prelude:: * ;
54use std:: io:: { self , BufReader } ;
@@ -10,7 +9,6 @@ use std::sync::LazyLock as Lazy;
109use itertools:: Itertools ;
1110use rustc_data_structures:: flock;
1211use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
13- use serde:: Serialize ;
1412
1513use super :: { collect_paths_for_type, ensure_trailing_slash, Context , BASIC_KEYWORDS } ;
1614use crate :: clean:: Crate ;
@@ -284,25 +282,43 @@ pub(super) fn write_shared(
284282 cx. write_shared ( SharedResource :: Unversioned { name } , contents, & options. emit ) ?;
285283 }
286284
287- fn collect ( path : & Path , krate : & str , key : & str ) -> io:: Result < ( Vec < String > , Vec < String > ) > {
285+ /// Read a file and return all lines that match the `"{crate}":{data},` format,
286+ /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
287+ ///
288+ /// This forms the payload of files that look like this:
289+ ///
290+ /// ```javascript
291+ /// var data = {
292+ /// "{crate1}":{data},
293+ /// "{crate2}":{data}
294+ /// };
295+ /// use_data(data);
296+ /// ```
297+ ///
298+ /// The file needs to be formatted so that *only crate data lines start with `"`*.
299+ fn collect ( path : & Path , krate : & str ) -> io:: Result < ( Vec < String > , Vec < String > ) > {
288300 let mut ret = Vec :: new ( ) ;
289301 let mut krates = Vec :: new ( ) ;
290302
291303 if path. exists ( ) {
292- let prefix = format ! ( r#"{}[ "{}"]"# , key , krate) ;
304+ let prefix = format ! ( " \ " {}\" " , krate) ;
293305 for line in BufReader :: new ( File :: open ( path) ?) . lines ( ) {
294306 let line = line?;
295- if !line. starts_with ( key ) {
307+ if !line. starts_with ( '"' ) {
296308 continue ;
297309 }
298310 if line. starts_with ( & prefix) {
299311 continue ;
300312 }
301- ret. push ( line. to_string ( ) ) ;
313+ if line. ends_with ( "," ) {
314+ ret. push ( line[ ..line. len ( ) - 1 ] . to_string ( ) ) ;
315+ } else {
316+ // No comma (it's the case for the last added crate line)
317+ ret. push ( line. to_string ( ) ) ;
318+ }
302319 krates. push (
303- line[ key. len ( ) + 2 ..]
304- . split ( '"' )
305- . next ( )
320+ line. split ( '"' )
321+ . find ( |s| !s. is_empty ( ) )
306322 . map ( |s| s. to_owned ( ) )
307323 . unwrap_or_else ( String :: new) ,
308324 ) ;
@@ -311,6 +327,20 @@ pub(super) fn write_shared(
311327 Ok ( ( ret, krates) )
312328 }
313329
330+ /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
331+ /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
332+ ///
333+ /// This forms the payload of files that look like this:
334+ ///
335+ /// ```javascript
336+ /// var data = JSON.parse('{\
337+ /// "{crate1}":{data},\
338+ /// "{crate2}":{data}\
339+ /// }');
340+ /// use_data(data);
341+ /// ```
342+ ///
343+ /// The file needs to be formatted so that *only crate data lines start with `"`*.
314344 fn collect_json ( path : & Path , krate : & str ) -> io:: Result < ( Vec < String > , Vec < String > ) > {
315345 let mut ret = Vec :: new ( ) ;
316346 let mut krates = Vec :: new ( ) ;
@@ -526,13 +556,40 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
526556 } ,
527557 } ;
528558
529- #[ derive( Serialize ) ]
530559 struct Implementor {
531560 text : String ,
532561 synthetic : bool ,
533562 types : Vec < String > ,
534563 }
535564
565+ impl Implementor {
566+ fn to_js_string ( & self ) -> String {
567+ fn single_quote_string ( s : & str ) -> String {
568+ let mut result = String :: with_capacity ( s. len ( ) + 2 ) ;
569+ result. push_str ( "'" ) ;
570+ for c in s. chars ( ) {
571+ if c == '"' {
572+ result. push_str ( "\" " ) ;
573+ } else {
574+ result. extend ( c. escape_default ( ) ) ;
575+ }
576+ }
577+ result. push_str ( "'" ) ;
578+ result
579+ }
580+ let text_esc = single_quote_string ( & self . text ) ;
581+ if self . synthetic {
582+ let types = self . types . iter ( ) . map ( |type_| single_quote_string ( type_) ) . join ( "," ) ;
583+ // use `1` to represent a synthetic, because it's fewer bytes than `true`
584+ format ! ( "[{text_esc},1,[{types}]]" )
585+ } else {
586+ // The types list is only used for synthetic impls.
587+ // If this changes, `main.js` and `write_shared.rs` both need changed.
588+ format ! ( "[{text_esc}]" )
589+ }
590+ }
591+ }
592+
536593 let implementors = imps
537594 . iter ( )
538595 . filter_map ( |imp| {
@@ -563,9 +620,9 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
563620 }
564621
565622 let implementors = format ! (
566- r#"implementors[ "{}"] = {}; "# ,
623+ r#""{}":[{}] "# ,
567624 krate. name( cx. tcx( ) ) ,
568- serde_json :: to_string ( & implementors ) . unwrap ( )
625+ implementors . iter ( ) . map ( Implementor :: to_js_string ) . join ( "," )
569626 ) ;
570627
571628 let mut mydst = dst. clone ( ) ;
@@ -576,16 +633,15 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
576633 mydst. push ( & format ! ( "{}.{}.js" , remote_item_type, remote_path[ remote_path. len( ) - 1 ] ) ) ;
577634
578635 let ( mut all_implementors, _) =
579- try_err ! ( collect( & mydst, krate. name( cx. tcx( ) ) . as_str( ) , "implementors" ) , & mydst) ;
636+ try_err ! ( collect( & mydst, krate. name( cx. tcx( ) ) . as_str( ) ) , & mydst) ;
580637 all_implementors. push ( implementors) ;
581638 // Sort the implementors by crate so the file will be generated
582639 // identically even with rustdoc running in parallel.
583640 all_implementors. sort ( ) ;
584641
585- let mut v = String :: from ( "(function() {var implementors = {};\n " ) ;
586- for implementor in & all_implementors {
587- writeln ! ( v, "{}" , * implementor) . unwrap ( ) ;
588- }
642+ let mut v = String :: from ( "(function() {var implementors = {\n " ) ;
643+ v. push_str ( & all_implementors. join ( ",\n " ) ) ;
644+ v. push_str ( "\n };" ) ;
589645 v. push_str (
590646 "if (window.register_implementors) {\
591647 window.register_implementors(implementors);\
0 commit comments