use std::path::Path; use std::{fs::File, io::Read}; use anyhow::{bail, Context}; use serde::Deserialize; use crate::versioned::VersionedCrate; #[derive(Deserialize)] struct RustdocFormatVersion { format_version: u32, } fn detect_rustdoc_format_version(path: &Path, file_data: &str) -> anyhow::Result { let version = serde_json::from_str::(file_data) .with_context(|| format!("unrecognized rustdoc format for file {}", path.display()))?; Ok(version.format_version) } fn parse_or_report_error(path: &Path, file_data: &str, format_version: u32) -> anyhow::Result where T: for<'a> Deserialize<'a>, { serde_json::from_str(file_data).with_context(|| { format!( "unexpected parse error for v{format_version} rustdoc for file {}", path.display() ) }) } pub fn load_rustdoc(path: &Path) -> anyhow::Result { // Parsing JSON after fully reading a file into memory is much faster than // parsing directly from a file, even if buffered: // https://github.com/serde-rs/json/issues/160 let mut file_data = String::new(); File::open(path) .with_context(|| format!("failed to open rustdoc JSON file {}", path.display()))? .read_to_string(&mut file_data) .with_context(|| format!("failed to read rustdoc JSON file {}", path.display()))?; let format_version = detect_rustdoc_format_version(path, &file_data)?; match format_version { {{#each version_numbers}} #[cfg(feature = "v{{this}}")] {{this}} => Ok(VersionedCrate::V{{this}}(parse_or_report_error( path, &file_data, format_version, )?)), {{/each}} _ => bail!( "rustdoc format v{format_version} for file {} is not supported", path.display() ), } }