Skip to main content

risingwave_connector/
macros.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[macro_export]
16macro_rules! for_all_classified_sources {
17    ($macro:path $(, $extra_args:tt)*) => {
18        $macro! {
19            // cdc sources
20            {
21                { Mysql },
22                { Postgres },
23                { Citus },
24                { Mongodb },
25                { SqlServer },
26                { Oracle }
27            },
28            // other sources
29            // todo: file source do not nest with mq source.
30            {
31                { Kafka, $crate::source::kafka::KafkaProperties, $crate::source::kafka::KafkaSplit },
32                { Pulsar, $crate::source::pulsar::PulsarProperties, $crate::source::pulsar::PulsarSplit },
33                { Kinesis, $crate::source::kinesis::KinesisProperties, $crate::source::kinesis::split::KinesisSplit },
34                { Nexmark, $crate::source::nexmark::NexmarkProperties, $crate::source::nexmark::NexmarkSplit },
35                { Datagen, $crate::source::datagen::DatagenProperties, $crate::source::datagen::DatagenSplit },
36                { GooglePubsub, $crate::source::google_pubsub::PubsubProperties, $crate::source::google_pubsub::PubsubSplit },
37                { Mqtt, $crate::source::mqtt::MqttProperties, $crate::source::mqtt::split::MqttSplit },
38                { Nats, $crate::source::nats::NatsProperties, $crate::source::nats::split::NatsSplit },
39                { S3, $crate::source::filesystem::LegacyS3Properties, $crate::source::filesystem::LegacyFsSplit },
40                { Gcs, $crate::source::filesystem::opendal_source::GcsProperties , $crate::source::filesystem::OpendalFsSplit<$crate::source::filesystem::opendal_source::OpendalGcs> },
41                { OpendalS3, $crate::source::filesystem::opendal_source::OpendalS3Properties, $crate::source::filesystem::OpendalFsSplit<$crate::source::filesystem::opendal_source::OpendalS3> },
42                { PosixFs, $crate::source::filesystem::opendal_source::PosixFsProperties, $crate::source::filesystem::OpendalFsSplit<$crate::source::filesystem::opendal_source::OpendalPosixFs> },
43                { BatchPosixFs, $crate::source::filesystem::opendal_source::BatchPosixFsProperties, $crate::source::filesystem::opendal_source::BatchPosixFsSplit },
44                { Azblob, $crate::source::filesystem::opendal_source::AzblobProperties, $crate::source::filesystem::OpendalFsSplit<$crate::source::filesystem::opendal_source::OpendalAzblob> },
45                { Test, $crate::source::test_source::TestSourceProperties, $crate::source::test_source::TestSourceSplit},
46                { Iceberg, $crate::source::iceberg::IcebergProperties, $crate::source::iceberg::IcebergSplit},
47                { AdbcSnowflake, $crate::source::adbc_snowflake::AdbcSnowflakeProperties, $crate::source::adbc_snowflake::AdbcSnowflakeSplit}
48            }
49            $(
50                ,$extra_args
51            )*
52        }
53    };
54}
55
56#[macro_export]
57macro_rules! for_all_connections {
58    ($macro:path $(, $extra_args:tt)*) => {
59        $macro! {
60            {
61                { Kafka, $crate::connector_common::KafkaConnection, risingwave_pb::catalog::connection_params::PbConnectionType, "kafka"},
62                { Iceberg, $crate::connector_common::IcebergConnection, risingwave_pb::catalog::connection_params::PbConnectionType, "iceberg"},
63                { SchemaRegistry, $crate::connector_common::ConfluentSchemaRegistryConnection, risingwave_pb::catalog::connection_params::PbConnectionType, "schema_registry"},
64                { Elasticsearch, $crate::connector_common::ElasticsearchConnection, risingwave_pb::catalog::connection_params::PbConnectionType, "elasticsearch"}
65            }
66            $(,$extra_args)*
67        }
68    };
69}
70
71#[macro_export]
72macro_rules! for_all_sources_inner {
73    (
74        { $({ $cdc_source_type:ident }),* },
75        { $({ $source_variant:ident, $prop_name:ty, $split:ty }),* },
76        $macro:tt $(, $extra_args:tt)*
77    ) => {
78        $crate::paste! {
79            $macro! {
80                {
81                    $(
82                        {
83                            [< $cdc_source_type Cdc >],
84                            $crate::source::cdc::[< $cdc_source_type CdcProperties >],
85                            $crate::source::cdc::DebeziumCdcSplit<$crate::source::cdc::$cdc_source_type>
86                        }
87                    ),*
88                    ,
89                    $(
90                        { $source_variant, $prop_name, $split }
91                    ),*
92                }
93                $(, $extra_args)*
94            }
95        }
96    };
97}
98
99#[macro_export]
100macro_rules! for_all_sources {
101    ($macro:path $(, $arg:tt )*) => {
102        $crate::for_all_classified_sources! {$crate::for_all_sources_inner, $macro $(,$arg)* }
103    };
104}
105
106/// The invocation:
107/// ```ignore
108/// dispatch_source_enum_inner!(
109///     {
110///         {A1,B1,C1},
111///         {A2,B2,C2}
112///     },
113///     EnumType, enum_value, inner_ident, body
114/// );
115/// ```
116/// expands to:
117/// ```ignore
118/// match enum_value {
119///     EnumType::A1(inner_ident) => {
120///         #[allow(dead_code)]
121///         type PropType = B1;
122///         #[allow(dead_code)]
123///         type SplitType = C1;
124///         {
125///             body
126///         }
127///     }
128///     EnumType::A2(inner_ident) => {
129///         #[allow(dead_code)]
130///         type PropType = B2;
131///         #[allow(dead_code)]
132///         type SplitType = C2;
133///         {
134///             body
135///         }
136///     }
137/// }
138/// ```
139#[macro_export]
140macro_rules! dispatch_source_enum_inner {
141    (
142        {$({$source_variant:ident, $prop_name:ty, $split:ty }),*},
143        $enum_type:ident,
144        $enum_value:expr,
145        $inner_name:ident,
146        $body:expr
147    ) => {{
148        match $enum_value {
149            $(
150                $enum_type::$source_variant($inner_name) => {
151                    #[allow(dead_code)]
152                    type PropType = $prop_name;
153                    #[allow(dead_code)]
154                    type SplitType = $split;
155                    {
156                        $body
157                    }
158                },
159            )*
160        }
161    }}
162}
163
164/// Usage: `dispatch_source_enum!(EnumType, enum_value, |inner_ident| body)`.
165///
166/// Inside `body`:
167/// - use `inner_ident` to represent the matched variant.
168/// - use `PropType` to represent the concrete property type.
169/// - use `SplitType` to represent the concrete split type.
170///
171/// Expands to:
172/// ```ignore
173/// match enum_value {
174///     EnumType::Variant1(inner_ident) => {
175///         body
176///     }
177///     ...
178/// }
179/// ```
180///
181/// Note: `inner_ident` must be passed as an argument due to macro hygiene.
182#[macro_export]
183macro_rules! dispatch_source_enum {
184    ($enum_type:ident, $enum_value:expr, |$inner_name:ident| $body:expr) => {{
185        $crate::for_all_sources! {$crate::dispatch_source_enum_inner, $enum_type, { $enum_value }, $inner_name, $body}
186    }};
187}
188
189#[macro_export]
190macro_rules! match_source_name_str_inner {
191    (
192        {$({$source_variant:ident, $prop_name:ty, $split:ty }),*},
193        $source_name_str:expr,
194        $prop_type_name:ident,
195        $body:expr,
196        $on_other_closure:expr
197    ) => {{
198        match $source_name_str {
199            $(
200                <$prop_name>::SOURCE_NAME => {
201                    type $prop_type_name = $prop_name;
202                    {
203                        $body
204                    }
205                },
206            )*
207            other => ($on_other_closure)(other),
208        }
209    }}
210}
211
212/// Matches against `SourceProperties::SOURCE_NAME` to dispatch logic.
213#[macro_export]
214macro_rules! match_source_name_str {
215    ($source_name_str:expr, $prop_type_name:ident, $body:expr, $on_other_closure:expr) => {{
216        $crate::for_all_sources! {
217            $crate::match_source_name_str_inner,
218            { $source_name_str },
219            $prop_type_name,
220            { $body },
221            { $on_other_closure }
222        }
223    }};
224}
225
226/// [`dispatch_source_enum`] with `SplitImpl` as the enum type.
227#[macro_export]
228macro_rules! dispatch_split_impl {
229    ($impl:expr, | $inner_name:ident | $body:expr) => {{
230        use $crate::source::SplitImpl;
231        $crate::dispatch_source_enum! {SplitImpl, { $impl }, |$inner_name| $body}
232    }};
233}
234
235#[macro_export]
236macro_rules! impl_connection {
237    ({$({ $variant_name:ident, $connection_type:ty, $pb_connection_path:path, $connection_type_name:literal }),*}) => {
238        // impl the connection name for all connections
239        pub fn connection_name_to_prop_type_name(connection_name: &str) -> Option<&'static str> {
240            match connection_name {
241                $(
242                    $connection_type_name => Some(
243                        std::any::type_name::<$connection_type>()
244                    ),
245                )*
246                _ => None,
247            }
248        }
249
250        pub fn pb_connection_type_to_connection_type(pb_connection_type: &risingwave_pb::catalog::connection_params::PbConnectionType) -> Option<&'static str> {
251            match pb_connection_type {
252                $(
253                    risingwave_pb::catalog::connection_params::PbConnectionType::$variant_name => Some($connection_type_name),
254                )*
255                _ => None,
256            }
257        }
258
259        pub fn build_connection(
260            pb_connection_type: risingwave_pb::catalog::connection_params::PbConnectionType,
261            value_secret_filled: std::collections::BTreeMap<String, String>
262        ) -> $crate::error::ConnectorResult<Box<dyn $crate::connector_common::Connection>> {
263            match pb_connection_type {
264                $(
265                    <$pb_connection_path>::$variant_name => {
266                        let c: Box<$connection_type> = serde_json::from_value(json!(value_secret_filled)).map_err($crate::error::ConnectorError::from)?;
267                        Ok(c)
268                    },
269                )*
270                risingwave_pb::catalog::connection_params::PbConnectionType::Unspecified => unreachable!(),
271            }
272        }
273
274        pub fn enforce_secret_connection<'a>(
275            pb_connection_type: &risingwave_pb::catalog::connection_params::PbConnectionType,
276            prop_iter: impl Iterator<Item = &'a str>,
277        ) -> $crate::error::ConnectorResult<()> {
278            match pb_connection_type {
279                $(
280                    <$pb_connection_path>::$variant_name => {
281                        <$connection_type>::enforce_secret(prop_iter)
282                    },
283                )*
284                risingwave_pb::catalog::connection_params::PbConnectionType::Unspecified => unreachable!(),
285            }
286        }
287    }
288}
289
290#[macro_export]
291macro_rules! impl_split {
292    ({$({ $variant_name:ident, $prop_name:ty, $split:ty}),*}) => {
293
294        #[derive(Debug, Clone, EnumAsInner, PartialEq)]
295        pub enum SplitImpl {
296            $(
297                $variant_name($split),
298            )*
299        }
300
301        $(
302            impl TryFrom<SplitImpl> for $split {
303                type Error = $crate::error::ConnectorError;
304
305                fn try_from(split: SplitImpl) -> std::result::Result<Self, Self::Error> {
306                    match split {
307                        SplitImpl::$variant_name(inner) => Ok(inner),
308                        other => risingwave_common::bail!(
309                            "expected {} but got {:?}",
310                            stringify!($split),
311                            other
312                        ),
313                    }
314                }
315            }
316
317            impl From<$split> for SplitImpl {
318                fn from(split: $split) -> SplitImpl {
319                    SplitImpl::$variant_name(split)
320                }
321            }
322
323        )*
324    }
325}
326
327/// [`dispatch_source_enum`] with `ConnectorProperties` as the enum type.
328#[macro_export]
329macro_rules! dispatch_source_prop {
330    ($connector_properties:expr, |$inner_ident:ident| $body:expr) => {{
331        use $crate::source::ConnectorProperties;
332        $crate::dispatch_source_enum! {ConnectorProperties, { $connector_properties }, |$inner_ident| {$body}}
333    }};
334}
335
336#[macro_export]
337macro_rules! impl_connector_properties {
338    ({$({ $variant_name:ident, $prop_name:ty, $split:ty}),*}) => {
339        #[derive(Clone, Debug)]
340        pub enum ConnectorProperties {
341            $(
342                $variant_name(Box<$prop_name>),
343            )*
344        }
345
346        impl ConnectorProperties {
347            pub fn kind(&self) -> &'static str {
348                match self {
349                    $(
350                        ConnectorProperties::$variant_name(_) => stringify!($variant_name),
351                    )*
352                }
353            }
354        }
355
356        $(
357            impl From<$prop_name> for ConnectorProperties {
358                fn from(prop: $prop_name) -> ConnectorProperties {
359                    ConnectorProperties::$variant_name(Box::new(prop))
360                }
361            }
362        )*
363    }
364}
365
366#[macro_export]
367macro_rules! impl_cdc_source_type {
368    (
369        {$({$cdc_source_type:tt}),*},
370        {$($_ignore:tt),*}
371    ) => {
372        $(
373            $crate::paste!{
374                #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
375                pub struct $cdc_source_type;
376                impl CdcSourceTypeTrait for $cdc_source_type {
377                    const CDC_CONNECTOR_NAME: &'static str = concat!(stringify!([<$cdc_source_type:lower>]), "-cdc");
378                    fn source_type() -> CdcSourceType {
379                        CdcSourceType::$cdc_source_type
380                    }
381                }
382                pub type [<$cdc_source_type CdcProperties>] = CdcProperties<$cdc_source_type>;
383            }
384        )*
385
386        #[derive(Clone)]
387        pub enum CdcSourceType {
388            $(
389                $cdc_source_type,
390            )*
391            Unspecified,
392        }
393
394        impl From<PbSourceType> for CdcSourceType {
395            fn from(value: PbSourceType) -> Self {
396                match value {
397                    PbSourceType::Unspecified => CdcSourceType::Unspecified,
398                    $(
399                        PbSourceType::$cdc_source_type => CdcSourceType::$cdc_source_type,
400                    )*
401                }
402            }
403        }
404
405        impl From<CdcSourceType> for PbSourceType {
406            fn from(this: CdcSourceType) -> PbSourceType {
407                match this {
408                    $(
409                        CdcSourceType::$cdc_source_type => PbSourceType::$cdc_source_type,
410                    )*
411                   CdcSourceType::Unspecified => PbSourceType::Unspecified,
412                }
413            }
414        }
415
416    }
417}