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 }