Skip to main content

braze_sync/config/
schema.rs

1//! Raw configuration types deserialized from `braze-sync.config.yaml`.
2//!
3//! See IMPLEMENTATION.md §10. Every struct here uses
4//! `#[serde(deny_unknown_fields)]` — the config file is the **only** place in
5//! braze-sync where unknown fields are rejected. Resource files
6//! (`schema.yaml`, `template.yaml`, etc.) stay forward-compat permissive
7//! per §2.5.
8
9use serde::Deserialize;
10use std::collections::BTreeMap;
11use std::path::PathBuf;
12use url::Url;
13
14#[derive(Debug, Clone, Deserialize)]
15#[serde(deny_unknown_fields)]
16pub struct ConfigFile {
17    /// Schema version. v1.0 binaries accept exactly `1`. Bumping this is a
18    /// breaking event by design.
19    pub version: u32,
20    pub default_environment: String,
21    #[serde(default)]
22    pub defaults: Defaults,
23    pub environments: BTreeMap<String, EnvironmentConfig>,
24    #[serde(default)]
25    pub resources: ResourcesConfig,
26    #[serde(default)]
27    pub naming: NamingConfig,
28}
29
30#[derive(Debug, Clone, Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct Defaults {
33    #[serde(default = "default_rate_limit")]
34    pub rate_limit_per_minute: u32,
35}
36
37impl Default for Defaults {
38    fn default() -> Self {
39        Self {
40            rate_limit_per_minute: default_rate_limit(),
41        }
42    }
43}
44
45fn default_rate_limit() -> u32 {
46    40
47}
48
49#[derive(Debug, Clone, Deserialize)]
50#[serde(deny_unknown_fields)]
51pub struct EnvironmentConfig {
52    pub api_endpoint: Url,
53    /// Name of the environment variable holding the Braze API key. The key
54    /// itself MUST NOT live in this file (§2.3 / §10).
55    pub api_key_env: String,
56    /// Per-environment override of `defaults.rate_limit_per_minute`.
57    #[serde(default)]
58    pub rate_limit_per_minute: Option<u32>,
59}
60
61#[derive(Debug, Clone, Deserialize)]
62#[serde(deny_unknown_fields)]
63pub struct ResourcesConfig {
64    #[serde(default = "default_catalog_schema")]
65    pub catalog_schema: ResourceConfig,
66    #[serde(default = "default_catalog_items")]
67    pub catalog_items: CatalogItemsConfig,
68    #[serde(default = "default_content_block")]
69    pub content_block: ResourceConfig,
70    #[serde(default = "default_email_template")]
71    pub email_template: ResourceConfig,
72    #[serde(default = "default_custom_attribute")]
73    pub custom_attribute: ResourceConfig,
74}
75
76impl ResourcesConfig {
77    pub fn is_enabled(&self, kind: crate::resource::ResourceKind) -> bool {
78        use crate::resource::ResourceKind;
79        match kind {
80            ResourceKind::CatalogSchema => self.catalog_schema.enabled,
81            ResourceKind::CatalogItems => self.catalog_items.enabled,
82            ResourceKind::ContentBlock => self.content_block.enabled,
83            ResourceKind::EmailTemplate => self.email_template.enabled,
84            ResourceKind::CustomAttribute => self.custom_attribute.enabled,
85        }
86    }
87}
88
89impl Default for ResourcesConfig {
90    fn default() -> Self {
91        Self {
92            catalog_schema: default_catalog_schema(),
93            catalog_items: default_catalog_items(),
94            content_block: default_content_block(),
95            email_template: default_email_template(),
96            custom_attribute: default_custom_attribute(),
97        }
98    }
99}
100
101#[derive(Debug, Clone, Deserialize)]
102#[serde(deny_unknown_fields)]
103pub struct ResourceConfig {
104    #[serde(default = "default_enabled")]
105    pub enabled: bool,
106    pub path: PathBuf,
107}
108
109#[derive(Debug, Clone, Deserialize)]
110#[serde(deny_unknown_fields)]
111pub struct CatalogItemsConfig {
112    #[serde(default = "default_enabled")]
113    pub enabled: bool,
114    #[serde(default = "default_parallel_batches")]
115    pub parallel_batches: u32,
116}
117
118fn default_enabled() -> bool {
119    true
120}
121
122fn default_parallel_batches() -> u32 {
123    4
124}
125
126fn default_catalog_schema() -> ResourceConfig {
127    ResourceConfig {
128        enabled: true,
129        path: PathBuf::from("catalogs/"),
130    }
131}
132
133fn default_catalog_items() -> CatalogItemsConfig {
134    CatalogItemsConfig {
135        enabled: true,
136        parallel_batches: 4,
137    }
138}
139
140fn default_content_block() -> ResourceConfig {
141    ResourceConfig {
142        enabled: true,
143        path: PathBuf::from("content_blocks/"),
144    }
145}
146
147fn default_email_template() -> ResourceConfig {
148    ResourceConfig {
149        enabled: true,
150        path: PathBuf::from("email_templates/"),
151    }
152}
153
154fn default_custom_attribute() -> ResourceConfig {
155    ResourceConfig {
156        enabled: true,
157        path: PathBuf::from("custom_attributes/registry.yaml"),
158    }
159}
160
161#[derive(Debug, Clone, Default, Deserialize)]
162#[serde(deny_unknown_fields)]
163pub struct NamingConfig {
164    #[serde(default)]
165    pub catalog_name_pattern: Option<String>,
166    #[serde(default)]
167    pub content_block_name_pattern: Option<String>,
168    #[serde(default)]
169    pub custom_attribute_name_pattern: Option<String>,
170}