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    pub path: PathBuf,
115    #[serde(default = "default_parallel_batches")]
116    pub parallel_batches: u32,
117}
118
119fn default_enabled() -> bool {
120    true
121}
122
123fn default_parallel_batches() -> u32 {
124    4
125}
126
127fn default_catalog_schema() -> ResourceConfig {
128    ResourceConfig {
129        enabled: true,
130        path: PathBuf::from("catalogs/"),
131    }
132}
133
134fn default_catalog_items() -> CatalogItemsConfig {
135    CatalogItemsConfig {
136        enabled: true,
137        path: PathBuf::from("catalogs/"),
138        parallel_batches: 4,
139    }
140}
141
142fn default_content_block() -> ResourceConfig {
143    ResourceConfig {
144        enabled: true,
145        path: PathBuf::from("content_blocks/"),
146    }
147}
148
149fn default_email_template() -> ResourceConfig {
150    ResourceConfig {
151        enabled: true,
152        path: PathBuf::from("email_templates/"),
153    }
154}
155
156fn default_custom_attribute() -> ResourceConfig {
157    ResourceConfig {
158        enabled: true,
159        path: PathBuf::from("custom_attributes/registry.yaml"),
160    }
161}
162
163#[derive(Debug, Clone, Default, Deserialize)]
164#[serde(deny_unknown_fields)]
165pub struct NamingConfig {
166    #[serde(default)]
167    pub catalog_name_pattern: Option<String>,
168    #[serde(default)]
169    pub content_block_name_pattern: Option<String>,
170    #[serde(default)]
171    pub custom_attribute_name_pattern: Option<String>,
172}