Skip to main content

rustauth_plugins/siwe/
schema.rs

1use indexmap::IndexMap;
2use rustauth_core::db::{DbField, DbFieldType, DbTable, ForeignKey, OnDelete};
3use rustauth_core::plugin::PluginSchemaContribution;
4
5#[derive(Debug, Clone, Default, PartialEq, Eq)]
6pub struct SiweSchemaOptions {
7    table_name: Option<String>,
8    field_names: IndexMap<String, String>,
9}
10
11impl SiweSchemaOptions {
12    pub fn new() -> Self {
13        Self::default()
14    }
15
16    #[must_use]
17    pub fn table_name(mut self, table_name: impl Into<String>) -> Self {
18        self.table_name = Some(table_name.into());
19        self
20    }
21
22    #[must_use]
23    pub fn field_name(
24        mut self,
25        logical_name: impl Into<String>,
26        db_name: impl Into<String>,
27    ) -> Self {
28        self.field_names.insert(
29            normalize_logical_field(&logical_name.into()),
30            db_name.into(),
31        );
32        self
33    }
34
35    fn table_name_or_default(&self) -> String {
36        self.table_name
37            .clone()
38            .unwrap_or_else(|| "wallet_addresses".to_owned())
39    }
40
41    fn field_name_or_default(&self, logical_name: &str) -> String {
42        self.field_names
43            .get(logical_name)
44            .cloned()
45            .unwrap_or_else(|| logical_name.to_owned())
46    }
47
48    pub(crate) fn metadata(&self) -> serde_json::Value {
49        let mut fields = serde_json::Map::new();
50        for logical_name in ["user_id", "address", "chain_id", "is_primary", "created_at"] {
51            if let Some(db_name) = self.field_names.get(logical_name) {
52                fields.insert(
53                    metadata_field_key(logical_name),
54                    serde_json::Value::String(db_name.clone()),
55                );
56            }
57        }
58        serde_json::json!({
59            "walletAddress": {
60                "modelName": self.table_name_or_default(),
61                "fields": fields,
62            }
63        })
64    }
65}
66
67fn normalize_logical_field(logical_name: &str) -> String {
68    match logical_name {
69        "userId" => "user_id".to_owned(),
70        "chainId" => "chain_id".to_owned(),
71        "isPrimary" => "is_primary".to_owned(),
72        "createdAt" => "created_at".to_owned(),
73        other => other.to_owned(),
74    }
75}
76
77fn metadata_field_key(logical_name: &str) -> String {
78    match logical_name {
79        "user_id" => "userId".to_owned(),
80        "chain_id" => "chainId".to_owned(),
81        "is_primary" => "isPrimary".to_owned(),
82        "created_at" => "createdAt".to_owned(),
83        other => other.to_owned(),
84    }
85}
86
87pub(crate) fn wallet_address_schema(options: &SiweSchemaOptions) -> PluginSchemaContribution {
88    let mut fields = IndexMap::new();
89    fields.insert(
90        "id".to_owned(),
91        DbField::new("id", DbFieldType::String).generated(),
92    );
93    fields.insert(
94        "user_id".to_owned(),
95        DbField::new(
96            options.field_name_or_default("user_id"),
97            DbFieldType::String,
98        )
99        .indexed()
100        .references(ForeignKey::new("users", "id", OnDelete::Cascade)),
101    );
102    fields.insert(
103        "address".to_owned(),
104        DbField::new(
105            options.field_name_or_default("address"),
106            DbFieldType::String,
107        ),
108    );
109    fields.insert(
110        "chain_id".to_owned(),
111        DbField::new(
112            options.field_name_or_default("chain_id"),
113            DbFieldType::Number,
114        ),
115    );
116    fields.insert(
117        "is_primary".to_owned(),
118        DbField::new(
119            options.field_name_or_default("is_primary"),
120            DbFieldType::Boolean,
121        ),
122    );
123    fields.insert(
124        "created_at".to_owned(),
125        DbField::new(
126            options.field_name_or_default("created_at"),
127            DbFieldType::Timestamp,
128        )
129        .generated(),
130    );
131
132    PluginSchemaContribution::table(
133        "wallet_address",
134        DbTable {
135            name: options.table_name_or_default(),
136            fields,
137            order: Some(20),
138        },
139    )
140}