use super::{features::Features, PkgKey};
use crate::{
database::CachedDocInfo,
event::{Event, Sender},
local_registry::PkgInfo,
Result,
};
use bincode::config;
use bytesize::ByteSize;
use serde::{de::DeserializeOwned, Serialize};
use std::{io::Write, path::PathBuf};
use xz2::write::{XzDecoder, XzEncoder};
#[derive(Clone)]
pub struct PkgWithFeatures {
pub features: Features,
pub info: PkgInfo,
}
pub fn build(sender: Sender, db_dir: PathBuf, pkg: PkgWithFeatures) -> PkgKey {
let in_progress = PkgKey::new(pkg.info.to_name_ver(), pkg.features.clone());
rayon::spawn(move || {
let cargo_toml = pkg.info.path().join("Cargo.toml");
let dir = match tempfile::tempdir() {
Ok(dir) => dir,
Err(err) => {
error!("Can't create a tempdir:\n{err}");
return;
}
};
let mut cache_info =
CachedDocInfo::new(pkg.info.to_name_ver(), pkg.features.clone(), db_dir);
info!(?cache_info.pkg, "begin to compile the doc under {}", dir.path().display());
let compile = rustdoc_json::Builder::default()
.toolchain("nightly")
.silent(true)
.target_dir(&dir)
.manifest_path(&cargo_toml);
let built = match pkg.features {
Features::Default => compile,
Features::All => compile.all_features(true),
Features::DefaultPlus(f) => compile.features(f.iter()),
Features::NoDefault => compile.no_default_features(true),
Features::NoDefaultPlus(f) => compile.no_default_features(true).features(f.iter()),
}
.build();
match built {
Ok(json_path) => {
let meta = cache_info.meta_mut();
meta.set_finished_duration();
let duration = meta.duration_as_secs();
info!(?cache_info.pkg, ?json_path, "succeefully compiled the doc in {duration:.2}s");
if let Err(err) = cache_info.save_doc(&json_path, pkg.info) {
error!("{err}");
}
match sender.send(Event::DocCompiled(Box::new(cache_info))) {
Ok(()) => (),
Err(err) => {
error!(
"Failed to send `DocCompiled` event when CachedDocInfo is ready:\n{err}"
)
}
}
}
Err(err) => error!("Failed to compile {}:\n{err}", cargo_toml.display()),
}
});
in_progress
}
pub fn encode<T: Serialize>(t: T) -> Result<Vec<u8>> {
Ok(bincode::serde::encode_to_vec(t, config::standard())?)
}
pub fn decode<T: DeserializeOwned>(raw: &[u8]) -> Result<T> {
Ok(bincode::serde::decode_from_slice(raw, config::standard())?.0)
}
pub fn encode_with_xz<T: Serialize>(t: T) -> Result<Vec<u8>> {
let raw = bincode::serde::encode_to_vec(t, config::standard())?;
xz_encode_on_bytes(&raw)
}
pub fn xz_encode_on_bytes(raw: &[u8]) -> Result<Vec<u8>> {
let mut compressed = Vec::with_capacity(raw.len() / 2);
let mut xz_encoder = XzEncoder::new(&mut compressed, 9);
xz_encoder.write_all(raw)?;
xz_encoder.finish()?;
compressed.shrink_to_fit();
let (before, after) = (raw.len(), compressed.len());
info!(
"compress {} => {} (reduced {:.1}%)",
ByteSize(before as u64),
ByteSize(after as u64),
(after as f32 / before as f32 - 1.0) * 100.0
);
Ok(compressed)
}
pub fn decode_with_xz<T: DeserializeOwned>(raw: &[u8]) -> Result<T> {
let decompressed = xz_decode_on_bytes(raw)?;
Ok(bincode::serde::decode_from_slice(&decompressed, config::standard())?.0)
}
pub fn xz_decode_on_bytes(raw: &[u8]) -> Result<Vec<u8>> {
let mut decompressed = Vec::with_capacity(raw.len() * 4);
{
let mut xz_decoder = XzDecoder::new(&mut decompressed);
xz_decoder.write_all(raw)?;
xz_decoder.finish()?;
}
let (before, after) = (raw.len(), decompressed.len());
info!(
"decompress {} => {} (ratio {:.1}%)",
ByteSize(before as u64),
ByteSize(after as u64),
(1.0 - before as f32 / after as f32) * 100.0
);
Ok(decompressed)
}