1use serde::{Deserialize, Serialize};
16
17#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "kebab-case")]
19#[serde(deny_unknown_fields)]
20pub struct ComputeNodeConfig {
21 #[serde(rename = "use")]
22 phantom_use: Option<String>,
23 pub id: String,
24
25 pub address: String,
26 #[serde(with = "string")]
27 pub port: u16,
28 pub listen_address: String,
29 pub exporter_port: u16,
30 pub async_stack_trace: String,
31 pub enable_tiered_cache: bool,
32
33 pub provide_minio: Option<Vec<MinioConfig>>,
34 pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
35 pub provide_compute_node: Option<Vec<ComputeNodeConfig>>,
36 pub provide_opendal: Option<Vec<OpendalConfig>>,
37 pub provide_aws_s3: Option<Vec<AwsS3Config>>,
38 pub provide_moat: Option<Vec<MoatConfig>>,
39 pub provide_tempo: Option<Vec<TempoConfig>>,
40 pub user_managed: bool,
41 pub resource_group: String,
42
43 pub total_memory_bytes: usize,
44 pub parallelism: usize,
45 pub role: String,
46}
47
48#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
49#[serde(rename_all = "kebab-case")]
50#[serde(deny_unknown_fields)]
51pub enum MetaBackend {
52 Memory,
53 Sqlite,
54 Postgres,
55 Mysql,
56 Env,
57}
58
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60#[serde(rename_all = "kebab-case")]
61#[serde(deny_unknown_fields)]
62pub struct MetaNodeConfig {
63 #[serde(rename = "use")]
64 phantom_use: Option<String>,
65 pub id: String,
66
67 pub address: String,
68 #[serde(with = "string")]
69 pub port: u16,
70 pub listen_address: String,
71 pub dashboard_port: u16,
72 pub exporter_port: u16,
73
74 pub user_managed: bool,
75
76 pub meta_backend: MetaBackend,
77 pub provide_sqlite_backend: Option<Vec<SqliteConfig>>,
78 pub provide_postgres_backend: Option<Vec<PostgresConfig>>,
79 pub provide_mysql_backend: Option<Vec<MySqlConfig>>,
80 pub provide_prometheus: Option<Vec<PrometheusConfig>>,
81
82 pub provide_compute_node: Option<Vec<ComputeNodeConfig>>,
83 pub provide_compactor: Option<Vec<CompactorConfig>>,
84
85 pub provide_tempo: Option<Vec<TempoConfig>>,
86
87 pub provide_aws_s3: Option<Vec<AwsS3Config>>,
88 pub provide_minio: Option<Vec<MinioConfig>>,
89 pub provide_opendal: Option<Vec<OpendalConfig>>,
90 pub provide_moat: Option<Vec<MoatConfig>>,
91}
92
93#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
94#[serde(rename_all = "kebab-case")]
95#[serde(deny_unknown_fields)]
96pub struct FrontendConfig {
97 #[serde(rename = "use")]
98 phantom_use: Option<String>,
99 pub id: String,
100
101 pub address: String,
102 #[serde(with = "string")]
103 pub port: u16,
104 pub listen_address: String,
105 pub exporter_port: u16,
106 pub health_check_port: u16,
107
108 pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
109 pub provide_tempo: Option<Vec<TempoConfig>>,
110 pub provide_prometheus: Option<Vec<PrometheusConfig>>,
111
112 pub user_managed: bool,
113}
114
115#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
116#[serde(rename_all = "kebab-case")]
117#[serde(deny_unknown_fields)]
118pub struct CompactorConfig {
119 #[serde(rename = "use")]
120 phantom_use: Option<String>,
121 pub id: String,
122
123 pub address: String,
124 #[serde(with = "string")]
125 pub port: u16,
126 pub listen_address: String,
127 pub exporter_port: u16,
128
129 pub provide_minio: Option<Vec<MinioConfig>>,
130
131 pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
132 pub provide_tempo: Option<Vec<TempoConfig>>,
133
134 pub user_managed: bool,
135 pub compaction_worker_threads_number: Option<usize>,
136
137 pub compactor_mode: String,
138}
139
140#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
141#[serde(rename_all = "kebab-case")]
142#[serde(deny_unknown_fields)]
143pub struct MinioConfig {
144 #[serde(rename = "use")]
145 phantom_use: Option<String>,
146 pub id: String,
147
148 pub address: String,
149 #[serde(with = "string")]
150 pub port: u16,
151 pub listen_address: String,
152
153 pub console_address: String,
154 #[serde(with = "string")]
155 pub console_port: u16,
156
157 pub root_user: String,
158 pub root_password: String,
159 pub hummock_bucket: String,
160
161 pub provide_prometheus: Option<Vec<PrometheusConfig>>,
162
163 pub api_requests_max: usize,
165 pub api_requests_deadline: String,
166}
167
168#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
169#[serde(rename_all = "kebab-case")]
170#[serde(deny_unknown_fields)]
171pub struct SqliteConfig {
172 #[serde(rename = "use")]
173 phantom_use: Option<String>,
174 pub id: String,
175
176 pub file: String,
177}
178
179#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
180#[serde(rename_all = "kebab-case")]
181#[serde(deny_unknown_fields)]
182pub struct PrometheusConfig {
183 #[serde(rename = "use")]
184 phantom_use: Option<String>,
185 pub id: String,
186
187 pub address: String,
188 #[serde(with = "string")]
189 pub port: u16,
190 pub listen_address: String,
191
192 pub remote_write: bool,
193 pub remote_write_region: String,
194 pub remote_write_url: String,
195
196 pub scrape_interval: String,
197
198 pub provide_compute_node: Option<Vec<ComputeNodeConfig>>,
199 pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
200 pub provide_minio: Option<Vec<MinioConfig>>,
201 pub provide_compactor: Option<Vec<CompactorConfig>>,
202 pub provide_frontend: Option<Vec<FrontendConfig>>,
203}
204
205#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
206#[serde(rename_all = "kebab-case")]
207#[serde(deny_unknown_fields)]
208pub struct GrafanaConfig {
209 #[serde(rename = "use")]
210 phantom_use: Option<String>,
211 pub id: String,
212 pub address: String,
213 pub listen_address: String,
214 pub port: u16,
215
216 pub provide_prometheus: Option<Vec<PrometheusConfig>>,
217 pub provide_tempo: Option<Vec<TempoConfig>>,
218}
219
220#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
221#[serde(rename_all = "kebab-case")]
222#[serde(deny_unknown_fields)]
223pub struct TempoConfig {
224 #[serde(rename = "use")]
225 phantom_use: Option<String>,
226 pub id: String,
227
228 pub listen_address: String,
229 pub address: String,
230 pub port: u16,
231 pub otlp_port: u16,
232 pub max_bytes_per_trace: usize,
233}
234
235#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
236#[serde(rename_all = "kebab-case")]
237#[serde(deny_unknown_fields)]
238pub struct AwsS3Config {
239 #[serde(rename = "use")]
240 phantom_use: Option<String>,
241 pub id: String,
242 pub bucket: String,
243}
244
245#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
246#[serde(rename_all = "kebab-case")]
247#[serde(deny_unknown_fields)]
248pub struct MotoConfig {
249 #[serde(rename = "use")]
250 phantom_use: Option<String>,
251 pub id: String,
252
253 pub address: String,
254 #[serde(with = "string")]
255 pub port: u16,
256
257 pub image: String,
258 pub user_managed: bool,
259
260 pub provide_minio: Option<Vec<MinioConfig>>,
261}
262
263#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
264#[serde(rename_all = "kebab-case")]
265#[serde(deny_unknown_fields)]
266pub struct OpendalConfig {
267 #[serde(rename = "use")]
268 phantom_use: Option<String>,
269
270 pub id: String,
271 pub engine: String,
272 pub namenode: String,
273 pub bucket: String,
274}
275
276#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
277#[serde(rename_all = "kebab-case")]
278#[serde(deny_unknown_fields)]
279pub struct KafkaConfig {
280 #[serde(rename = "use")]
281 phantom_use: Option<String>,
282 pub id: String,
283
284 pub address: String,
286 #[serde(with = "string")]
287 pub port: u16,
288 pub docker_port: u16,
291
292 #[serde(with = "string")]
293 pub controller_port: u16,
294
295 pub image: String,
296 pub persist_data: bool,
297 pub node_id: u32,
298
299 pub user_managed: bool,
300}
301
302#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
303#[serde(rename_all = "kebab-case")]
304#[serde(deny_unknown_fields)]
305pub struct SchemaRegistryConfig {
306 #[serde(rename = "use")]
307 phantom_use: Option<String>,
308
309 pub id: String,
310
311 pub address: String,
312 #[serde(with = "string")]
313 pub port: u16,
314
315 pub provide_kafka: Option<Vec<KafkaConfig>>,
316
317 pub image: String,
318 pub user_managed: bool,
321}
322
323#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
324#[serde(rename_all = "kebab-case")]
325#[serde(deny_unknown_fields)]
326pub struct PubsubConfig {
327 #[serde(rename = "use")]
328 phantom_use: Option<String>,
329 pub id: String,
330 #[serde(default)]
331 pub user_managed: bool,
332 #[serde(with = "string")]
333 pub port: u16,
334 pub address: String,
335
336 pub persist_data: bool,
337}
338
339#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
340#[serde(rename_all = "kebab-case")]
341#[serde(deny_unknown_fields)]
342pub struct PulsarConfig {
343 #[serde(rename = "use")]
344 phantom_use: Option<String>,
345 pub id: String,
346
347 pub address: String,
348 pub broker_port: u16,
349 pub http_port: u16,
350
351 pub user_managed: bool,
352 pub image: String,
353 pub persist_data: bool,
354}
355
356#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
357#[serde(rename_all = "kebab-case")]
358#[serde(deny_unknown_fields)]
359pub struct RedisConfig {
360 #[serde(rename = "use")]
361 phantom_use: Option<String>,
362 pub id: String,
363
364 pub port: u16,
365 pub address: String,
366
367 pub image: String,
368 pub user_managed: bool,
369 pub persist_data: bool,
370}
371
372#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
373#[serde(rename_all = "kebab-case")]
374#[serde(deny_unknown_fields)]
375pub enum Application {
376 Metastore,
377 Connector,
378}
379
380#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
381#[serde(rename_all = "kebab-case")]
382#[serde(deny_unknown_fields)]
383pub struct MySqlConfig {
384 #[serde(rename = "use")]
385 phantom_use: Option<String>,
386 pub id: String,
387
388 pub port: u16,
389 pub address: String,
390
391 pub user: String,
392 pub password: String,
393 pub database: String,
394
395 pub application: Application,
396 pub image: String,
397 pub user_managed: bool,
398 pub persist_data: bool,
399}
400
401#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
402#[serde(rename_all = "kebab-case")]
403#[serde(deny_unknown_fields)]
404pub struct PostgresConfig {
405 #[serde(rename = "use")]
406 phantom_use: Option<String>,
407 pub id: String,
408
409 pub port: u16,
410 pub address: String,
411
412 pub user: String,
413 pub password: String,
414 pub database: String,
415
416 pub application: Application,
417 pub image: String,
418 pub user_managed: bool,
419 pub persist_data: bool,
420
421 pub latency_ms: u32,
423}
424
425#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
426#[serde(rename_all = "kebab-case")]
427#[serde(deny_unknown_fields)]
428pub struct SqlServerConfig {
429 #[serde(rename = "use")]
430 phantom_use: Option<String>,
431 pub id: String,
432
433 pub port: u16,
434 pub address: String,
435
436 pub user: String,
437 pub password: String,
438 pub database: String,
439
440 pub image: String,
441 pub user_managed: bool,
442 pub persist_data: bool,
443}
444
445#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
446#[serde(rename_all = "kebab-case")]
447#[serde(deny_unknown_fields)]
448pub struct MongoDbConfig {
449 #[serde(rename = "use")]
450 phantom_use: Option<String>,
451 pub id: String,
452
453 pub address: String,
454 pub port: u16,
455
456 pub image: String,
457 pub user_managed: bool,
458 pub persist_data: bool,
459}
460
461#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
462#[serde(rename_all = "kebab-case")]
463#[serde(deny_unknown_fields)]
464pub struct NatsConfig {
465 #[serde(rename = "use")]
466 phantom_use: Option<String>,
467 pub id: String,
468
469 pub address: String,
470 pub port: u16,
471 pub monitor_port: u16,
472
473 pub image: String,
474 pub user_managed: bool,
475 pub persist_data: bool,
476}
477
478#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
479#[serde(rename_all = "kebab-case")]
480#[serde(deny_unknown_fields)]
481pub struct MqttConfig {
482 #[serde(rename = "use")]
483 phantom_use: Option<String>,
484 pub id: String,
485
486 pub address: String,
487 pub port: u16,
488
489 pub image: String,
490 pub user_managed: bool,
491 pub persist_data: bool,
492}
493
494#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
495#[serde(rename_all = "kebab-case")]
496#[serde(deny_unknown_fields)]
497pub struct ElasticSearchConfig {
498 #[serde(rename = "use")]
499 phantom_use: Option<String>,
500 pub id: String,
501
502 pub address: String,
503 pub port: u16,
504
505 pub user: String,
506 pub password: String,
507
508 pub image: String,
509 pub user_managed: bool,
510 pub persist_data: bool,
511}
512
513#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
514#[serde(rename_all = "kebab-case")]
515#[serde(deny_unknown_fields)]
516pub struct OpenSearchConfig {
517 #[serde(rename = "use")]
518 phantom_use: Option<String>,
519 pub id: String,
520
521 pub address: String,
522 pub port: u16,
523
524 pub user: String,
525 pub password: String,
526
527 pub image: String,
528 pub user_managed: bool,
529 pub persist_data: bool,
530}
531
532#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
533#[serde(rename_all = "kebab-case")]
534#[serde(deny_unknown_fields)]
535pub struct LakekeeperConfig {
536 #[serde(rename = "use")]
537 phantom_use: Option<String>,
538 pub id: String,
539
540 pub port: u16,
541 pub address: String,
542
543 pub user_managed: bool,
544 pub persist_data: bool,
545
546 pub catalog_backend: String,
547 pub encryption_key: String,
548 pub provide_postgres_backend: Option<Vec<PostgresConfig>>,
549 pub provide_minio: Option<Vec<MinioConfig>>,
550}
551
552#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
553#[serde(rename_all = "kebab-case")]
554#[serde(deny_unknown_fields)]
555pub struct MoatConfig {
556 #[serde(rename = "use")]
557 phantom_use: Option<String>,
558 pub id: String,
559
560 pub address: String,
561 pub port: u16,
562
563 pub provide_minio: Option<Vec<MinioConfig>>,
564}
565
566#[derive(Clone, Debug, PartialEq)]
568pub enum ServiceConfig {
569 ComputeNode(ComputeNodeConfig),
570 MetaNode(MetaNodeConfig),
571 Frontend(FrontendConfig),
572 Compactor(CompactorConfig),
573 Minio(MinioConfig),
574 Sqlite(SqliteConfig),
575 Prometheus(PrometheusConfig),
576 Grafana(GrafanaConfig),
577 Tempo(TempoConfig),
578 Opendal(OpendalConfig),
579 AwsS3(AwsS3Config),
580 Moto(MotoConfig),
581 Kafka(KafkaConfig),
582 SchemaRegistry(SchemaRegistryConfig),
583 Pubsub(PubsubConfig),
584 Pulsar(PulsarConfig),
585 Redis(RedisConfig),
586 MySql(MySqlConfig),
587 Postgres(PostgresConfig),
588 SqlServer(SqlServerConfig),
589 MongoDb(MongoDbConfig),
590 ElasticSearch(ElasticSearchConfig),
591 OpenSearch(OpenSearchConfig),
592 Nats(NatsConfig),
593 Mqtt(MqttConfig),
594 Lakekeeper(LakekeeperConfig),
595 Moat(MoatConfig),
596}
597
598#[derive(PartialEq, Eq, Hash, Debug)]
599pub enum TaskGroup {
600 RisingWave,
601 Observability,
602 Kafka,
603 Pubsub,
604 Pulsar,
605 MySql,
606 Postgres,
607 SqlServer,
608 MongoDb,
609 ElasticSearch,
610 OpenSearch,
611 Nats,
612 Mqtt,
613 Redis,
614 Lakekeeper,
615 Moat,
616}
617
618impl ServiceConfig {
619 pub fn id(&self) -> &str {
620 match self {
621 Self::ComputeNode(c) => &c.id,
622 Self::MetaNode(c) => &c.id,
623 Self::Frontend(c) => &c.id,
624 Self::Compactor(c) => &c.id,
625 Self::Minio(c) => &c.id,
626 Self::Sqlite(c) => &c.id,
627 Self::Prometheus(c) => &c.id,
628 Self::Grafana(c) => &c.id,
629 Self::Tempo(c) => &c.id,
630 Self::AwsS3(c) => &c.id,
631 Self::Moto(c) => &c.id,
632 Self::Kafka(c) => &c.id,
633 Self::Pubsub(c) => &c.id,
634 Self::Pulsar(c) => &c.id,
635 Self::Redis(c) => &c.id,
636 Self::Opendal(c) => &c.id,
637 Self::MySql(c) => &c.id,
638 Self::Postgres(c) => &c.id,
639 Self::SqlServer(c) => &c.id,
640 Self::MongoDb(c) => &c.id,
641 Self::ElasticSearch(c) => &c.id,
642 Self::OpenSearch(c) => &c.id,
643 Self::Nats(c) => &c.id,
644 Self::Mqtt(c) => &c.id,
645 Self::SchemaRegistry(c) => &c.id,
646 Self::Lakekeeper(c) => &c.id,
647 Self::Moat(c) => &c.id,
648 }
649 }
650
651 pub fn port(&self) -> Option<u16> {
653 match self {
654 Self::ComputeNode(c) => Some(c.port),
655 Self::MetaNode(c) => Some(c.port),
656 Self::Frontend(c) => Some(c.port),
657 Self::Compactor(c) => Some(c.port),
658 Self::Minio(c) => Some(c.port),
659 Self::Sqlite(_) => None,
660 Self::Prometheus(c) => Some(c.port),
661 Self::Grafana(c) => Some(c.port),
662 Self::Tempo(c) => Some(c.port),
663 Self::AwsS3(_) => None,
664 Self::Moto(c) => Some(c.port),
665 Self::Kafka(c) => Some(c.port),
666 Self::Pubsub(c) => Some(c.port),
667 Self::Pulsar(c) => Some(c.http_port),
668 Self::Redis(c) => Some(c.port),
669 Self::Opendal(_) => None,
670 Self::MySql(c) => Some(c.port),
671 Self::Postgres(c) => Some(c.port),
672 Self::SqlServer(c) => Some(c.port),
673 Self::MongoDb(c) => Some(c.port),
674 Self::ElasticSearch(c) => Some(c.port),
675 Self::OpenSearch(c) => Some(c.port),
676 Self::Nats(c) => Some(c.port),
677 Self::Mqtt(c) => Some(c.port),
678 Self::SchemaRegistry(c) => Some(c.port),
679 Self::Lakekeeper(c) => Some(c.port),
680 Self::Moat(c) => Some(c.port),
681 }
682 }
683
684 pub fn user_managed(&self) -> bool {
685 match self {
686 Self::ComputeNode(c) => c.user_managed,
687 Self::MetaNode(c) => c.user_managed,
688 Self::Frontend(c) => c.user_managed,
689 Self::Compactor(c) => c.user_managed,
690 Self::Minio(_c) => false,
691 Self::Sqlite(_c) => false,
692 Self::Prometheus(_c) => false,
693 Self::Grafana(_c) => false,
694 Self::Tempo(_c) => false,
695 Self::AwsS3(_c) => false,
696 Self::Moto(c) => c.user_managed,
697 Self::Kafka(c) => c.user_managed,
698 Self::Pubsub(c) => c.user_managed,
699 Self::Pulsar(c) => c.user_managed,
700 Self::Redis(c) => c.user_managed,
701 Self::Opendal(_c) => false,
702 Self::MySql(c) => c.user_managed,
703 Self::Postgres(c) => c.user_managed,
704 Self::SqlServer(c) => c.user_managed,
705 Self::MongoDb(c) => c.user_managed,
706 Self::ElasticSearch(c) => c.user_managed,
707 Self::OpenSearch(c) => c.user_managed,
708 Self::Nats(c) => c.user_managed,
709 Self::Mqtt(c) => c.user_managed,
710 Self::SchemaRegistry(c) => c.user_managed,
711 Self::Lakekeeper(c) => c.user_managed,
712 Self::Moat(_c) => false,
713 }
714 }
715
716 pub fn task_group(&self) -> TaskGroup {
717 use TaskGroup::*;
718 match self {
719 ServiceConfig::ComputeNode(_)
720 | ServiceConfig::MetaNode(_)
721 | ServiceConfig::Frontend(_)
722 | ServiceConfig::Compactor(_)
723 | ServiceConfig::Minio(_)
724 | ServiceConfig::Sqlite(_) => RisingWave,
725 ServiceConfig::Prometheus(_) | ServiceConfig::Grafana(_) | ServiceConfig::Tempo(_) => {
726 Observability
727 }
728 ServiceConfig::Opendal(_) | ServiceConfig::AwsS3(_) => RisingWave,
729 ServiceConfig::Moto(_) => RisingWave,
730 ServiceConfig::Kafka(_) | ServiceConfig::SchemaRegistry(_) => Kafka,
731 ServiceConfig::Pubsub(_) => Pubsub,
732 ServiceConfig::Pulsar(_) => Pulsar,
733 ServiceConfig::Redis(_) => Redis,
734 ServiceConfig::MySql(my_sql_config) => {
735 if matches!(my_sql_config.application, Application::Metastore) {
736 RisingWave
737 } else {
738 MySql
739 }
740 }
741 ServiceConfig::Postgres(postgres_config) => {
742 if matches!(postgres_config.application, Application::Metastore) {
743 RisingWave
744 } else {
745 Postgres
746 }
747 }
748 ServiceConfig::SqlServer(_) => SqlServer,
749 ServiceConfig::MongoDb(_) => MongoDb,
750 ServiceConfig::ElasticSearch(_) => ElasticSearch,
751 ServiceConfig::OpenSearch(_) => OpenSearch,
752 ServiceConfig::Nats(_) => Nats,
753 ServiceConfig::Mqtt(_) => Mqtt,
754 ServiceConfig::Lakekeeper(_) => Lakekeeper,
755 ServiceConfig::Moat(_) => Moat,
756 }
757 }
758}
759
760mod string {
761 use std::fmt::Display;
762 use std::str::FromStr;
763
764 use serde::{Deserialize, Deserializer, Serializer, de};
765
766 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
767 where
768 T: Display,
769 S: Serializer,
770 {
771 serializer.collect_str(value)
772 }
773
774 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
775 where
776 T: FromStr,
777 T::Err: Display,
778 D: Deserializer<'de>,
779 {
780 String::deserialize(deserializer)?
781 .parse()
782 .map_err(de::Error::custom)
783 }
784}