lace/cc/feature/
ftype.rs

1use std::convert::TryFrom;
2
3use serde::Deserialize;
4use serde::Serialize;
5
6use crate::codebook::ColType;
7use crate::data::Datum;
8
9/// Feature type
10#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
11#[serde(rename_all = "snake_case")]
12pub enum FType {
13    Binary,
14    Continuous,
15    Categorical,
16    Count,
17}
18
19impl std::fmt::Display for FType {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        match self {
22            Self::Binary => write!(f, "Binary"),
23            Self::Continuous => write!(f, "Continuous"),
24            Self::Categorical => write!(f, "Categorical"),
25            Self::Count => write!(f, "Count"),
26        }
27    }
28}
29
30impl std::str::FromStr for FType {
31    type Err = String;
32
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        match s {
35            "Binary" => Ok(FType::Binary),
36            "Continuous" => Ok(FType::Continuous),
37            "Categorical" => Ok(FType::Categorical),
38            "Count" => Ok(FType::Count),
39            invalid => Err(format!("Invalid ftype: '{invalid}'")),
40        }
41    }
42}
43
44impl From<FType> for String {
45    fn from(ftype: FType) -> Self {
46        ftype.to_string()
47    }
48}
49
50/// FType compatibility information
51#[derive(Debug)]
52pub struct FTypeCompat {
53    /// The FType of the Datum passed to this feature
54    pub ftype_req: FType,
55    /// The FType of this feature
56    pub ftype: FType,
57}
58
59impl TryFrom<&Datum> for FType {
60    type Error = ();
61
62    fn try_from(datum: &Datum) -> Result<Self, Self::Error> {
63        match datum {
64            Datum::Binary(_) => Ok(FType::Binary),
65            Datum::Categorical(_) => Ok(FType::Categorical),
66            Datum::Continuous(_) => Ok(FType::Continuous),
67            Datum::Count(_) => Ok(FType::Count),
68            Datum::Missing => Err(()),
69        }
70    }
71}
72
73impl FType {
74    pub fn from_coltype(coltype: &ColType) -> FType {
75        match coltype {
76            ColType::Continuous { .. } => FType::Continuous,
77            ColType::Categorical { .. } => FType::Categorical,
78            ColType::Count { .. } => FType::Count,
79        }
80    }
81
82    pub fn is_continuous(self) -> bool {
83        matches!(self, FType::Continuous)
84    }
85
86    pub fn is_categorical(self) -> bool {
87        matches!(self, FType::Categorical)
88    }
89
90    pub fn is_count(self) -> bool {
91        matches!(self, FType::Count)
92    }
93
94    /// Return a tuple
95    pub fn datum_compatible(self, datum: &Datum) -> (bool, FTypeCompat) {
96        if let Ok(ftype_req) = FType::try_from(datum) {
97            let ok = ftype_req == self;
98            (
99                ok,
100                FTypeCompat {
101                    ftype_req,
102                    ftype: self,
103                },
104            )
105        } else {
106            // always compatible if the datum is a missing value
107            (
108                true,
109                FTypeCompat {
110                    ftype_req: self,
111                    ftype: self,
112                },
113            )
114        }
115    }
116}
117
118// TODO: tests