1use std::fmt::{self, Debug};
6
7use crate::buf::{Buf, Element, PackedElement};
8use crate::error::{Error, ErrorKind};
9use crate::dev::DevState;
10
11
12pub type Scalar<T> = Buf<T, 0>;
18pub type Spectrum<T> = Buf<T, 1>;
20pub type Image<T> = Buf<T, 2>;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26#[repr(transparent)]
27pub struct DevEnum(pub i16);
28
29#[derive(Debug, Clone, PartialEq)]
36pub struct DevEncoded {
37 pub format: Scalar<str>,
40 pub data: Spectrum<u8>,
42}
43
44impl DevEncoded {
45 #[inline]
47 pub fn new(format: impl Into<Scalar<str>>, data: impl Into<Spectrum<u8>>) -> Self {
48 Self { format: format.into(), data: data.into() }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq)]
56pub enum Value {
57 Void,
58 Bool(ValueFormat<bool>),
59 Octet(ValueFormat<u8>),
60 UShort(ValueFormat<u16>),
61 Short(ValueFormat<i16>),
62 ULong(ValueFormat<u32>),
63 Long(ValueFormat<i32>),
64 ULong64(ValueFormat<u64>),
65 Long64(ValueFormat<i64>),
66 Float(ValueFormat<f32>),
67 Double(ValueFormat<f64>),
68 Enum(ValueFormat<DevEnum>),
69 State(ValueFormat<DevState>),
70 String(ValueFormat<str>),
71 LongString(Spectrum<i32>, Spectrum<str>),
73 DoubleString(Spectrum<f64>, Spectrum<str>),
75 Encoded(DevEncoded),
77}
78
79pub enum ValueFormat<T: Element + ?Sized> {
81 Scalar(Scalar<T>),
82 Spectrum(Spectrum<T>),
83 Image(Image<T>),
84}
85
86macro_rules! impl_value_format {
87 ( $( $variant:ident: $get:ident, $into:ident; )* ) => {
88 impl<T: Element + ?Sized> ValueFormat<T> {
89 $(
90 #[doc = concat!("The data, if it is a ", stringify!($get), ".")]
91 #[inline]
92 pub fn $get(&self) -> Option<&$variant<T>> {
93 match self {
94 Self::$variant(data) => Some(data),
95 _ => None,
96 }
97 }
98
99 #[doc = concat!("The data, if it is a ", stringify!($get), ", or this format back.")]
100 #[inline]
101 pub fn $into(self) -> Result<$variant<T>, Self> {
102 match self {
103 Self::$variant(data) => Ok(data),
104 format => Err(format),
105 }
106 }
107 )*
108 }
109 };
110}
111
112impl_value_format! {
113 Scalar: scalar, into_scalar;
114 Spectrum: spectrum, into_spectrum;
115 Image: image, into_image;
116}
117
118impl<T: Element + ?Sized> Clone for ValueFormat<T> {
119 fn clone(&self) -> Self {
120 match self {
121 Self::Scalar(data) => Self::Scalar(data.clone()),
122 Self::Spectrum(data) => Self::Spectrum(data.clone()),
123 Self::Image(data) => Self::Image(data.clone()),
124 }
125 }
126}
127
128impl<T: Element + ?Sized> Debug for ValueFormat<T> {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match self {
132 Self::Scalar(data) => data.fmt(f),
133 Self::Spectrum(data) => data.fmt(f),
134 Self::Image(data) => data.fmt(f),
135 }
136 }
137}
138
139impl<T: Element + ?Sized> PartialEq for ValueFormat<T>
140where
141 for<'a> T::Ref<'a>: PartialEq,
142{
143 fn eq(&self, other: &Self) -> bool {
144 match (self, other) {
145 (Self::Scalar(a), Self::Scalar(b)) => a == b,
146 (Self::Spectrum(a), Self::Spectrum(b)) => a == b,
147 (Self::Image(a), Self::Image(b)) => a == b,
148 _ => false,
149 }
150 }
151}
152
153pub trait TangoElement: Element {
156
157 const ATTR_KIND: AttrDataKind;
159 const CMD_SCALAR_KIND: Option<CommandDataKind>;
161 const CMD_ARRAY_KIND: Option<CommandDataKind>;
163
164 fn wrap(format: ValueFormat<Self>) -> Value;
165 fn unwrap(data: Value) -> Result<ValueFormat<Self>, Value>;
166 fn unwrap_ref(data: &Value) -> Option<&ValueFormat<Self>>;
167
168}
169
170macro_rules! impl_tango_element {
171 ($($ty:ty => $variant:ident, $scalar:expr, $array:expr;)*) => { $(
172 impl TangoElement for $ty {
173
174 const ATTR_KIND: AttrDataKind = AttrDataKind::$variant;
175 const CMD_SCALAR_KIND: Option<CommandDataKind> = $scalar;
176 const CMD_ARRAY_KIND: Option<CommandDataKind> = $array;
177
178 #[inline]
179 fn wrap(format: ValueFormat<Self>) -> Value {
180 Value::$variant(format)
181 }
182
183 #[inline]
184 fn unwrap(data: Value) -> Result<ValueFormat<Self>, Value> {
185 match data {
186 Value::$variant(format) => Ok(format),
187 data => Err(data),
188 }
189 }
190
191 #[inline]
192 fn unwrap_ref(data: &Value) -> Option<&ValueFormat<Self>> {
193 match data {
194 Value::$variant(format) => Some(format),
195 _ => None,
196 }
197 }
198
199 }
200 )* };
201}
202
203impl_tango_element! {
204 bool => Bool, Some(CommandDataKind::Bool), Some(CommandDataKind::BoolArray);
205 u8 => Octet, Some(CommandDataKind::Octet), Some(CommandDataKind::OctetArray);
206 u16 => UShort, Some(CommandDataKind::UShort), Some(CommandDataKind::UShortArray);
207 i16 => Short, Some(CommandDataKind::Short), Some(CommandDataKind::ShortArray);
208 u32 => ULong, Some(CommandDataKind::ULong), Some(CommandDataKind::ULongArray);
209 i32 => Long, Some(CommandDataKind::Long), Some(CommandDataKind::LongArray);
210 u64 => ULong64, Some(CommandDataKind::ULong64), Some(CommandDataKind::ULong64Array);
211 i64 => Long64, Some(CommandDataKind::Long64), Some(CommandDataKind::Long64Array);
212 f32 => Float, Some(CommandDataKind::Float), Some(CommandDataKind::FloatArray);
213 f64 => Double, Some(CommandDataKind::Double), Some(CommandDataKind::DoubleArray);
214 DevEnum => Enum, None, None;
215 DevState => State, Some(CommandDataKind::State), Some(CommandDataKind::StateArray);
216 str => String, Some(CommandDataKind::String), Some(CommandDataKind::StringArray);
217}
218
219pub trait FromValue: Sized {
229 fn from_value(value: Value) -> Result<Self, Error>;
230}
231
232impl<T> FromValue for T
233where
234 T: TryFrom<Value>,
235 T::Error: Into<Error>,
236{
237 #[inline]
238 fn from_value(value: Value) -> Result<Self, Error> {
239 T::try_from(value).map_err(Into::into)
240 }
241}
242
243pub trait IntoValue {
249 fn into_value(self) -> Value;
250}
251
252impl<T: Into<Value>> IntoValue for T {
253 #[inline]
254 fn into_value(self) -> Value {
255 self.into()
256 }
257}
258
259macro_rules! impl_data_conversions {
261 ( $( $variant:ident: $get:ident, $into:ident; )* ) => {
262 $(
263 impl<T: TangoElement + ?Sized> From<$variant<T>> for Value {
264 #[inline]
265 fn from(data: $variant<T>) -> Self {
266 T::wrap(ValueFormat::$variant(data))
267 }
268 }
269
270 impl<T: TangoElement + ?Sized> TryFrom<Value> for $variant<T> {
271 type Error = Error;
272 fn try_from(data: Value) -> Result<Self, Error> {
273 T::unwrap(data)
274 .and_then(|format| format.$into().map_err(T::wrap))
275 .map_err(|data| Error::new(ErrorKind::InvalidData)
276 .with_description(format!("Value is not a {:?} {}", T::ATTR_KIND, stringify!($get)))
277 .with_append_description(format_args!("Got: {data:?}")))
278 }
279 }
280
281 impl<'a, T: TangoElement + ?Sized> TryFrom<&'a Value> for &'a $variant<T> {
282 type Error = Error;
283 fn try_from(data: &'a Value) -> Result<Self, Error> {
284 T::unwrap_ref(data)
285 .and_then(ValueFormat::$get)
286 .ok_or_else(|| Error::new(ErrorKind::InvalidData)
287 .with_description(format!("Value is not a {:?} {}", T::ATTR_KIND, stringify!($get)))
288 .with_append_description(format_args!("Got: {data:?}")))
289 }
290 }
291 )*
292 };
293}
294
295impl_data_conversions! {
296 Scalar: scalar, into_scalar;
297 Spectrum: spectrum, into_spectrum;
298 Image: image, into_image;
299}
300
301impl<T: TangoElement + PackedElement> From<T> for Value {
302 #[inline]
303 fn from(value: T) -> Self {
304 Buf::<T, 0>::from(value).into()
305 }
306}
307
308impl<T: TangoElement + PackedElement> From<&[T]> for Value {
309 #[inline]
310 fn from(values: &[T]) -> Self {
311 Buf::from(values).into()
312 }
313}
314
315impl<T: TangoElement + PackedElement> From<Vec<T>> for Value {
316 #[inline]
317 fn from(values: Vec<T>) -> Self {
318 Buf::from(values).into()
319 }
320}
321
322impl<T: TangoElement + PackedElement> From<Box<[T]>> for Value {
323 #[inline]
324 fn from(values: Box<[T]>) -> Self {
325 Buf::from(values).into()
326 }
327}
328
329impl<T: TangoElement + PackedElement, const N: usize> From<[T; N]> for Value {
330 #[inline]
331 fn from(values: [T; N]) -> Self {
332 Buf::from(values).into()
333 }
334}
335
336impl From<&str> for Value {
337 #[inline]
338 fn from(value: &str) -> Self {
339 Buf::<str, 0>::from(value).into()
340 }
341}
342
343impl From<String> for Value {
344 #[inline]
345 fn from(value: String) -> Self {
346 Self::from(value.as_str())
347 }
348}
349
350impl<const N: usize> From<[&str; N]> for Value {
351 #[inline]
352 fn from(values: [&str; N]) -> Self {
353 Buf::<str, 1>::from(&values[..]).into()
354 }
355}
356
357impl From<&[&str]> for Value {
358 #[inline]
359 fn from(values: &[&str]) -> Self {
360 Buf::<str, 1>::from(values).into()
361 }
362}
363
364impl From<Vec<String>> for Value {
365 #[inline]
366 fn from(values: Vec<String>) -> Self {
367 Buf::<str, 1>::from(values.as_slice()).into()
368 }
369}
370
371impl From<()> for Value {
372 #[inline]
373 fn from((): ()) -> Self {
374 Self::Void
375 }
376}
377
378impl From<(Buf<i32, 1>, Buf<str, 1>)> for Value {
379 #[inline]
380 fn from((longs, strings): (Buf<i32, 1>, Buf<str, 1>)) -> Self {
381 Self::LongString(longs, strings)
382 }
383}
384
385impl From<(Buf<f64, 1>, Buf<str, 1>)> for Value {
386 #[inline]
387 fn from((doubles, strings): (Buf<f64, 1>, Buf<str, 1>)) -> Self {
388 Self::DoubleString(doubles, strings)
389 }
390}
391
392impl From<DevEncoded> for Value {
393 #[inline]
394 fn from(encoded: DevEncoded) -> Self {
395 Self::Encoded(encoded)
396 }
397}
398
399macro_rules! impl_scalar_try_from {
400 ($($ty:ty),*) => { $(
401 impl TryFrom<Value> for $ty {
402 type Error = Error;
403 #[inline]
404 fn try_from(data: Value) -> Result<Self, Error> {
405 Self::try_from(&data)
406 }
407 }
408
409 impl TryFrom<&Value> for $ty {
410 type Error = Error;
411 #[inline]
412 fn try_from(data: &Value) -> Result<Self, Error> {
413 <&Buf<$ty, 0>>::try_from(data).map(Buf::value)
414 }
415 }
416 )* };
417}
418
419impl_scalar_try_from!(bool, u8, u16, i16, u32, i32, u64, i64, f32, f64, DevEnum, DevState);
420
421impl<T: TangoElement + PackedElement> TryFrom<Value> for Vec<T> {
422 type Error = Error;
423 #[inline]
424 fn try_from(data: Value) -> Result<Self, Error> {
425 <&[T]>::try_from(&data).map(<[T]>::to_vec)
426 }
427}
428
429impl<T: TangoElement + PackedElement> TryFrom<Value> for Box<[T]> {
430 type Error = Error;
431 #[inline]
432 fn try_from(data: Value) -> Result<Self, Error> {
433 <&[T]>::try_from(&data).map(Box::from)
434 }
435}
436
437impl<T: TangoElement + PackedElement, const N: usize> TryFrom<Value> for [T; N] {
438 type Error = Error;
439 fn try_from(data: Value) -> Result<Self, Error> {
440 let values = <&[T]>::try_from(&data)?;
441 values.try_into().map_err(|_| Error::new(ErrorKind::InvalidData)
442 .with_description(format!("Spectrum holds {} values where {N} were expected", values.len())))
443 }
444}
445
446impl<'a, T: TangoElement + PackedElement> TryFrom<&'a Value> for &'a [T] {
447 type Error = Error;
448 #[inline]
449 fn try_from(data: &'a Value) -> Result<Self, Error> {
450 <&Buf<T, 1>>::try_from(data).map(Buf::as_slice)
451 }
452}
453
454impl TryFrom<Value> for String {
455 type Error = Error;
456 #[inline]
457 fn try_from(data: Value) -> Result<Self, Error> {
458 <&str>::try_from(&data).map(str::to_string)
459 }
460}
461
462impl<'a> TryFrom<&'a Value> for &'a str {
463 type Error = Error;
464 #[inline]
465 fn try_from(data: &'a Value) -> Result<Self, Error> {
466 <&Buf<str, 0>>::try_from(data).map(Buf::value)
467 }
468}
469
470impl TryFrom<Value> for Vec<String> {
471 type Error = Error;
472 #[inline]
473 fn try_from(data: Value) -> Result<Self, Error> {
474 <&Buf<str, 1>>::try_from(&data).map(|strings| strings.iter().map(str::to_string).collect())
475 }
476}
477
478impl TryFrom<Value> for () {
479 type Error = Error;
480 #[inline]
481 fn try_from(data: Value) -> Result<Self, Error> {
482 match data {
483 Value::Void => Ok(()),
484 data => Err(Error::new(ErrorKind::InvalidData)
485 .with_description("Value is not void")
486 .with_append_description(format_args!("Got: {data:?}"))),
487 }
488 }
489}
490
491impl TryFrom<Value> for (Buf<i32, 1>, Buf<str, 1>) {
492 type Error = Error;
493 #[inline]
494 fn try_from(data: Value) -> Result<Self, Error> {
495 match data {
496 Value::LongString(longs, strings) => Ok((longs, strings)),
497 data => Err(Error::new(ErrorKind::InvalidData)
498 .with_description("Value is not a long string array")
499 .with_append_description(format_args!("Got: {data:?}"))),
500 }
501 }
502}
503
504impl TryFrom<Value> for (Buf<f64, 1>, Buf<str, 1>) {
505 type Error = Error;
506 #[inline]
507 fn try_from(data: Value) -> Result<Self, Error> {
508 match data {
509 Value::DoubleString(doubles, strings) => Ok((doubles, strings)),
510 data => Err(Error::new(ErrorKind::InvalidData)
511 .with_description("Value is not a double string array")
512 .with_append_description(format_args!("Got: {data:?}"))),
513 }
514 }
515}
516
517impl TryFrom<Value> for DevEncoded {
518 type Error = Error;
519 #[inline]
520 fn try_from(data: Value) -> Result<Self, Error> {
521 match data {
522 Value::Encoded(encoded) => Ok(encoded),
523 data => Err(Error::new(ErrorKind::InvalidData)
524 .with_description("Value is not encoded")
525 .with_append_description(format_args!("Got: {data:?}"))),
526 }
527 }
528}
529
530impl<'a> TryFrom<&'a Value> for &'a DevEncoded {
531 type Error = Error;
532 #[inline]
533 fn try_from(data: &'a Value) -> Result<Self, Error> {
534 match data {
535 Value::Encoded(encoded) => Ok(encoded),
536 data => Err(Error::new(ErrorKind::InvalidData)
537 .with_description("Value is not encoded")
538 .with_append_description(format_args!("Got: {data:?}"))),
539 }
540 }
541}
542
543#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
550pub enum CommandDataKind {
551 Void,
552 Bool,
553 Octet,
554 UShort,
555 Short,
556 ULong,
557 Long,
558 ULong64,
559 Long64,
560 Float,
561 Double,
562 State,
563 String,
564 BoolArray,
565 OctetArray,
566 UShortArray,
567 ShortArray,
568 ULongArray,
569 LongArray,
570 ULong64Array,
571 Long64Array,
572 FloatArray,
573 DoubleArray,
574 StateArray,
575 StringArray,
576 LongStringArray,
577 DoubleStringArray,
578 Encoded,
579}
580
581#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
583pub enum AttrDataKind {
584 Bool,
585 Octet,
586 UShort,
587 Short,
588 ULong,
589 Long,
590 ULong64,
591 Long64,
592 Float,
593 Double,
594 State,
595 String,
596 Enum,
600 Encoded,
602}
603
604#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
609pub enum AttrDataFormat {
610 Scalar,
611 Spectrum(usize),
612 Image(usize, usize),
613}
614
615pub trait AttrData {
617 const DATA_KIND: AttrDataKind;
618 const DATA_FORMAT: AttrDataFormat;
619}
620
621pub trait CommandData {
623 const DATA_KIND: CommandDataKind;
624}
625
626impl<T: TangoElement + ?Sized, const N: usize> AttrData for Buf<T, N> {
627 const DATA_KIND: AttrDataKind = T::ATTR_KIND;
628 const DATA_FORMAT: AttrDataFormat = match N {
629 0 => AttrDataFormat::Scalar,
630 1 => AttrDataFormat::Spectrum(0),
631 2 => AttrDataFormat::Image(0, 0),
632 _ => panic!("an attribute has at most two dimensions"),
633 };
634}
635
636impl<T: TangoElement + ?Sized, const N: usize> CommandData for Buf<T, N> {
637 const DATA_KIND: CommandDataKind = match (N, T::CMD_SCALAR_KIND, T::CMD_ARRAY_KIND) {
638 (0, Some(kind), _) | (1, _, Some(kind)) => kind,
639 (0 | 1, ..) => panic!("this element type cannot be a command argument"),
640 _ => panic!("a command argument has at most one dimension"),
641 };
642}
643
644macro_rules! impl_kinds_as {
646 ($({$($generics:tt)*} $ty:ty => $data:ty;)*) => { $(
647 impl<$($generics)*> AttrData for $ty {
648 const DATA_KIND: AttrDataKind = <$data as AttrData>::DATA_KIND;
649 const DATA_FORMAT: AttrDataFormat = <$data as AttrData>::DATA_FORMAT;
650 }
651
652 impl<$($generics)*> CommandData for $ty {
653 const DATA_KIND: CommandDataKind = <$data as CommandData>::DATA_KIND;
654 }
655 )* };
656}
657
658impl_kinds_as! {
659 {T: TangoElement + PackedElement} T => Buf<T, 0>;
660 {T: TangoElement + PackedElement} Vec<T> => Buf<T, 1>;
661 {T: TangoElement + PackedElement} Box<[T]> => Buf<T, 1>;
662 {T: TangoElement + PackedElement, const N: usize} [T; N] => Buf<T, 1>;
663 {'a, T: TangoElement + PackedElement} &'a [T] => Buf<T, 1>;
664 {} String => Buf<str, 0>;
665 {'a} &'a str => Buf<str, 0>;
666 {} Vec<String> => Buf<str, 1>;
667 {'a, 'b} &'a [&'b str] => Buf<str, 1>;
668}
669
670impl CommandData for () {
671 const DATA_KIND: CommandDataKind = CommandDataKind::Void;
672}
673
674impl CommandData for (Buf<i32, 1>, Buf<str, 1>) {
675 const DATA_KIND: CommandDataKind = CommandDataKind::LongStringArray;
676}
677
678impl CommandData for (Buf<f64, 1>, Buf<str, 1>) {
679 const DATA_KIND: CommandDataKind = CommandDataKind::DoubleStringArray;
680}
681
682impl AttrData for DevEncoded {
683 const DATA_KIND: AttrDataKind = AttrDataKind::Encoded;
684 const DATA_FORMAT: AttrDataFormat = AttrDataFormat::Scalar;
685}
686
687impl CommandData for DevEncoded {
688 const DATA_KIND: CommandDataKind = CommandDataKind::Encoded;
689}
690
691
692impl DevEncoded {
697 pub const JPEG_RGB: &'static str = "JPEG_RGB";
699 pub const JPEG_GRAY8: &'static str = "JPEG_GRAY8";
701 pub const GRAY8: &'static str = "GRAY8";
703 pub const GRAY16: &'static str = "GRAY16";
705 pub const RGB24: &'static str = "RGB24";
707}
708
709#[derive(Debug, Clone, PartialEq)]
716pub enum EncodedPixels {
717 Gray8(Image<u8>),
719 Gray16(Image<u16>),
721 Rgb24(Buf<u8, 3>),
724}
725
726impl DevEncoded {
727
728 pub fn to_pixels(&self) -> Result<EncodedPixels, Error> {
731
732 let format = self.format.value();
733 let channels = match format {
734 Self::GRAY8 => 1,
735 Self::GRAY16 => 2,
736 Self::RGB24 => 3,
737 format => return Err(Error::new(ErrorKind::InvalidData)
738 .with_description(format!("Encoded format {format:?} is not a raw image format"))),
739 };
740
741 let octets = self.data.as_slice();
742 let [w_hi, w_lo, h_hi, h_lo, ..] = *octets else {
743 return Err(Error::new(ErrorKind::InvalidData)
744 .with_description(format!("Encoded {format} image has no dimensions")));
745 };
746
747 let width = u16::from_be_bytes([w_hi, w_lo]) as usize;
748 let height = u16::from_be_bytes([h_hi, h_lo]) as usize;
749 if octets.len() - 4 != width * height * channels {
750 return Err(Error::new(ErrorKind::InvalidData)
751 .with_description(format!("Encoded {format} image of {width}x{height} pixels holds {} octets where {} were expected",
752 octets.len() - 4, width * height * channels)));
753 }
754
755 let pixels = self.data.slice(4..);
756 Ok(match channels {
757 1 => EncodedPixels::Gray8(pixels.reshape([width, height]).expect("as many pixels as the dimensions")),
758 2 => {
759 let values = pixels.as_slice().as_chunks::<2>().0.iter()
760 .map(|pair| u16::from_be_bytes([pair[0], pair[1]]))
761 .collect::<Vec<_>>();
762 EncodedPixels::Gray16(Image::from_slice(&values, [width, height]).expect("as many pixels as the dimensions"))
763 }
764 _ => EncodedPixels::Rgb24(pixels.reshape([3, width, height]).expect("as many pixels as the dimensions")),
765 })
766
767 }
768
769 pub fn from_pixels(pixels: &EncodedPixels) -> Result<Self, Error> {
772
773 let (format, [width, height]) = match pixels {
774 EncodedPixels::Gray8(image) => (Self::GRAY8, image.dim()),
775 EncodedPixels::Gray16(image) => (Self::GRAY16, image.dim()),
776 EncodedPixels::Rgb24(image) => {
777 let [_, width, height] = image.dim();
778 (Self::RGB24, [width, height])
779 }
780 };
781
782 let (Ok(width), Ok(height)) = (u16::try_from(width), u16::try_from(height)) else {
783 return Err(Error::new(ErrorKind::InvalidData)
784 .with_description(format!("Image of {width}x{height} pixels is too large to be encoded")));
785 };
786
787 let mut data = Vec::new();
788 data.extend_from_slice(&width.to_be_bytes());
789 data.extend_from_slice(&height.to_be_bytes());
790 match pixels {
791 EncodedPixels::Gray8(image) => data.extend_from_slice(image.as_slice()),
792 EncodedPixels::Gray16(image) => data.extend(image.as_slice().iter().flat_map(|pixel| pixel.to_be_bytes())),
793 EncodedPixels::Rgb24(image) => data.extend_from_slice(image.as_slice()),
794 }
795
796 Ok(Self::new(format, data))
797
798 }
799
800 #[cfg(feature = "image")]
802 pub fn to_image(&self) -> Result<image::DynamicImage, Error> {
803
804 use image::{DynamicImage, ImageBuffer, ImageFormat};
805
806 fn image_err(err: image::ImageError) -> Error {
807 Error::new(ErrorKind::InvalidData)
808 .with_description("Failed to decode encoded image")
809 .with_source(Box::new(err))
810 }
811
812 let format = self.format.value();
813 if format == Self::JPEG_RGB || format == Self::JPEG_GRAY8 {
814 return image::load_from_memory_with_format(self.data.as_slice(), ImageFormat::Jpeg).map_err(image_err);
815 }
816
817 Ok(match self.to_pixels()? {
818 EncodedPixels::Gray8(pixels) => {
819 let [width, height] = pixels.dim();
820 DynamicImage::ImageLuma8(ImageBuffer::from_raw(width as u32, height as u32, pixels.as_slice().to_vec()).expect("as many pixels as the dimensions"))
821 }
822 EncodedPixels::Gray16(pixels) => {
823 let [width, height] = pixels.dim();
824 DynamicImage::ImageLuma16(ImageBuffer::from_raw(width as u32, height as u32, pixels.as_slice().to_vec()).expect("as many pixels as the dimensions"))
825 }
826 EncodedPixels::Rgb24(pixels) => {
827 let [_, width, height] = pixels.dim();
828 DynamicImage::ImageRgb8(ImageBuffer::from_raw(width as u32, height as u32, pixels.as_slice().to_vec()).expect("as many pixels as the dimensions"))
829 }
830 })
831
832 }
833
834 #[cfg(feature = "image")]
837 pub fn from_image(image: &image::DynamicImage) -> Result<Self, Error> {
838
839 use image::DynamicImage;
840
841 let dim = [image.width() as usize, image.height() as usize];
842 let pixels = match image {
843 DynamicImage::ImageLuma8(gray) => EncodedPixels::Gray8(Image::from_slice(gray.as_raw(), dim).expect("as many pixels as the dimensions")),
844 DynamicImage::ImageLuma16(gray) => EncodedPixels::Gray16(Image::from_slice(gray.as_raw(), dim).expect("as many pixels as the dimensions")),
845 image => EncodedPixels::Rgb24(Buf::from_slice(image.to_rgb8().as_raw(), [3, dim[0], dim[1]]).expect("as many pixels as the dimensions")),
846 };
847
848 Self::from_pixels(&pixels)
849
850 }
851
852}
853
854
855#[cfg(test)]
856mod tests {
857
858 use super::*;
859
860 #[test]
863 fn value() {
864 let spectrum = Buf::<f64, 1>::from(vec![1.0, 2.0]);
865 let data = Value::from(spectrum.clone());
866 assert!(!spectrum.is_unique());
867 assert_eq!(Buf::<f64, 1>::try_from(data).unwrap().as_slice(), &[1.0, 2.0]);
868
869 let values: Vec<f64> = Value::from(vec![1.0, 2.0]).try_into().unwrap();
870 assert_eq!(values, [1.0, 2.0]);
871 assert_eq!(i32::try_from(Value::from(3)).unwrap(), 3);
872 assert_eq!(String::try_from(Value::from("text")).unwrap(), "text");
873 assert_eq!(Vec::<String>::try_from(Value::from(&["a", "b"][..])).unwrap(), ["a", "b"]);
874 assert!(<()>::try_from(Value::from(())).is_ok());
875 assert_eq!(<[u8; 2]>::try_from(Value::from([1u8, 2])).unwrap(), [1, 2]);
876 let encoded = DevEncoded::new("RAW", vec![1u8, 2]);
877 assert_eq!(DevEncoded::try_from(Value::from(encoded.clone())).unwrap(), encoded);
878 assert!(DevEncoded::try_from(Value::from(vec![1u8, 2])).is_err());
879 assert!(<[u8; 3]>::try_from(Value::from([1u8, 2])).is_err());
880 }
881
882 #[test]
884 fn value_borrowed() {
885 let data = Value::from(vec![1u16, 2]);
886 assert_eq!(<&[u16]>::try_from(&data).unwrap(), &[1, 2]);
887 let data = Value::from("text");
888 assert_eq!(<&str>::try_from(&data).unwrap(), "text");
889 let data = Value::from(Buf::<u8, 2>::from_slice(&[1, 2, 3, 4], [2, 2]).unwrap());
890 assert_eq!(<&Buf<u8, 2>>::try_from(&data).unwrap().get([1, 1]), Some(4));
891 }
892
893 #[test]
896 fn value_mismatch() {
897 let err = Buf::<i32, 1>::try_from(Value::from(1.5)).unwrap_err();
898 assert_eq!(err.description(), "Value is not a Long spectrum\nGot: Double(Buf(1.5))");
899 assert!(f64::try_from(Value::from(vec![1.5])).is_err());
900 assert!(DevEnum::try_from(Value::from(1i16)).is_err());
902 }
903
904 #[test]
906 fn kinds() {
907 assert_eq!(<Buf<f64, 2> as AttrData>::DATA_FORMAT, AttrDataFormat::Image(0, 0));
908 assert_eq!(<Vec<f64> as AttrData>::DATA_FORMAT, AttrDataFormat::Spectrum(0));
909 assert_eq!(<Vec<f64> as CommandData>::DATA_KIND, CommandDataKind::DoubleArray);
910 assert_eq!(<&str as CommandData>::DATA_KIND, CommandDataKind::String);
911 assert_eq!(<DevEnum as AttrData>::DATA_KIND, AttrDataKind::Enum);
912 assert_eq!(<() as CommandData>::DATA_KIND, CommandDataKind::Void);
913 assert_eq!(<(Buf<i32, 1>, Buf<str, 1>) as CommandData>::DATA_KIND, CommandDataKind::LongStringArray);
914 assert_eq!(<DevEncoded as AttrData>::DATA_KIND, AttrDataKind::Encoded);
915 assert_eq!(<DevEncoded as AttrData>::DATA_FORMAT, AttrDataFormat::Scalar);
916 assert_eq!(<DevEncoded as CommandData>::DATA_KIND, CommandDataKind::Encoded);
917 }
918
919 #[test]
922 fn value_equality() {
923 let spectrum = Buf::<i32, 1>::from([1, 2, 3, 4]);
924 assert_eq!(Value::from(spectrum), Value::from(vec![1, 2, 3, 4]));
925 assert_ne!(Value::from(1i16), Value::from(DevEnum(1)));
926 }
927
928 #[test]
931 fn encoded_pixels() {
932
933 let gray8 = EncodedPixels::Gray8(Image::from_slice(&[1u8, 2, 3, 4, 5, 6], [3, 2]).unwrap());
934 let gray16 = EncodedPixels::Gray16(Image::from_slice(&[0x0102u16, 0xFFFE], [1, 2]).unwrap());
935 let rgb24 = EncodedPixels::Rgb24(Buf::from_slice(&[1u8, 2, 3, 4, 5, 6], [3, 2, 1]).unwrap());
936
937 for pixels in [gray8, gray16, rgb24] {
938 let encoded = DevEncoded::from_pixels(&pixels).unwrap();
939 assert_eq!(encoded.to_pixels().unwrap(), pixels);
940 }
941
942 let encoded = DevEncoded::from_pixels(&EncodedPixels::Gray16(Image::from_slice(&[0x0102u16], [1, 1]).unwrap())).unwrap();
943 assert_eq!(encoded.format.value(), DevEncoded::GRAY16);
944 assert_eq!(encoded.data.as_slice(), &[0, 1, 0, 1, 0x01, 0x02], "dimensions and pixels are big endian");
945
946 let encoded = DevEncoded::new(DevEncoded::GRAY8, vec![0u8, 2, 0, 1, 7, 8]);
947 let EncodedPixels::Gray8(image) = encoded.to_pixels().unwrap() else { panic!("not a GRAY8 image") };
948 assert_eq!(image.get([1, 0]), Some(8));
949 assert!(!encoded.data.is_unique(), "the pixels were copied out of the value");
950
951 }
952
953 #[test]
956 fn encoded_pixels_invalid() {
957 assert!(DevEncoded::new(DevEncoded::GRAY8, vec![0u8, 2, 0, 2, 1]).to_pixels().is_err());
958 assert!(DevEncoded::new(DevEncoded::RGB24, vec![0u8, 1]).to_pixels().is_err());
959 assert!(DevEncoded::new(DevEncoded::JPEG_RGB, vec![0xFFu8, 0xD8]).to_pixels().is_err());
960 let wide = EncodedPixels::Gray8(Image::from_slice(&vec![0u8; 70000], [70000, 1]).unwrap());
961 assert!(DevEncoded::from_pixels(&wide).is_err());
962 }
963
964 #[cfg(feature = "image")]
967 #[test]
968 fn encoded_image() {
969
970 use image::{DynamicImage, GrayImage, ImageBuffer, ImageFormat, Luma, RgbImage};
971
972 let gray8 = DynamicImage::ImageLuma8(GrayImage::from_raw(2, 1, vec![10, 20]).unwrap());
973 let gray16 = DynamicImage::ImageLuma16(ImageBuffer::<Luma<u16>, _>::from_raw(1, 2, vec![1000, 2000]).unwrap());
974 let rgb8 = DynamicImage::ImageRgb8(RgbImage::from_raw(1, 1, vec![1, 2, 3]).unwrap());
975
976 for image in [gray8, gray16, rgb8] {
977 assert_eq!(DevEncoded::from_image(&image).unwrap().to_image().unwrap(), image);
978 }
979
980 let mut jpeg = Vec::new();
981 DynamicImage::ImageRgb8(RgbImage::from_pixel(8, 4, [200, 100, 50].into()))
982 .write_to(&mut std::io::Cursor::new(&mut jpeg), ImageFormat::Jpeg)
983 .unwrap();
984 let image = DevEncoded::new(DevEncoded::JPEG_RGB, jpeg).to_image().unwrap();
985 assert_eq!((image.width(), image.height()), (8, 4));
986
987 }
988
989}