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!("Expected a {:?} {}, got {data:?}", T::ATTR_KIND, stringify!($get))))
277 }
278 }
279
280 impl<'a, T: TangoElement + ?Sized> TryFrom<&'a Value> for &'a $variant<T> {
281 type Error = Error;
282 fn try_from(data: &'a Value) -> Result<Self, Error> {
283 T::unwrap_ref(data)
284 .and_then(ValueFormat::$get)
285 .ok_or_else(|| Error::new(ErrorKind::InvalidData)
286 .with_description(format!("Expected a {:?} {}, got {data:?}", T::ATTR_KIND, stringify!($get))))
287 }
288 }
289 )*
290 };
291}
292
293impl_data_conversions! {
294 Scalar: scalar, into_scalar;
295 Spectrum: spectrum, into_spectrum;
296 Image: image, into_image;
297}
298
299impl<T: TangoElement + PackedElement> From<T> for Value {
300 #[inline]
301 fn from(value: T) -> Self {
302 Buf::<T, 0>::from(value).into()
303 }
304}
305
306impl<T: TangoElement + PackedElement> From<&[T]> for Value {
307 #[inline]
308 fn from(values: &[T]) -> Self {
309 Buf::from(values).into()
310 }
311}
312
313impl<T: TangoElement + PackedElement> From<Vec<T>> for Value {
314 #[inline]
315 fn from(values: Vec<T>) -> Self {
316 Buf::from(values).into()
317 }
318}
319
320impl<T: TangoElement + PackedElement> From<Box<[T]>> for Value {
321 #[inline]
322 fn from(values: Box<[T]>) -> Self {
323 Buf::from(values).into()
324 }
325}
326
327impl<T: TangoElement + PackedElement, const N: usize> From<[T; N]> for Value {
328 #[inline]
329 fn from(values: [T; N]) -> Self {
330 Buf::from(values).into()
331 }
332}
333
334impl From<&str> for Value {
335 #[inline]
336 fn from(value: &str) -> Self {
337 Buf::<str, 0>::from(value).into()
338 }
339}
340
341impl From<String> for Value {
342 #[inline]
343 fn from(value: String) -> Self {
344 Self::from(value.as_str())
345 }
346}
347
348impl<const N: usize> From<[&str; N]> for Value {
349 #[inline]
350 fn from(values: [&str; N]) -> Self {
351 Buf::<str, 1>::from(&values[..]).into()
352 }
353}
354
355impl From<&[&str]> for Value {
356 #[inline]
357 fn from(values: &[&str]) -> Self {
358 Buf::<str, 1>::from(values).into()
359 }
360}
361
362impl From<Vec<String>> for Value {
363 #[inline]
364 fn from(values: Vec<String>) -> Self {
365 Buf::<str, 1>::from(values.as_slice()).into()
366 }
367}
368
369impl From<()> for Value {
370 #[inline]
371 fn from((): ()) -> Self {
372 Self::Void
373 }
374}
375
376impl From<(Buf<i32, 1>, Buf<str, 1>)> for Value {
377 #[inline]
378 fn from((longs, strings): (Buf<i32, 1>, Buf<str, 1>)) -> Self {
379 Self::LongString(longs, strings)
380 }
381}
382
383impl From<(Buf<f64, 1>, Buf<str, 1>)> for Value {
384 #[inline]
385 fn from((doubles, strings): (Buf<f64, 1>, Buf<str, 1>)) -> Self {
386 Self::DoubleString(doubles, strings)
387 }
388}
389
390impl From<DevEncoded> for Value {
391 #[inline]
392 fn from(encoded: DevEncoded) -> Self {
393 Self::Encoded(encoded)
394 }
395}
396
397macro_rules! impl_scalar_try_from {
398 ($($ty:ty),*) => { $(
399 impl TryFrom<Value> for $ty {
400 type Error = Error;
401 #[inline]
402 fn try_from(data: Value) -> Result<Self, Error> {
403 Self::try_from(&data)
404 }
405 }
406
407 impl TryFrom<&Value> for $ty {
408 type Error = Error;
409 #[inline]
410 fn try_from(data: &Value) -> Result<Self, Error> {
411 <&Buf<$ty, 0>>::try_from(data).map(Buf::value)
412 }
413 }
414 )* };
415}
416
417impl_scalar_try_from!(bool, u8, u16, i16, u32, i32, u64, i64, f32, f64, DevEnum, DevState);
418
419impl<T: TangoElement + PackedElement> TryFrom<Value> for Vec<T> {
420 type Error = Error;
421 #[inline]
422 fn try_from(data: Value) -> Result<Self, Error> {
423 <&[T]>::try_from(&data).map(<[T]>::to_vec)
424 }
425}
426
427impl<T: TangoElement + PackedElement> TryFrom<Value> for Box<[T]> {
428 type Error = Error;
429 #[inline]
430 fn try_from(data: Value) -> Result<Self, Error> {
431 <&[T]>::try_from(&data).map(Box::from)
432 }
433}
434
435impl<T: TangoElement + PackedElement, const N: usize> TryFrom<Value> for [T; N] {
436 type Error = Error;
437 fn try_from(data: Value) -> Result<Self, Error> {
438 let values = <&[T]>::try_from(&data)?;
439 values.try_into().map_err(|_| Error::new(ErrorKind::InvalidData)
440 .with_description(format!("Expected a spectrum of {N} values, got {}", values.len())))
441 }
442}
443
444impl<'a, T: TangoElement + PackedElement> TryFrom<&'a Value> for &'a [T] {
445 type Error = Error;
446 #[inline]
447 fn try_from(data: &'a Value) -> Result<Self, Error> {
448 <&Buf<T, 1>>::try_from(data).map(Buf::as_slice)
449 }
450}
451
452impl TryFrom<Value> for String {
453 type Error = Error;
454 #[inline]
455 fn try_from(data: Value) -> Result<Self, Error> {
456 <&str>::try_from(&data).map(str::to_string)
457 }
458}
459
460impl<'a> TryFrom<&'a Value> for &'a str {
461 type Error = Error;
462 #[inline]
463 fn try_from(data: &'a Value) -> Result<Self, Error> {
464 <&Buf<str, 0>>::try_from(data).map(Buf::value)
465 }
466}
467
468impl TryFrom<Value> for Vec<String> {
469 type Error = Error;
470 #[inline]
471 fn try_from(data: Value) -> Result<Self, Error> {
472 <&Buf<str, 1>>::try_from(&data).map(|strings| strings.iter().map(str::to_string).collect())
473 }
474}
475
476impl TryFrom<Value> for () {
477 type Error = Error;
478 #[inline]
479 fn try_from(data: Value) -> Result<Self, Error> {
480 match data {
481 Value::Void => Ok(()),
482 data => Err(Error::new(ErrorKind::InvalidData)
483 .with_description(format!("Expected a void, got {data:?}"))),
484 }
485 }
486}
487
488impl TryFrom<Value> for (Buf<i32, 1>, Buf<str, 1>) {
489 type Error = Error;
490 #[inline]
491 fn try_from(data: Value) -> Result<Self, Error> {
492 match data {
493 Value::LongString(longs, strings) => Ok((longs, strings)),
494 data => Err(Error::new(ErrorKind::InvalidData)
495 .with_description(format!("Expected a long string array, got {data:?}"))),
496 }
497 }
498}
499
500impl TryFrom<Value> for (Buf<f64, 1>, Buf<str, 1>) {
501 type Error = Error;
502 #[inline]
503 fn try_from(data: Value) -> Result<Self, Error> {
504 match data {
505 Value::DoubleString(doubles, strings) => Ok((doubles, strings)),
506 data => Err(Error::new(ErrorKind::InvalidData)
507 .with_description(format!("Expected a double string array, got {data:?}"))),
508 }
509 }
510}
511
512impl TryFrom<Value> for DevEncoded {
513 type Error = Error;
514 #[inline]
515 fn try_from(data: Value) -> Result<Self, Error> {
516 match data {
517 Value::Encoded(encoded) => Ok(encoded),
518 data => Err(Error::new(ErrorKind::InvalidData)
519 .with_description(format!("Expected an encoded value, got {data:?}"))),
520 }
521 }
522}
523
524impl<'a> TryFrom<&'a Value> for &'a DevEncoded {
525 type Error = Error;
526 #[inline]
527 fn try_from(data: &'a Value) -> Result<Self, Error> {
528 match data {
529 Value::Encoded(encoded) => Ok(encoded),
530 data => Err(Error::new(ErrorKind::InvalidData)
531 .with_description(format!("Expected an encoded value, got {data:?}"))),
532 }
533 }
534}
535
536#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
543pub enum CommandDataKind {
544 Void,
545 Bool,
546 Octet,
547 UShort,
548 Short,
549 ULong,
550 Long,
551 ULong64,
552 Long64,
553 Float,
554 Double,
555 State,
556 String,
557 BoolArray,
558 OctetArray,
559 UShortArray,
560 ShortArray,
561 ULongArray,
562 LongArray,
563 ULong64Array,
564 Long64Array,
565 FloatArray,
566 DoubleArray,
567 StateArray,
568 StringArray,
569 LongStringArray,
570 DoubleStringArray,
571 Encoded,
572}
573
574#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
576pub enum AttrDataKind {
577 Bool,
578 Octet,
579 UShort,
580 Short,
581 ULong,
582 Long,
583 ULong64,
584 Long64,
585 Float,
586 Double,
587 State,
588 String,
589 Enum,
593 Encoded,
595}
596
597#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
602pub enum AttrDataFormat {
603 Scalar,
604 Spectrum(usize),
605 Image(usize, usize),
606}
607
608pub trait AttrData {
610 const DATA_KIND: AttrDataKind;
611 const DATA_FORMAT: AttrDataFormat;
612}
613
614pub trait CommandData {
616 const DATA_KIND: CommandDataKind;
617}
618
619impl<T: TangoElement + ?Sized, const N: usize> AttrData for Buf<T, N> {
620 const DATA_KIND: AttrDataKind = T::ATTR_KIND;
621 const DATA_FORMAT: AttrDataFormat = match N {
622 0 => AttrDataFormat::Scalar,
623 1 => AttrDataFormat::Spectrum(0),
624 2 => AttrDataFormat::Image(0, 0),
625 _ => panic!("an attribute has at most two dimensions"),
626 };
627}
628
629impl<T: TangoElement + ?Sized, const N: usize> CommandData for Buf<T, N> {
630 const DATA_KIND: CommandDataKind = match (N, T::CMD_SCALAR_KIND, T::CMD_ARRAY_KIND) {
631 (0, Some(kind), _) | (1, _, Some(kind)) => kind,
632 (0 | 1, ..) => panic!("this element type cannot be a command argument"),
633 _ => panic!("a command argument has at most one dimension"),
634 };
635}
636
637macro_rules! impl_kinds_as {
639 ($({$($generics:tt)*} $ty:ty => $data:ty;)*) => { $(
640 impl<$($generics)*> AttrData for $ty {
641 const DATA_KIND: AttrDataKind = <$data as AttrData>::DATA_KIND;
642 const DATA_FORMAT: AttrDataFormat = <$data as AttrData>::DATA_FORMAT;
643 }
644
645 impl<$($generics)*> CommandData for $ty {
646 const DATA_KIND: CommandDataKind = <$data as CommandData>::DATA_KIND;
647 }
648 )* };
649}
650
651impl_kinds_as! {
652 {T: TangoElement + PackedElement} T => Buf<T, 0>;
653 {T: TangoElement + PackedElement} Vec<T> => Buf<T, 1>;
654 {T: TangoElement + PackedElement} Box<[T]> => Buf<T, 1>;
655 {T: TangoElement + PackedElement, const N: usize} [T; N] => Buf<T, 1>;
656 {'a, T: TangoElement + PackedElement} &'a [T] => Buf<T, 1>;
657 {} String => Buf<str, 0>;
658 {'a} &'a str => Buf<str, 0>;
659 {} Vec<String> => Buf<str, 1>;
660 {'a, 'b} &'a [&'b str] => Buf<str, 1>;
661}
662
663impl CommandData for () {
664 const DATA_KIND: CommandDataKind = CommandDataKind::Void;
665}
666
667impl CommandData for (Buf<i32, 1>, Buf<str, 1>) {
668 const DATA_KIND: CommandDataKind = CommandDataKind::LongStringArray;
669}
670
671impl CommandData for (Buf<f64, 1>, Buf<str, 1>) {
672 const DATA_KIND: CommandDataKind = CommandDataKind::DoubleStringArray;
673}
674
675impl AttrData for DevEncoded {
676 const DATA_KIND: AttrDataKind = AttrDataKind::Encoded;
677 const DATA_FORMAT: AttrDataFormat = AttrDataFormat::Scalar;
678}
679
680impl CommandData for DevEncoded {
681 const DATA_KIND: CommandDataKind = CommandDataKind::Encoded;
682}
683
684
685impl DevEncoded {
690 pub const JPEG_RGB: &'static str = "JPEG_RGB";
692 pub const JPEG_GRAY8: &'static str = "JPEG_GRAY8";
694 pub const GRAY8: &'static str = "GRAY8";
696 pub const GRAY16: &'static str = "GRAY16";
698 pub const RGB24: &'static str = "RGB24";
700}
701
702#[derive(Debug, Clone, PartialEq)]
709pub enum EncodedPixels {
710 Gray8(Image<u8>),
712 Gray16(Image<u16>),
714 Rgb24(Buf<u8, 3>),
717}
718
719impl DevEncoded {
720
721 pub fn to_pixels(&self) -> Result<EncodedPixels, Error> {
724
725 let format = self.format.value();
726 let channels = match format {
727 Self::GRAY8 => 1,
728 Self::GRAY16 => 2,
729 Self::RGB24 => 3,
730 format => return Err(Error::new(ErrorKind::InvalidData)
731 .with_description(format!("Not an encoded image in a raw format: {format:?}"))),
732 };
733
734 let octets = self.data.as_slice();
735 let [w_hi, w_lo, h_hi, h_lo, ..] = *octets else {
736 return Err(Error::new(ErrorKind::InvalidData)
737 .with_description(format!("The {format} image has no dimensions")));
738 };
739
740 let width = u16::from_be_bytes([w_hi, w_lo]) as usize;
741 let height = u16::from_be_bytes([h_hi, h_lo]) as usize;
742 if octets.len() - 4 != width * height * channels {
743 return Err(Error::new(ErrorKind::InvalidData)
744 .with_description(format!("The {format} image of {width}x{height} pixels holds {} octets", octets.len() - 4)));
745 }
746
747 let pixels = self.data.slice(4..);
748 Ok(match channels {
749 1 => EncodedPixels::Gray8(pixels.reshape([width, height]).expect("as many pixels as the dimensions")),
750 2 => {
751 let values = pixels.as_slice().chunks_exact(2)
752 .map(|pair| u16::from_be_bytes([pair[0], pair[1]]))
753 .collect::<Vec<_>>();
754 EncodedPixels::Gray16(Image::from_slice(&values, [width, height]).expect("as many pixels as the dimensions"))
755 }
756 _ => EncodedPixels::Rgb24(pixels.reshape([3, width, height]).expect("as many pixels as the dimensions")),
757 })
758
759 }
760
761 pub fn from_pixels(pixels: &EncodedPixels) -> Result<Self, Error> {
764
765 let (format, [width, height]) = match pixels {
766 EncodedPixels::Gray8(image) => (Self::GRAY8, image.dim()),
767 EncodedPixels::Gray16(image) => (Self::GRAY16, image.dim()),
768 EncodedPixels::Rgb24(image) => {
769 let [_, width, height] = image.dim();
770 (Self::RGB24, [width, height])
771 }
772 };
773
774 let (Ok(width), Ok(height)) = (u16::try_from(width), u16::try_from(height)) else {
775 return Err(Error::new(ErrorKind::InvalidData)
776 .with_description(format!("A {width}x{height} image is too large to be encoded")));
777 };
778
779 let mut data = Vec::new();
780 data.extend_from_slice(&width.to_be_bytes());
781 data.extend_from_slice(&height.to_be_bytes());
782 match pixels {
783 EncodedPixels::Gray8(image) => data.extend_from_slice(image.as_slice()),
784 EncodedPixels::Gray16(image) => data.extend(image.as_slice().iter().flat_map(|pixel| pixel.to_be_bytes())),
785 EncodedPixels::Rgb24(image) => data.extend_from_slice(image.as_slice()),
786 }
787
788 Ok(Self::new(format, data))
789
790 }
791
792 #[cfg(feature = "image")]
794 pub fn to_image(&self) -> Result<image::DynamicImage, Error> {
795
796 use image::{DynamicImage, ImageBuffer, ImageFormat};
797
798 fn image_err(err: image::ImageError) -> Error {
799 Error::new(ErrorKind::InvalidData)
800 .with_description("The encoded image could not be decoded")
801 .with_source(Box::new(err))
802 }
803
804 let format = self.format.value();
805 if format == Self::JPEG_RGB || format == Self::JPEG_GRAY8 {
806 return image::load_from_memory_with_format(self.data.as_slice(), ImageFormat::Jpeg).map_err(image_err);
807 }
808
809 Ok(match self.to_pixels()? {
810 EncodedPixels::Gray8(pixels) => {
811 let [width, height] = pixels.dim();
812 DynamicImage::ImageLuma8(ImageBuffer::from_raw(width as u32, height as u32, pixels.as_slice().to_vec()).expect("as many pixels as the dimensions"))
813 }
814 EncodedPixels::Gray16(pixels) => {
815 let [width, height] = pixels.dim();
816 DynamicImage::ImageLuma16(ImageBuffer::from_raw(width as u32, height as u32, pixels.as_slice().to_vec()).expect("as many pixels as the dimensions"))
817 }
818 EncodedPixels::Rgb24(pixels) => {
819 let [_, width, height] = pixels.dim();
820 DynamicImage::ImageRgb8(ImageBuffer::from_raw(width as u32, height as u32, pixels.as_slice().to_vec()).expect("as many pixels as the dimensions"))
821 }
822 })
823
824 }
825
826 #[cfg(feature = "image")]
829 pub fn from_image(image: &image::DynamicImage) -> Result<Self, Error> {
830
831 use image::DynamicImage;
832
833 let dim = [image.width() as usize, image.height() as usize];
834 let pixels = match image {
835 DynamicImage::ImageLuma8(gray) => EncodedPixels::Gray8(Image::from_slice(gray.as_raw(), dim).expect("as many pixels as the dimensions")),
836 DynamicImage::ImageLuma16(gray) => EncodedPixels::Gray16(Image::from_slice(gray.as_raw(), dim).expect("as many pixels as the dimensions")),
837 image => EncodedPixels::Rgb24(Buf::from_slice(image.to_rgb8().as_raw(), [3, dim[0], dim[1]]).expect("as many pixels as the dimensions")),
838 };
839
840 Self::from_pixels(&pixels)
841
842 }
843
844}
845
846
847#[cfg(test)]
848mod tests {
849
850 use super::*;
851
852 #[test]
855 fn value() {
856 let spectrum = Buf::<f64, 1>::from(vec![1.0, 2.0]);
857 let data = Value::from(spectrum.clone());
858 assert!(!spectrum.is_unique());
859 assert_eq!(Buf::<f64, 1>::try_from(data).unwrap().as_slice(), &[1.0, 2.0]);
860
861 let values: Vec<f64> = Value::from(vec![1.0, 2.0]).try_into().unwrap();
862 assert_eq!(values, [1.0, 2.0]);
863 assert_eq!(i32::try_from(Value::from(3)).unwrap(), 3);
864 assert_eq!(String::try_from(Value::from("text")).unwrap(), "text");
865 assert_eq!(Vec::<String>::try_from(Value::from(&["a", "b"][..])).unwrap(), ["a", "b"]);
866 assert!(<()>::try_from(Value::from(())).is_ok());
867 assert_eq!(<[u8; 2]>::try_from(Value::from([1u8, 2])).unwrap(), [1, 2]);
868 let encoded = DevEncoded::new("RAW", vec![1u8, 2]);
869 assert_eq!(DevEncoded::try_from(Value::from(encoded.clone())).unwrap(), encoded);
870 assert!(DevEncoded::try_from(Value::from(vec![1u8, 2])).is_err());
871 assert!(<[u8; 3]>::try_from(Value::from([1u8, 2])).is_err());
872 }
873
874 #[test]
876 fn value_borrowed() {
877 let data = Value::from(vec![1u16, 2]);
878 assert_eq!(<&[u16]>::try_from(&data).unwrap(), &[1, 2]);
879 let data = Value::from("text");
880 assert_eq!(<&str>::try_from(&data).unwrap(), "text");
881 let data = Value::from(Buf::<u8, 2>::from_slice(&[1, 2, 3, 4], [2, 2]).unwrap());
882 assert_eq!(<&Buf<u8, 2>>::try_from(&data).unwrap().get([1, 1]), Some(4));
883 }
884
885 #[test]
888 fn value_mismatch() {
889 let err = Buf::<i32, 1>::try_from(Value::from(1.5)).unwrap_err();
890 assert_eq!(err.description(), "Expected a Long spectrum, got Double(Buf(1.5))");
891 assert!(f64::try_from(Value::from(vec![1.5])).is_err());
892 assert!(DevEnum::try_from(Value::from(1i16)).is_err());
894 }
895
896 #[test]
898 fn kinds() {
899 assert_eq!(<Buf<f64, 2> as AttrData>::DATA_FORMAT, AttrDataFormat::Image(0, 0));
900 assert_eq!(<Vec<f64> as AttrData>::DATA_FORMAT, AttrDataFormat::Spectrum(0));
901 assert_eq!(<Vec<f64> as CommandData>::DATA_KIND, CommandDataKind::DoubleArray);
902 assert_eq!(<&str as CommandData>::DATA_KIND, CommandDataKind::String);
903 assert_eq!(<DevEnum as AttrData>::DATA_KIND, AttrDataKind::Enum);
904 assert_eq!(<() as CommandData>::DATA_KIND, CommandDataKind::Void);
905 assert_eq!(<(Buf<i32, 1>, Buf<str, 1>) as CommandData>::DATA_KIND, CommandDataKind::LongStringArray);
906 assert_eq!(<DevEncoded as AttrData>::DATA_KIND, AttrDataKind::Encoded);
907 assert_eq!(<DevEncoded as AttrData>::DATA_FORMAT, AttrDataFormat::Scalar);
908 assert_eq!(<DevEncoded as CommandData>::DATA_KIND, CommandDataKind::Encoded);
909 }
910
911 #[test]
914 fn value_equality() {
915 let spectrum = Buf::<i32, 1>::from([1, 2, 3, 4]);
916 assert_eq!(Value::from(spectrum), Value::from(vec![1, 2, 3, 4]));
917 assert_ne!(Value::from(1i16), Value::from(DevEnum(1)));
918 }
919
920 #[test]
923 fn encoded_pixels() {
924
925 let gray8 = EncodedPixels::Gray8(Image::from_slice(&[1u8, 2, 3, 4, 5, 6], [3, 2]).unwrap());
926 let gray16 = EncodedPixels::Gray16(Image::from_slice(&[0x0102u16, 0xFFFE], [1, 2]).unwrap());
927 let rgb24 = EncodedPixels::Rgb24(Buf::from_slice(&[1u8, 2, 3, 4, 5, 6], [3, 2, 1]).unwrap());
928
929 for pixels in [gray8, gray16, rgb24] {
930 let encoded = DevEncoded::from_pixels(&pixels).unwrap();
931 assert_eq!(encoded.to_pixels().unwrap(), pixels);
932 }
933
934 let encoded = DevEncoded::from_pixels(&EncodedPixels::Gray16(Image::from_slice(&[0x0102u16], [1, 1]).unwrap())).unwrap();
935 assert_eq!(encoded.format.value(), DevEncoded::GRAY16);
936 assert_eq!(encoded.data.as_slice(), &[0, 1, 0, 1, 0x01, 0x02], "dimensions and pixels are big endian");
937
938 let encoded = DevEncoded::new(DevEncoded::GRAY8, vec![0u8, 2, 0, 1, 7, 8]);
939 let EncodedPixels::Gray8(image) = encoded.to_pixels().unwrap() else { panic!("not a GRAY8 image") };
940 assert_eq!(image.get([1, 0]), Some(8));
941 assert!(!encoded.data.is_unique(), "the pixels were copied out of the value");
942
943 }
944
945 #[test]
948 fn encoded_pixels_invalid() {
949 assert!(DevEncoded::new(DevEncoded::GRAY8, vec![0u8, 2, 0, 2, 1]).to_pixels().is_err());
950 assert!(DevEncoded::new(DevEncoded::RGB24, vec![0u8, 1]).to_pixels().is_err());
951 assert!(DevEncoded::new(DevEncoded::JPEG_RGB, vec![0xFFu8, 0xD8]).to_pixels().is_err());
952 let wide = EncodedPixels::Gray8(Image::from_slice(&vec![0u8; 70000], [70000, 1]).unwrap());
953 assert!(DevEncoded::from_pixels(&wide).is_err());
954 }
955
956 #[cfg(feature = "image")]
959 #[test]
960 fn encoded_image() {
961
962 use image::{DynamicImage, GrayImage, ImageBuffer, ImageFormat, Luma, RgbImage};
963
964 let gray8 = DynamicImage::ImageLuma8(GrayImage::from_raw(2, 1, vec![10, 20]).unwrap());
965 let gray16 = DynamicImage::ImageLuma16(ImageBuffer::<Luma<u16>, _>::from_raw(1, 2, vec![1000, 2000]).unwrap());
966 let rgb8 = DynamicImage::ImageRgb8(RgbImage::from_raw(1, 1, vec![1, 2, 3]).unwrap());
967
968 for image in [gray8, gray16, rgb8] {
969 assert_eq!(DevEncoded::from_image(&image).unwrap().to_image().unwrap(), image);
970 }
971
972 let mut jpeg = Vec::new();
973 DynamicImage::ImageRgb8(RgbImage::from_pixel(8, 4, [200, 100, 50].into()))
974 .write_to(&mut std::io::Cursor::new(&mut jpeg), ImageFormat::Jpeg)
975 .unwrap();
976 let image = DevEncoded::new(DevEncoded::JPEG_RGB, jpeg).to_image().unwrap();
977 assert_eq!((image.width(), image.height()), (8, 4));
978
979 }
980
981}