use std::fmt;
use std::fs;
use std::path::Path;
use crate::archive;
use crate::error::Result;
#[derive(Clone, PartialEq, Eq)]
pub struct Weights {
bytes: Vec<u8>,
}
impl Weights {
pub fn from_bytes<B>(bytes: B) -> Result<Self>
where
B: Into<Vec<u8>>,
{
let bytes = bytes.into();
archive::check_magic(&bytes)?;
Ok(Weights { bytes })
}
pub fn from_file<P>(path: P) -> Result<Self>
where
P: AsRef<Path>,
{
Self::from_bytes(fs::read(path.as_ref())?)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
pub(crate) fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl fmt::Debug for Weights {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Weights")
.field("len", &self.bytes.len())
.finish()
}
}
#[cfg(feature = "download")]
#[cfg_attr(docsrs, doc(cfg(feature = "download")))]
pub const WEIGHTS_REVISION: &str = "b358ddadd89b7a713b5aa131f23032d3cca1b251";
#[cfg(feature = "download")]
#[cfg_attr(docsrs, doc(cfg(feature = "download")))]
pub const WEIGHTS_SHA256: &str = "b6e02f048568ac5d01a2042556c658061e699acbc0aa2a1439f52f3d461dffeb";
#[cfg(feature = "download")]
pub(crate) const PIN: crate::download::Pin = crate::download::Pin {
repo: "Cactus-Compute/whistle",
revision: WEIGHTS_REVISION,
file: "whistle.cact",
sha256: WEIGHTS_SHA256,
env: "CACTUS_WHISTLE_WEIGHTS",
cache_dir: "whistle",
};
#[cfg(feature = "download")]
impl Weights {
#[cfg_attr(docsrs, doc(cfg(feature = "download")))]
pub fn fetch() -> Result<Self> {
Weights::from_bytes(crate::download::fetch(&PIN)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Error;
#[test]
fn the_magic_tag_is_accepted() {
let weights = Weights::from_bytes(vec![0x84, 0x2A, 0xE1, 0x05, 0x45]).expect("the tag");
assert_eq!(weights.len(), 5);
assert!(!weights.is_empty());
}
#[test]
fn a_wav_file_is_not_an_archive() {
let error = Weights::from_bytes(b"RIFF\0\0\0\0WAVE".to_vec()).expect_err("a WAV header");
assert!(matches!(
error,
Error::UnsupportedWeights { tag: 0x4646_4952 }
));
}
#[test]
fn a_short_file_is_not_an_archive() {
let error = Weights::from_bytes(vec![0x84, 0x2A]).expect_err("truncated header");
assert!(matches!(error, Error::UnsupportedWeights { tag: 0 }));
}
#[test]
fn debug_does_not_dump_the_bytes() {
let weights = Weights::from_bytes(vec![0x84, 0x2A, 0xE1, 0x05, 0xAB]).expect("the tag");
assert_eq!(format!("{weights:?}"), "Weights { len: 5 }");
}
#[cfg(feature = "download")]
#[test]
fn the_pin_names_the_whistle_archive() {
assert_eq!(PIN.file, crate::Model::Whistle.archive());
assert_eq!(PIN.revision.len(), 40);
assert_eq!(PIN.sha256.len(), 64);
assert_ne!(PIN.env, crate::needle::weights::PIN.env);
assert_ne!(PIN.cache_dir, crate::needle::weights::PIN.cache_dir);
}
}