/ libs / mdbook-gen / src / transform_book.rs
transform_book.rs
  1  use std::path::PathBuf;
  2  
  3  use mdbook_shared::MdBook;
  4  use mdbook_shared::Page;
  5  use mdbook_shared::Section;
  6  use mdbook_shared::Summary;
  7  use mdbook_shared::SummaryItem;
  8  use proc_macro2::TokenStream;
  9  use quote::quote;
 10  use quote::ToTokens;
 11  
 12  use crate::path_to_route_enum;
 13  
 14  /// Transforms the book to use enum routes instead of paths
 15  pub fn write_book_with_routes(book: &mdbook_shared::MdBook<PathBuf>) -> TokenStream {
 16      let MdBook { summary, .. } = book;
 17      let summary = write_summary_with_routes(summary);
 18      let pages = book.pages().iter().map(|(id, v)| {
 19          let name = match path_to_route_enum(&v.url) {
 20              Ok(url) => url,
 21              Err(err) => {
 22                  return err.to_token_stream();
 23              }
 24          };
 25          let page = write_page_with_routes(v);
 26          let fn_token = quote::format_ident!("__push_page_{}", id);
 27  
 28          quote! {
 29              let #fn_token: fn(_, _) = |_pages: &mut Vec<_>, _page_id_mapping: &mut std::collections::HashMap<_, _>| {
 30                  _pages.push((#id, #page));
 31                  _page_id_mapping.insert(#name, ::use_mdbook::mdbook_shared::PageId(#id));
 32              };
 33              #fn_token(&mut pages, &mut page_id_mapping);
 34          }
 35      });
 36  
 37      let out = quote! {
 38          {
 39              let mut page_id_mapping = ::std::collections::HashMap::new();
 40              let mut pages = Vec::new();
 41              #(#pages)*
 42              ::use_mdbook::mdbook_shared::MdBook {
 43                  summary: #summary,
 44                  pages: pages.into_iter().collect(),
 45                  page_id_mapping,
 46              }
 47          }
 48      };
 49  
 50      out.to_token_stream()
 51  }
 52  
 53  fn write_summary_with_routes(book: &mdbook_shared::Summary<PathBuf>) -> TokenStream {
 54      let Summary {
 55          title,
 56          prefix_chapters,
 57          numbered_chapters,
 58          suffix_chapters,
 59      } = book;
 60  
 61      let prefix_chapters = prefix_chapters.iter().map(write_summary_item_with_routes);
 62      let numbered_chapters = numbered_chapters.iter().map(write_summary_item_with_routes);
 63      let suffix_chapters = suffix_chapters.iter().map(write_summary_item_with_routes);
 64      let title = match title {
 65          Some(title) => quote! { Some(#title.to_string()) },
 66          None => quote! { None },
 67      };
 68  
 69      quote! {
 70          ::use_mdbook::mdbook_shared::Summary {
 71              title: #title,
 72              prefix_chapters: vec![#(#prefix_chapters),*],
 73              numbered_chapters: vec![#(#numbered_chapters),*],
 74              suffix_chapters: vec![#(#suffix_chapters),*],
 75          }
 76      }
 77  }
 78  
 79  fn write_summary_item_with_routes(item: &SummaryItem<PathBuf>) -> TokenStream {
 80      match item {
 81          SummaryItem::Link(link) => {
 82              let link = write_link_with_routes(link);
 83              quote! {
 84                  ::use_mdbook::mdbook_shared::SummaryItem::Link(#link)
 85              }
 86          }
 87          SummaryItem::Separator => {
 88              quote! {
 89                  ::use_mdbook::mdbook_shared::SummaryItem::Separator
 90              }
 91          }
 92          SummaryItem::PartTitle(title) => {
 93              quote! {
 94                  ::use_mdbook::mdbook_shared::SummaryItem::PartTitle(#title.to_string())
 95              }
 96          }
 97      }
 98  }
 99  
100  fn write_link_with_routes(book: &mdbook_shared::Link<PathBuf>) -> TokenStream {
101      let mdbook_shared::Link {
102          name,
103          location,
104          number,
105          nested_items,
106      } = book;
107  
108      let location = match location {
109          Some(loc) => {
110              let inner = match path_to_route_enum(loc) {
111                  Ok(url) => url,
112                  Err(err) => {
113                      return err.to_token_stream();
114                  }
115              };
116              quote! { Some(#inner) }
117          }
118          None => quote! { None },
119      };
120      let number = match number {
121          Some(number) => {
122              let inner = write_number_with_routes(number);
123              quote! { Some(#inner) }
124          }
125          None => quote! {None},
126      };
127  
128      let nested_items = nested_items.iter().map(write_summary_item_with_routes);
129  
130      quote! {
131          ::use_mdbook::mdbook_shared::Link {
132              name: #name.to_string(),
133              location: #location,
134              number: #number,
135              nested_items: vec![#(#nested_items,)*],
136          }
137      }
138  }
139  
140  fn write_number_with_routes(number: &mdbook_shared::SectionNumber) -> TokenStream {
141      let mdbook_shared::SectionNumber(number) = number;
142      let numbers = number.iter().map(|num| {
143          quote! {
144              #num
145          }
146      });
147  
148      quote! {
149          ::use_mdbook::mdbook_shared::SectionNumber(vec![#(#numbers),*])
150      }
151  }
152  
153  fn write_page_with_routes(book: &mdbook_shared::Page<PathBuf>) -> TokenStream {
154      let Page {
155          title,
156          url,
157          segments,
158          sections,
159          raw: _,
160          id,
161      } = book;
162  
163      let segments = segments.iter().map(|segment| {
164          quote! {
165              #segment.to_string()
166          }
167      });
168  
169      let sections = sections.iter().map(write_section_with_routes);
170  
171      let path = url;
172      let url = match path_to_route_enum(path) {
173          Ok(url) => url,
174          Err(err) => {
175              return err.to_token_stream();
176          }
177      };
178      let id = id.0;
179  
180      quote! {
181          {
182              ::use_mdbook::mdbook_shared::Page {
183                  title: #title.to_string(),
184                  url: #url,
185                  segments: vec![#(#segments,)*],
186                  sections: vec![#(#sections,)*],
187                  raw: String::new(),
188                  id: ::use_mdbook::mdbook_shared::PageId(#id),
189              }
190          }
191      }
192  }
193  
194  fn write_section_with_routes(book: &mdbook_shared::Section) -> TokenStream {
195      let Section { title, id, level } = book;
196  
197      quote! {
198          ::use_mdbook::mdbook_shared::Section {
199              title: #title.to_string(),
200              id: #id.to_string(),
201              level: #level,
202          }
203      }
204  }