#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
extern crate alloc;
use alloc::{boxed::Box, vec::Vec};
#[doc(hidden)]
pub type __InputVector = Vec<f32>;
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
#[doc(inline)]
pub use to_values_derive::ToInputVector;
pub trait ToInputVector {
#[must_use]
#[inline]
fn input_vector_len(&self) -> usize {
0
}
fn append_input_vector(&self, output: &mut Vec<f32>);
#[must_use]
#[inline]
fn to_input_vector(&self) -> Vec<f32> {
let mut output = Vec::with_capacity(self.input_vector_len());
self.append_input_vector(&mut output);
output
}
#[must_use]
#[inline]
fn members_as_f32_vector(&self) -> Vec<f32> {
self.to_input_vector()
}
}
macro_rules! impl_numeric_input {
($($type:ty),+ $(,)?) => {
$(
impl ToInputVector for $type {
#[inline]
fn input_vector_len(&self) -> usize {
1
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
output.push(*self as f32);
}
}
)+
};
}
impl_numeric_input!(
f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize,
);
impl ToInputVector for bool {
#[inline]
fn input_vector_len(&self) -> usize {
1
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
output.push(f32::from(*self));
}
}
impl ToInputVector for () {
#[inline]
fn input_vector_len(&self) -> usize {
0
}
#[inline]
fn append_input_vector(&self, _output: &mut Vec<f32>) {}
}
impl<T> ToInputVector for [T]
where
T: ToInputVector,
{
#[inline]
fn input_vector_len(&self) -> usize {
self.iter().fold(0, |length, item| {
length.saturating_add(item.input_vector_len())
})
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
for item in self {
item.append_input_vector(output);
}
}
}
impl<T, const N: usize> ToInputVector for [T; N]
where
T: ToInputVector,
{
#[inline]
fn input_vector_len(&self) -> usize {
self.as_slice().input_vector_len()
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
self.as_slice().append_input_vector(output);
}
}
impl<T> ToInputVector for Vec<T>
where
T: ToInputVector,
{
#[inline]
fn input_vector_len(&self) -> usize {
self.as_slice().input_vector_len()
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
self.as_slice().append_input_vector(output);
}
}
impl<T> ToInputVector for Box<T>
where
T: ToInputVector + ?Sized,
{
#[inline]
fn input_vector_len(&self) -> usize {
(**self).input_vector_len()
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
(**self).append_input_vector(output);
}
}
impl<T> ToInputVector for &T
where
T: ToInputVector + ?Sized,
{
#[inline]
fn input_vector_len(&self) -> usize {
(*self).input_vector_len()
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
(*self).append_input_vector(output);
}
}
impl<T> ToInputVector for &mut T
where
T: ToInputVector + ?Sized,
{
#[inline]
fn input_vector_len(&self) -> usize {
(**self).input_vector_len()
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
(**self).append_input_vector(output);
}
}
macro_rules! impl_tuple_input {
($($type:ident : $index:tt),+ $(,)?) => {
impl<$($type),+> ToInputVector for ($($type,)+)
where
$($type: ToInputVector),+
{
#[inline]
fn input_vector_len(&self) -> usize {
let mut length = 0usize;
$(length = length.saturating_add(self.$index.input_vector_len());)+
length
}
#[inline]
fn append_input_vector(&self, output: &mut Vec<f32>) {
$(self.$index.append_input_vector(output);)+
}
}
};
}
impl_tuple_input!(A: 0);
impl_tuple_input!(A: 0, B: 1);
impl_tuple_input!(A: 0, B: 1, C: 2);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3, E: 4);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9);
impl_tuple_input!(A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9, K: 10);
impl_tuple_input!(
A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9, K: 10, L: 11
);
#[cfg(test)]
mod tests {
use alloc::{boxed::Box, vec, vec::Vec};
use super::ToInputVector;
#[test]
fn numeric_primitives_convert_to_one_value() {
assert_eq!(42_u16.to_input_vector(), vec![42.0]);
assert_eq!((-3_i8).to_input_vector(), vec![-3.0]);
assert_eq!(1.25_f64.to_input_vector(), vec![1.25]);
}
#[test]
fn booleans_are_zero_or_one() {
assert_eq!(false.to_input_vector(), vec![0.0]);
assert_eq!(true.to_input_vector(), vec![1.0]);
}
#[test]
fn collections_flatten_in_order() {
let values = vec![[1_u8, 2], [3, 4]];
assert_eq!(values.input_vector_len(), 4);
assert_eq!(values.to_input_vector(), vec![1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn indirection_and_empty_values_are_supported() {
let mut value = Box::new(((), 3_u8));
let reference = &mut value;
assert_eq!(reference.input_vector_len(), 1);
assert_eq!(
<&mut Box<((), u8)> as ToInputVector>::to_input_vector(&reference),
vec![3.0]
);
}
#[test]
fn tuples_flatten_in_order() {
let value = (1_u8, [2_i16, 3_i16], true);
assert_eq!(value.input_vector_len(), 4);
assert_eq!(value.to_input_vector(), vec![1.0, 2.0, 3.0, 1.0]);
}
#[test]
fn custom_types_can_compose_fields_and_skip_fields() {
struct Entity {
position: [f32; 2],
id: u64,
money: f32,
}
impl ToInputVector for Entity {
fn input_vector_len(&self) -> usize {
self.position.input_vector_len() + self.money.input_vector_len()
}
fn append_input_vector(&self, output: &mut Vec<f32>) {
self.position.append_input_vector(output);
self.money.append_input_vector(output);
}
}
let entity = Entity {
position: [4.0, -1.0],
id: 99,
money: 12.5,
};
assert_eq!(entity.id, 99);
assert_eq!(entity.members_as_f32_vector(), vec![4.0, -1.0, 12.5]);
}
#[test]
fn custom_types_can_omit_the_capacity_hint() {
struct Dynamic;
impl ToInputVector for Dynamic {
fn append_input_vector(&self, output: &mut Vec<f32>) {
output.extend([3.0, 5.0]);
}
}
assert_eq!(Dynamic.input_vector_len(), 0);
assert_eq!(Dynamic.to_input_vector(), vec![3.0, 5.0]);
}
}