1- use std:: collections:: HashSet ;
2- use std:: sync:: LazyLock ;
3-
4- use serde:: { Deserialize , Serialize } ;
1+ use crate :: { DeltaResult , Error } ;
2+ use enumset:: { enum_set, EnumSet , EnumSetType } ;
3+ use serde:: { de:: DeserializeOwned , Deserialize , Serialize } ;
4+ use std:: any:: type_name;
5+ use std:: fmt:: Display ;
56use strum:: { AsRefStr , Display as StrumDisplay , EnumString , VariantNames } ;
67
78pub ( crate ) use column_mapping:: column_mapping_mode;
@@ -18,14 +19,12 @@ mod column_mapping;
1819 Serialize ,
1920 Deserialize ,
2021 Debug ,
21- Clone ,
22- Eq ,
23- PartialEq ,
2422 EnumString ,
2523 StrumDisplay ,
2624 AsRefStr ,
2725 VariantNames ,
2826 Hash ,
27+ EnumSetType ,
2928) ]
3029#[ strum( serialize_all = "camelCase" ) ]
3130#[ serde( rename_all = "camelCase" ) ]
@@ -59,14 +58,12 @@ pub enum ReaderFeature {
5958 Serialize ,
6059 Deserialize ,
6160 Debug ,
62- Clone ,
63- Eq ,
64- PartialEq ,
6561 EnumString ,
6662 StrumDisplay ,
6763 AsRefStr ,
6864 VariantNames ,
6965 Hash ,
66+ EnumSetType ,
7067) ]
7168#[ strum( serialize_all = "camelCase" ) ]
7269#[ serde( rename_all = "camelCase" ) ]
@@ -123,30 +120,55 @@ impl From<WriterFeature> for String {
123120 }
124121}
125122
126- pub ( crate ) static SUPPORTED_READER_FEATURES : LazyLock < HashSet < ReaderFeature > > =
127- LazyLock :: new ( || {
128- HashSet :: from ( [
129- ReaderFeature :: ColumnMapping ,
130- ReaderFeature :: DeletionVectors ,
131- ReaderFeature :: TimestampWithoutTimezone ,
132- ReaderFeature :: TypeWidening ,
133- ReaderFeature :: TypeWideningPreview ,
134- ReaderFeature :: VacuumProtocolCheck ,
135- ReaderFeature :: V2Checkpoint ,
136- ] )
137- } ) ;
138-
139- pub ( crate ) static SUPPORTED_WRITER_FEATURES : LazyLock < HashSet < WriterFeature > > =
123+ pub ( crate ) static SUPPORTED_READER_FEATURES : EnumSet < ReaderFeature > = enum_set ! (
124+ ReaderFeature :: ColumnMapping
125+ | ReaderFeature :: DeletionVectors
126+ | ReaderFeature :: TimestampWithoutTimezone
127+ | ReaderFeature :: TypeWidening
128+ | ReaderFeature :: TypeWideningPreview
129+ | ReaderFeature :: VacuumProtocolCheck
130+ | ReaderFeature :: V2Checkpoint
131+ ) ;
132+
133+ pub ( crate ) static SUPPORTED_WRITER_FEATURES : EnumSet < WriterFeature > =
140134 // note: we 'support' Invariants, but only insofar as we check that they are not present.
141135 // we support writing to tables that have Invariants enabled but not used. similarly, we only
142136 // support DeletionVectors in that we never write them (no DML).
143- LazyLock :: new ( || {
144- HashSet :: from ( [
145- WriterFeature :: AppendOnly ,
146- WriterFeature :: DeletionVectors ,
147- WriterFeature :: Invariants ,
148- ] )
149- } ) ;
137+ enum_set ! (
138+ WriterFeature :: AppendOnly | WriterFeature :: DeletionVectors | WriterFeature :: Invariants
139+ ) ;
140+
141+ pub ( crate ) static CDF_SUPPORTED_READER_FEATURES : EnumSet < ReaderFeature > =
142+ enum_set ! ( ReaderFeature :: DeletionVectors ) ;
143+
144+ pub ( crate ) fn ensure_supported_features < F > (
145+ features : & [ String ] ,
146+ supported : & EnumSet < F > ,
147+ ) -> DeltaResult < ( ) >
148+ where
149+ F : DeserializeOwned + EnumSetType + Display ,
150+ {
151+ let features_type = type_name :: < F > ( ) . rsplit ( "::" ) . next ( ) . unwrap ( ) ;
152+ for feature_str in features {
153+ match serde_json:: from_str :: < F > ( & format ! ( "\" {}\" " , feature_str) ) {
154+ Ok ( feature_enum) => {
155+ if !supported. contains ( feature_enum) {
156+ return Err ( Error :: Unsupported ( format ! (
157+ "Unsupported {} variant `{}`. Supported features: {}" ,
158+ features_type, feature_enum, supported
159+ ) ) ) ;
160+ }
161+ }
162+ Err ( _) => {
163+ return Err ( Error :: Unsupported ( format ! (
164+ "Unknown {} variant `{}`. Supported features: {}" ,
165+ features_type, feature_str, supported
166+ ) ) ) ;
167+ }
168+ }
169+ }
170+ Ok ( ( ) )
171+ }
150172
151173#[ cfg( test) ]
152174mod tests {
@@ -217,4 +239,30 @@ mod tests {
217239 assert_eq ! ( from_str, feature) ;
218240 }
219241 }
242+
243+ #[ test]
244+ fn test_ensure_supported_features ( ) {
245+ let supported_features =
246+ enum_set ! ( ReaderFeature :: ColumnMapping | ReaderFeature :: DeletionVectors ) ;
247+ let table_features = vec ! [ ReaderFeature :: ColumnMapping . to_string( ) ] ;
248+ ensure_supported_features ( & table_features, & supported_features) . unwrap ( ) ;
249+ }
250+
251+ #[ test]
252+ fn test_ensure_supported_features_unsupported ( ) {
253+ let supported_features =
254+ enum_set ! ( ReaderFeature :: ColumnMapping | ReaderFeature :: DeletionVectors ) ;
255+ let table_features = vec ! [ ReaderFeature :: TimestampWithoutTimezone . to_string( ) ] ;
256+ let error = ensure_supported_features ( & table_features, & supported_features) . unwrap_err ( ) ;
257+ assert_eq ! ( error. to_string( ) , "Unsupported: Unsupported ReaderFeature variant `timestampNtz`. Supported features: columnMapping | deletionVectors" . to_string( ) ) ;
258+ }
259+
260+ #[ test]
261+ fn test_ensure_supported_features_unknown ( ) {
262+ let supported_features =
263+ enum_set ! ( ReaderFeature :: ColumnMapping | ReaderFeature :: DeletionVectors ) ;
264+ let table_features = vec ! [ ReaderFeature :: ColumnMapping . to_string( ) , "idk" . to_string( ) ] ;
265+ let error = ensure_supported_features ( & table_features, & supported_features) . unwrap_err ( ) ;
266+ assert_eq ! ( error. to_string( ) , "Unsupported: Unknown ReaderFeature variant `idk`. Supported features: columnMapping | deletionVectors" . to_string( ) ) ;
267+ }
220268}
0 commit comments