use crate::dtob_ffi;
use crate::decode_dtob_bytes;
use std::fs;
use std::path::Path;
use chrono::{TimeZone, Utc};
struct TableEntry {
xvuid: String,
title: String,
cid: String,
ccid: String,
created_ts: i64,
edited_ts: i64,
}
pub fn generate_table_to_buf(maps_path: &Path, out: &mut Vec<u8>) {
let map_path = maps_path.join("xvuid_to_meta.dtob");
let mut entries = Vec::new();
if let Ok(data) = fs::read(&map_path) {
unsafe {
if let Some(map_root) = decode_dtob_bytes(&data) {
let len = dtob_ffi::ffi_dtob_kvset_len(map_root);
for i in 0..len {
let key_ptr = dtob_ffi::ffi_dtob_kvset_key(map_root, i);
let val_ptr = dtob_ffi::ffi_dtob_kvset_value_at(map_root, i);
if !key_ptr.is_null() && !val_ptr.is_null() {
let key_c = std::ffi::CStr::from_ptr(key_ptr);
let xvuid_str = key_c.to_string_lossy().into_owned();
let v_title = dtob_ffi::ffi_dtob_array_get(val_ptr, 0);
let v_cid = dtob_ffi::ffi_dtob_array_get(val_ptr, 1);
let v_ccid = dtob_ffi::ffi_dtob_array_get(val_ptr, 2);
let v_created = dtob_ffi::ffi_dtob_array_get(val_ptr, 3);
let v_edited = dtob_ffi::ffi_dtob_array_get(val_ptr, 4);
let title_str = get_raw_str(v_title);
let cid_str = hex::encode(get_raw_bytes(v_cid));
let ccid_str = hex::encode(get_raw_bytes(v_ccid));
let created_ts = dtob_ffi::ffi_dtob_get_u64(v_created as *mut _) as i64;
let edited_ts = dtob_ffi::ffi_dtob_get_u64(v_edited as *mut _) as i64;
entries.push(TableEntry {
xvuid: xvuid_str,
title: title_str,
cid: cid_str,
ccid: ccid_str,
created_ts,
edited_ts,
});
}
}
dtob_ffi::ffi_dtob_free(map_root as *mut _);
}
}
} else {
eprintln!("No parsed metadata map found. Have you run 'sculblog cache-maps'?");
}
entries.sort_by(|a, b| b.edited_ts.cmp(&a.edited_ts));
encode_entries_to_buf(&entries, out);
}
pub fn generate_table(maps_path: &Path) {
let mut buf = Vec::new();
generate_table_to_buf(maps_path, &mut buf);
use std::io::Write;
let _ = std::io::stdout().write_all(&buf);
let _ = std::io::stdout().flush();
}
pub fn generate_table_for_xvuid_to_buf(_maps_path: &Path, cache: &crate::maps::MapsCache, xvuid: &str, out: &mut Vec<u8>) {
let meta_root = match cache.meta_root() {
Some(r) => r,
None => { eprintln!("meta map missing"); return; }
};
unsafe {
let key_c = std::ffi::CString::new(xvuid).unwrap();
let val_ptr = dtob_ffi::ffi_kvset_get(meta_root, key_c.as_ptr());
if val_ptr.is_null() {
eprintln!("No metadata found for {}", xvuid);
return;
}
let v_title = dtob_ffi::ffi_dtob_array_get(val_ptr, 0);
let v_cid = dtob_ffi::ffi_dtob_array_get(val_ptr, 1);
let v_ccid = dtob_ffi::ffi_dtob_array_get(val_ptr, 2);
let v_created = dtob_ffi::ffi_dtob_array_get(val_ptr, 3);
let v_edited = dtob_ffi::ffi_dtob_array_get(val_ptr, 4);
let title_str = get_raw_str(v_title);
let cid_str = hex::encode(get_raw_bytes(v_cid));
let ccid_str = hex::encode(get_raw_bytes(v_ccid));
let created_ts = dtob_ffi::ffi_dtob_get_u64(v_created as *mut _) as i64;
let edited_ts = dtob_ffi::ffi_dtob_get_u64(v_edited as *mut _) as i64;
let entries = vec![TableEntry {
xvuid: xvuid.to_string(),
title: title_str,
cid: cid_str,
ccid: ccid_str,
created_ts,
edited_ts,
}];
encode_entries_to_buf(&entries, out);
}
}
pub fn generate_table_for_xvuid(_maps_path: &Path, cache: &crate::maps::MapsCache, xvuid: &str) {
let mut buf = Vec::new();
generate_table_for_xvuid_to_buf(_maps_path, cache, xvuid, &mut buf);
use std::io::Write;
let _ = std::io::stdout().write_all(&buf);
let _ = std::io::stdout().flush();
}
fn encode_entries_to_buf(entries: &[TableEntry], out: &mut Vec<u8>) {
unsafe {
let out_array = dtob_ffi::ffi_array_new();
for entry in entries {
let kv = dtob_ffi::ffi_kvset_new();
let put_string = |kv: *mut dtob_ffi::DtobValue, k: &str, v: &str| {
if let Ok(k_c) = std::ffi::CString::new(k) {
let val = dtob_ffi::ffi_raw_new(v.as_ptr(), v.len());
dtob_ffi::ffi_kvset_put(kv, k_c.as_ptr(), val);
}
};
let created_date = Utc.timestamp_opt(entry.created_ts, 0).unwrap().format("%d %b %Y %H:%M:%S GMT").to_string();
let edited_date = Utc.timestamp_opt(entry.edited_ts, 0).unwrap().format("%d %b %Y %H:%M:%S GMT").to_string();
put_string(kv, "title", &entry.title);
put_string(kv, "xvuid", &entry.xvuid);
put_string(kv, "cid", &entry.cid);
put_string(kv, "ccid", &entry.ccid);
put_string(kv, "date_created", &created_date);
put_string(kv, "date_edited", &edited_date);
dtob_ffi::ffi_array_push(out_array, kv);
}
let mut out_len = 0;
let out_ptr = dtob_ffi::ffi_encode_dif(out_array, &mut out_len);
if !out_ptr.is_null() && out_len > 0 {
let out_bytes = std::slice::from_raw_parts(out_ptr, out_len);
out.extend_from_slice(out_bytes);
}
dtob_ffi::ffi_dtob_free(out_array);
if !out_ptr.is_null() {
libc::free(out_ptr as *mut libc::c_void);
}
}
}
unsafe fn get_raw_bytes(val: *const dtob_ffi::DtobValue) -> Vec<u8> {
if val.is_null() { return Vec::new(); }
let mut len = 0;
let ptr = dtob_ffi::ffi_dtob_get_raw(val as *mut _, &mut len);
if ptr.is_null() || len == 0 {
Vec::new()
} else {
std::slice::from_raw_parts(ptr, len).to_vec()
}
}
unsafe fn get_raw_str(val: *const dtob_ffi::DtobValue) -> String {
let bytes = get_raw_bytes(val);
String::from_utf8_lossy(&bytes).to_string()
}