use std::ops::Range; use super::info::{LinkInfo, PageInfo}; #[derive(Debug, Clone, Copy)] pub struct Page

{ /// Index of the first link belonging to this page. pub start: u32, pub data: P, } impl

Page

{ pub fn change_data(self, f: impl Fn(P) -> P2) -> Page { Page { start: self.start, data: f(self.data), } } } #[derive(Debug, Clone, Copy)] pub struct Link { /// Index of the page this link points to. pub to: u32, pub data: L, } impl Link { pub fn change_data(self, f: impl Fn(L) -> L2) -> Link { Link { to: self.to, data: f(self.data), } } } pub struct AdjacencyList { pub pages: Vec>, pub links: Vec>, } impl Default for AdjacencyList { fn default() -> Self { Self { pages: Default::default(), links: Default::default(), } } } impl AdjacencyList { pub fn push_page(&mut self, data: P) { self.pages.push(Page { start: self.links.len() as u32, data, }); } pub fn push_link(&mut self, to: u32, data: L) { self.links.push(Link { to, data }) } pub fn page(&self, page_idx: u32) -> &Page

{ &self.pages[page_idx as usize] } pub fn page_mut(&mut self, page_idx: u32) -> &mut Page

{ &mut self.pages[page_idx as usize] } pub fn pages(&self) -> impl Iterator)> { self.pages.iter().enumerate().map(|(i, p)| (i as u32, p)) } pub fn link(&self, link_idx: u32) -> &Link { &self.links[link_idx as usize] } pub fn link_mut(&mut self, link_idx: u32) -> &mut Link { &mut self.links[link_idx as usize] } pub fn link_range(&self, page_idx: u32) -> Range { let start_idx = self.pages[page_idx as usize].start; let end_idx = match self.pages.get(page_idx as usize + 1) { Some(page) => page.start, None => self.links.len() as u32, }; start_idx..end_idx } pub fn link_redirect(&self, page_idx: u32) -> Option { let range = self.link_range(page_idx); if range.is_empty() { None } else { Some(range.start) } } pub fn links(&self, page_idx: u32) -> impl Iterator)> { self.link_range(page_idx).map(|i| (i, self.link(i))) } pub fn change_page_data(self, page_f: impl Fn(P) -> P2 + Copy) -> AdjacencyList { let pages = self .pages .into_iter() .map(|p| p.change_data(page_f)) .collect::>(); AdjacencyList { pages, links: self.links, } } pub fn change_link_data(self, link_f: impl Fn(L) -> L2 + Copy) -> AdjacencyList { let links = self .links .into_iter() .map(|l| l.change_data(link_f)) .collect::>(); AdjacencyList { pages: self.pages, links, } } } impl AdjacencyList { pub fn check_consistency(&self) { // Check that all types are large enough assert!(self.pages.len() < u32::MAX as usize, "too many pages"); assert!(self.links.len() < u32::MAX as usize, "too many links"); for page in &self.pages { assert!( page.data.title.len() <= u8::MAX as usize, "page title too long" ); } // Check that all links contain valid indices. Links must not link to // the sentinel page. let range = 0..self.pages.len() as u32; for link in &self.links { assert!(range.contains(&link.to), "invalid link"); } // Check that all redirect pages have at most one link for (page_idx, page) in self.pages.iter().enumerate() { if page.data.redirect { let range = self.link_range(page_idx as u32); let amount = range.end - range.start; assert!(amount <= 1, "too many redirect links"); } } } }