use jreflection::*; use std::collections::*; use std::path::*; use std::io::{self, BufWriter, Write}; use std::time::Instant; const CLASSES_PLACEHOLDER : &'static str = "{CLASSES}"; const TEMPLATE_MD : &'static str = include_str!("template.md"); const TEMPLATE_HTML : &'static str = include_str!("template.html"); #[derive(Clone, Copy)] enum Consistency { None, Consistent(T), Inconsistent(T, T), } impl std::default::Default for Consistency { fn default() -> Self { Consistency::None } } impl Consistency { pub fn merge(&mut self, value: T) { *self = match take(self) { Consistency::None => Consistency::Consistent(value), Consistency::Consistent(v) => { if v == value { Consistency::Consistent(v) } else { Consistency::Inconsistent(v, value) } }, Consistency::Inconsistent(a,b) => Consistency::Inconsistent(a,b), }; } pub fn into_consistent(self) -> Option { match self { Consistency::Consistent(v) => Some(v), _ => None, } } } #[derive(Clone, Copy, Default)] struct Class { pub is_public: Consistency, pub java_jdk: u64, pub java_jre: u64, pub aojdk_jdk: u64, pub aojdk_jre: u64, pub android_adk: u64, } struct Classes(BTreeMap); impl Classes { pub fn new() -> Self { Self(BTreeMap::new()) } pub fn injest_jdk_dir(&mut self, path: impl AsRef, set_class_bit: impl Fn(&mut Class)) { let path = path.as_ref(); println!("Injesting {}", path.display()); self.injest_src(Source::from_jdk_dir(path).unwrap(), set_class_bit); } pub fn injest_jar(&mut self, path: impl AsRef, set_class_bit: impl Fn(&mut Class)) { let path = path.as_ref(); println!("Injesting {}", path.display()); self.injest_src(Source::from_jar(path).unwrap(), set_class_bit); } fn injest_src(&mut self, src: Source, set_class_bit: impl Fn(&mut Class)) { src.for_each_class(|name|{ let entry = self.0.entry(name.to_string()).or_default(); set_class_bit(entry); let class = src.read_class(name).expect("Unable to read class"); entry.is_public.merge(class.is_public()); Ok(()) }).unwrap(); } pub fn write_markdown_to(&self, path: impl AsRef) -> io::Result<()> { self.write_template_to(path, TEMPLATE_MD) } pub fn write_html_to(&self, path: impl AsRef) -> io::Result<()> { self.write_template_to(path, TEMPLATE_HTML) } fn write_template_to(&self, path: impl AsRef, template: &str) -> io::Result<()> { let path = path.as_ref(); println!("Writing {} classes to {}...", self.0.len(), path.display()); let classes_placeholder_index = template.find(CLASSES_PLACEHOLDER).expect("{CLASSES} placeholder missing from template"); let (template_pre, template_post) = template.split_at(classes_placeholder_index); let template_post = &template_post[CLASSES_PLACEHOLDER.len()..]; let mut classes_md = BufWriter::new(std::fs::File::create(path)?); write!(classes_md, "{}", template_pre)?; let mut public = 0; for (name, data) in self.0.iter() { if !data.is_public.into_consistent().unwrap_or(true) { continue; // Skip non-public classes } public += 1; write!(classes_md, " {}", name)?; for (col, max) in [ (data.java_jdk, 13), (data.java_jre, 8), (data.aojdk_jdk, 13), (data.aojdk_jre, 13), (data.android_adk, 29), ].iter().copied() { write!(classes_md, "")?; Self::write_versions_column(&mut classes_md, col, max)?; } writeln!(classes_md, "")?; } write!(classes_md, "{}", template_post)?; println!(" {} were public", public); Ok(()) } fn write_versions_column(md: &mut impl Write, versions: u64, max : u64) -> io::Result<()> { let mut anything = false; let mut bit = 0; while bit < 64 { if (1<(dest: &mut T) -> T where T: Default { std::mem::replace(dest, T::default()) }