Skip to main content

tango/
value.rs

1//! The values Tango devices exchange: a [`Value`] is a [`Buf`] of any element type and
2//! dimensions Tango has, and what the arguments and results of handlers convert from and
3//! into.
4
5use std::fmt::{self, Debug};
6
7use crate::buf::{Buf, Element, PackedElement};
8use crate::error::{Error, ErrorKind};
9use crate::dev::DevState;
10
11
12// =========================== //
13//          Tango data         //
14// =========================== //
15
16/// A single value, see [`Buf`].
17pub type Scalar<T> = Buf<T, 0>;
18/// A one-dimensional array of values, see [`Buf`].
19pub type Spectrum<T> = Buf<T, 1>;
20/// A two-dimensional array of values, in column-major order, see [`Buf`].
21pub type Image<T> = Buf<T, 2>;
22
23/// A value of an enumerated attribute: the index of its label among the labels of the
24/// attribute, see [`crate::dev::AttrConfig::enum_labels`].
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26#[repr(transparent)]
27pub struct DevEnum(pub i16);
28
29/// A value of an encoded attribute or command: octets, and the name of the format they
30/// are in, which is how a device sends what Tango has no type for, a compressed image
31/// above all. Both share the message they were decoded out of.
32///
33/// Tango only has such a value as a scalar, an attribute of it being neither a spectrum
34/// nor an image.
35#[derive(Debug, Clone, PartialEq)]
36pub struct DevEncoded {
37    /// The name of the format the octets are in, such as `JPEG_RGB`, which is only a
38    /// convention between the device and its clients.
39    pub format: Scalar<str>,
40    /// The octets themselves.
41    pub data: Spectrum<u8>,
42}
43
44impl DevEncoded {
45    /// An encoded value of that format, out of its octets.
46    #[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/// Any value a Tango device exchanges, whatever its element type and dimensions: what a
53/// backend encodes and decodes, and what the arguments and results of handlers are
54/// converted from and into, with [`From`] and [`TryFrom`].
55#[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    /// The `DevVarLongStringArray` a command may take or give.
72    LongString(Spectrum<i32>, Spectrum<str>),
73    /// The `DevVarDoubleStringArray` a command may take or give.
74    DoubleString(Spectrum<f64>, Spectrum<str>),
75    /// A [`DevEncoded`], which only exists as a scalar.
76    Encoded(DevEncoded),
77}
78
79/// A data in any of the formats Tango has, as many dimensions as it has.
80pub 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    /// The data it holds, whose dimensions already tell its format.
130    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
153/// An element type Tango has: which variant of [`Value`] holds it, and what kinds it
154/// is advertised as, see [`AttrData`] and [`CommandData`].
155pub trait TangoElement: Element {
156
157    /// The kind of an attribute of such values.
158    const ATTR_KIND: AttrDataKind;
159    /// The kind of a command argument of one such value, absent if none can be.
160    const CMD_SCALAR_KIND: Option<CommandDataKind>;
161    /// The kind of a command argument of an array of such values, absent if none can be.
162    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
219// =========================== //
220//         Conversions         //
221// =========================== //
222
223/// A type a value converts into, failing with an [`Error`] when it holds something else:
224/// every [`TryFrom<Value>`] whose error is one, [`Value`] itself included.
225///
226/// **Supporting trait**: it is only there to make this crate's API easier to use, it is
227/// implemented for everything that API accepts and is not one to implement anywhere else.
228pub 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
243/// A type that converts into a value: every [`Into<Value>`], [`Value`] itself included, see
244/// [`FromValue`] for the way back.
245///
246/// **Supporting trait**: it is only there to make this crate's API easier to use, it is
247/// implemented for everything that API accepts and is not one to implement anywhere else.
248pub 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
259/// The conversions between a value and a data of each of the formats it may hold.
260macro_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// =========================== //
537//            Kinds            //
538// =========================== //
539
540/// The data kind of a Tango command argument. This is the closed set of types accepted
541/// by `command_inout`, without any layout information (see [`CommandData`]).
542#[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/// The data kind of a Tango attribute value: an element type together with its layout.
575#[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    /// An enumerated value, which travels as the index of its own label in the list of
590    /// them: what those labels are is a property of the attribute a client may change, and
591    /// lives in its configuration, see [`crate::dev::AttrConfig::enum_labels`].
592    Enum,
593    /// A [`DevEncoded`], only ever a scalar.
594    Encoded,
595}
596
597/// The layout of a Tango attribute value.
598/// 
599/// The maximum size for spectrum and image formats is also given, those can be set to 0
600/// or any value when no.
601#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
602pub enum AttrDataFormat {
603    Scalar,
604    Spectrum(usize),
605    Image(usize, usize),
606}
607
608/// A type an attribute value can be, and what kind and format it is advertised as.
609pub trait AttrData {
610    const DATA_KIND: AttrDataKind;
611    const DATA_FORMAT: AttrDataFormat;
612}
613
614/// A type a command argument or result can be, and what kind it is advertised as.
615pub 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
637/// Implement the kinds of these types as those of the data they convert to.
638macro_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
685// =========================== //
686//       Encoded images        //
687// =========================== //
688
689impl DevEncoded {
690    /// The format of a JPEG image in color, see [`Self::to_image`].
691    pub const JPEG_RGB: &'static str = "JPEG_RGB";
692    /// The format of a JPEG image in shades of gray, see [`Self::to_image`].
693    pub const JPEG_GRAY8: &'static str = "JPEG_GRAY8";
694    /// The format of raw 8 bits gray pixels, see [`EncodedPixels::Gray8`].
695    pub const GRAY8: &'static str = "GRAY8";
696    /// The format of raw 16 bits gray pixels, see [`EncodedPixels::Gray16`].
697    pub const GRAY16: &'static str = "GRAY16";
698    /// The format of raw 24 bits color pixels, see [`EncodedPixels::Rgb24`].
699    pub const RGB24: &'static str = "RGB24";
700}
701
702/// The pixels of an image in one of the raw formats cppTango encodes images in, rows after
703/// rows: in the dimensions of an image, the width comes first and varies fastest, the way
704/// it does in an image attribute.
705///
706/// On the wire, such an image is its width and its height, both as big endian 16 bits
707/// integers, and then its pixels.
708#[derive(Debug, Clone, PartialEq)]
709pub enum EncodedPixels {
710    /// A [`DevEncoded::GRAY8`] image, sharing the octets of the value.
711    Gray8(Image<u8>),
712    /// A [`DevEncoded::GRAY16`] image, whose big endian pixels are converted.
713    Gray16(Image<u16>),
714    /// A [`DevEncoded::RGB24`] image, sharing the octets of the value, the three components
715    /// of a pixel being the first dimension, then the width, then the height.
716    Rgb24(Buf<u8, 3>),
717}
718
719impl DevEncoded {
720
721    /// The pixels of an image in one of the raw formats, failing for any other format,
722    /// JPEG included, see [`Self::to_image`] for that one.
723    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    /// Encode these pixels in their raw format, failing for an image wider or higher than
762    /// the 65535 pixels its dimensions have room for.
763    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    /// The image, in any of the formats cppTango encodes images in, JPEG included.
793    #[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    /// Encode the image in the raw format closest to it: gray images keep their depth, and
827    /// any other one is converted to 8 bits color.
828    #[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    /// A typed data converts into a value and back without copying, while the usual
853    /// owned types convert by copying their values.
854    #[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    /// A reference to a value converts into a reference to what it holds.
875    #[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    /// Converting into another element type or other dimensions fails, saying what was
886    /// expected and what was there.
887    #[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        // A short is not an enumerated value, even though both are 16 bits.
893        assert!(DevEnum::try_from(Value::from(1i16)).is_err());
894    }
895
896    /// The kinds a type is advertised as follow from its element type and dimensions.
897    #[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    /// Values are equal when they hold the same data, wherever it is stored, and of the
912    /// same element type.
913    #[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    /// The raw formats go out with their dimensions ahead of their pixels and come back as
921    /// they were, the 8 bits ones sharing the octets of the value.
922    #[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    /// An image whose octets do not add up to its dimensions, or in a format that is not a
946    /// raw one, is refused.
947    #[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    /// An image goes through its closest raw format and comes back as it was, and a JPEG
957    /// one is decoded whatever its depth.
958    #[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}