use rusqlite::Connection;
use crate::container::{traverse_loose, Compression, Container};
use crate::{io_packs, Error};
pub fn pack_loose(cnt: &Container) -> Result<(), Error> {
let compression = cnt.compression()?;
_pack_loose_internal(cnt, &compression)
}
pub fn _pack_loose_internal(cnt: &Container, compression: &Compression) -> Result<(), Error> {
cnt.valid()?;
let loose_objs = traverse_loose(cnt)?;
let conn = Connection::open(cnt.packs_db())?;
let mut stmt = conn.prepare("SELECT hashkey FROM db_object")?;
let rows: Vec<_> = stmt
.query([])?
.mapped(|row| row.get::<_, String>(0))
.filter_map(std::result::Result::ok)
.collect();
let sources = loose_objs.filter(|obj| {
let hash = obj.parent().and_then(|p| p.file_name()).and_then(|parent| {
obj.file_name().map(|filename| {
format!("{}{}", parent.to_str().unwrap(), filename.to_str().unwrap())
})
});
hash.map_or(false, |h| !rows.contains(&h))
});
io_packs::_insert_many_internal(sources, cnt, compression)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::io_loose::insert as loose_insert;
use crate::io_packs::extract as packs_extract;
use crate::stat;
use crate::test_utils::new_container;
use std::collections::HashMap;
#[test]
fn pack_loose_default() {
let (_tmp_dir, cnt) = new_container(1024, "none");
let n = 200;
let mut hash_content_map: HashMap<String, String> = HashMap::new();
for i in 0..n {
let content = format!("test {i:03}"); let buf = content.clone().into_bytes();
let (_, hash) = loose_insert(buf, &cnt).unwrap();
hash_content_map.insert(hash, content);
}
let info = stat(&cnt).unwrap();
assert_eq!(info.count.loose, n);
pack_loose(&cnt).unwrap();
let info = stat(&cnt).unwrap();
assert_eq!(info.count.packs, n);
assert_eq!(info.count.packs_file, n * 8 / 1024 + 1);
for (hash, content) in hash_content_map {
let obj = packs_extract(&hash, &cnt).unwrap().unwrap();
assert_eq!(String::from_utf8(obj.try_into().unwrap()).unwrap(), content);
}
}
#[test]
fn pack_loose_default_compress() {
let (_tmp_dir, cnt) = new_container(1024, "zlib:+1");
let mut hash_content_map: HashMap<String, String> = HashMap::new();
for i in 0..100 {
let content = format!("test {i}").repeat(i); let buf = content.clone().into_bytes();
let (_, hash) = loose_insert(buf, &cnt).unwrap();
hash_content_map.insert(hash, content);
}
let info = stat(&cnt).unwrap();
assert_eq!(info.count.loose, 100);
pack_loose(&cnt).unwrap();
let info = stat(&cnt).unwrap();
assert_eq!(info.count.packs, 100);
for (hash, content) in hash_content_map {
let obj = packs_extract(&hash, &cnt).unwrap().unwrap();
assert_eq!(String::from_utf8(obj.try_into().unwrap()).unwrap(), content);
}
}
}