use js_export_macro::js_export;
use miden_client::{Felt as NativeFelt, Word as NativeWord};
use super::felt::Felt;
use crate::platform::{JsBytes, JsErr, from_str_err, js_u64_to_u64, u64_to_js_u64};
use crate::utils::{deserialize_from_bytes, serialize_to_bytes};
#[derive(Clone)]
#[js_export]
pub struct Word(NativeWord);
#[js_export]
impl Word {
#[js_export(constructor)]
pub fn new(u64_vec: Vec<JsU64>) -> Result<Word, JsErr> {
if u64_vec.len() != 4 {
return Err(from_str_err(&format!(
"Word requires exactly 4 elements, got {}",
u64_vec.len()
)));
}
let fixed_array_u64: [u64; 4] = u64_vec
.into_iter()
.map(js_u64_to_u64)
.collect::<Vec<u64>>()
.try_into()
.expect("length checked above");
let native_felt_vec: [NativeFelt; 4] = fixed_array_u64
.iter()
.map(|&v| NativeFelt::new(v))
.collect::<Result<Vec<NativeFelt>, _>>()
.map_err(|err| from_str_err(&format!("invalid field element: {err}")))?
.try_into()
.expect("length checked above");
Ok(Word(native_felt_vec.into()))
}
#[js_export(js_name = "toU64s")]
pub fn to_u64s(&self) -> Vec<JsU64> {
self.0.iter().map(|f| u64_to_js_u64(NativeFelt::as_canonical_u64(f))).collect()
}
#[js_export(js_name = "fromHex")]
pub fn from_hex(hex: String) -> Result<Word, JsErr> {
let native_word = NativeWord::try_from(hex.as_str())
.map_err(|err| from_str_err(&format!("Error instantiating Word from hex: {err}")))?;
Ok(Word(native_word))
}
#[js_export(js_name = "newFromFelts")]
#[allow(clippy::needless_pass_by_value)]
pub fn new_from_felts(felt_vec: Vec<Felt>) -> Word {
assert!(
felt_vec.len() == 4,
"Word requires exactly 4 field elements, got {}",
felt_vec.len()
);
let native_felt_vec: [NativeFelt; 4] = felt_vec
.iter()
.map(|felt: &Felt| felt.into())
.collect::<Vec<NativeFelt>>()
.try_into()
.expect("Word requires exactly 4 field elements");
Word(native_felt_vec.into())
}
#[js_export(js_name = "toHex")]
pub fn to_hex(&self) -> String {
self.0.to_hex()
}
pub fn serialize(&self) -> JsBytes {
serialize_to_bytes(&self.0)
}
pub fn deserialize(bytes: JsBytes) -> Result<Word, JsErr> {
let native_word = deserialize_from_bytes::<NativeWord>(&bytes)?;
Ok(Word(native_word))
}
#[js_export(js_name = "toFelts")]
pub fn to_felts(&self) -> Vec<Felt> {
self.0.iter().map(|felt| Felt::from(*felt)).collect::<Vec<Felt>>()
}
}
impl Word {
pub(crate) fn as_native(&self) -> &NativeWord {
&self.0
}
}
impl From<NativeWord> for Word {
fn from(native_word: NativeWord) -> Self {
Word(native_word)
}
}
impl From<&NativeWord> for Word {
fn from(native_word: &NativeWord) -> Self {
Word(*native_word)
}
}
impl From<Word> for NativeWord {
fn from(word: Word) -> Self {
word.0
}
}
impl From<&Word> for NativeWord {
fn from(word: &Word) -> Self {
word.0
}
}
impl_napi_from_value!(Word);