use std::fs;
use std::path::Path;
use prikk_error::{PrikkError, Result};
use prikk_object::ObjectId;
use crate::byte_cursor::ByteCursor;
use crate::file_codec::push_bytes_u64;
use crate::fsutil::write_file_atomically;
use crate::layout::RepositoryLayout;
const REF_POINTER_MAGIC: &[u8; 8] = b"PREFPTR1";
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RefPointer {
pub ref_name: String,
pub ref_state_id: ObjectId,
}
pub(crate) fn read_ref_pointer(path: &Path) -> Result<RefPointer> {
let bytes = fs::read(path)?;
decode_ref_pointer(&bytes)
}
pub(crate) fn write_ref_pointer_candidate(
layout: &RepositoryLayout,
ref_name: &str,
ref_state_id: ObjectId,
) -> Result<()> {
let candidate = layout.ref_tmp_path(ref_name);
let bytes = encode_ref_pointer(ref_name, ref_state_id)?;
write_file_atomically(&candidate, &bytes)
}
fn encode_ref_pointer(ref_name: &str, ref_state_id: ObjectId) -> Result<Vec<u8>> {
let mut out = Vec::new();
out.extend_from_slice(REF_POINTER_MAGIC);
push_bytes_u64(&mut out, ref_name.as_bytes())?;
out.extend_from_slice(ref_state_id.as_bytes());
Ok(out)
}
fn decode_ref_pointer(bytes: &[u8]) -> Result<RefPointer> {
let mut cursor = ByteCursor::new(bytes);
let magic = cursor.read_array::<8>()?;
if &magic != REF_POINTER_MAGIC {
return Err(PrikkError::MalformedData(
"invalid ref pointer magic".to_string(),
));
}
let ref_name_bytes = cursor.read_bytes_u64()?;
let ref_name = String::from_utf8(ref_name_bytes)
.map_err(|err| PrikkError::MalformedData(format!("invalid ref name utf-8: {err}")))?;
let ref_state_id = ObjectId::from_bytes(cursor.read_array::<32>()?);
if !cursor.is_finished() {
return Err(PrikkError::MalformedData(
"trailing bytes in ref pointer".to_string(),
));
}
Ok(RefPointer {
ref_name,
ref_state_id,
})
}