-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathbuild.rs
More file actions
139 lines (120 loc) · 4.45 KB
/
Copy pathbuild.rs
File metadata and controls
139 lines (120 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright AGNTCY Contributors (https://github.com/agntcy)
// SPDX-License-Identifier: Apache-2.0
use std::fmt::Write as _;
fn main() {
generate_schema(&std::env::var("CARGO_MANIFEST_DIR").unwrap());
}
// ─── Schema types for PRAGMA introspection ────────────────────────────────────
#[derive(diesel::QueryableByName)]
struct TableName {
#[diesel(sql_type = diesel::sql_types::Text)]
name: String,
}
#[derive(diesel::QueryableByName)]
#[allow(dead_code)]
struct ColumnInfo {
#[diesel(sql_type = diesel::sql_types::Integer)]
cid: i32,
#[diesel(sql_type = diesel::sql_types::Text)]
name: String,
#[diesel(sql_type = diesel::sql_types::Text, column_name = "type")]
col_type: String,
#[diesel(sql_type = diesel::sql_types::Integer)]
notnull: i32,
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Text>)]
dflt_value: Option<String>,
#[diesel(sql_type = diesel::sql_types::Integer)]
pk: i32,
}
/// Map a SQLite column type string to its Diesel schema type.
/// Follows the same rules as `diesel print-schema` for SQLite.
fn sqlite_type_to_diesel(type_name: &str) -> &'static str {
let t = type_name.to_lowercase();
if t.contains("bool") {
"Bool"
} else if t == "integer" || t == "int" || t.contains("tiny") || t == "int4" {
"Integer"
} else if t.contains("int") {
// bigint, int8, unsigned big int, …
"BigInt"
// spellchecker:off
} else if t.contains("real") || t.contains("floa") || t.contains("doub") {
// spellchecker:on
"Double"
} else if t.contains("blob") || t == "binary" {
"Binary"
} else {
// TEXT, VARCHAR, CHAR, CLOB, and the empty-type SQLite default
"Text"
}
}
fn generate_schema(manifest_dir: &str) {
use diesel::connection::SimpleConnection as _;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
println!("cargo:rerun-if-changed=src/db/sqlite/migrations/");
let out_dir = std::env::var("OUT_DIR").unwrap();
let db_path = format!("{out_dir}/schema_gen.db");
let _ = std::fs::remove_file(&db_path);
let up_sql = std::fs::read_to_string(format!(
"{manifest_dir}/src/db/sqlite/migrations/00000000000000_initial/up.sql"
))
.expect("failed to read up.sql");
let mut conn =
SqliteConnection::establish(&db_path).expect("failed to open schema generation db");
conn.batch_execute(&up_sql)
.expect("failed to apply schema DDL");
let tables: Vec<TableName> = diesel::sql_query(
"SELECT name FROM sqlite_master \
WHERE type='table' AND name NOT LIKE 'sqlite_%' \
ORDER BY name",
)
.load(&mut conn)
.expect("failed to list tables");
let mut out = String::new();
writeln!(
out,
"// @generated — do not edit manually; regenerated from migrations/ on every build"
)
.unwrap();
writeln!(
out,
"// Copyright AGNTCY Contributors (https://github.com/agntcy)"
)
.unwrap();
writeln!(out, "// SPDX-License-Identifier: Apache-2.0").unwrap();
writeln!(out).unwrap();
for table in &tables {
let cols: Vec<ColumnInfo> =
diesel::sql_query(format!("PRAGMA table_info(\"{}\")", table.name))
.load(&mut conn)
.expect("failed to query table_info");
// Collect primary key columns in order.
let mut pk_cols: Vec<(i32, &str)> = cols
.iter()
.filter(|c| c.pk > 0)
.map(|c| (c.pk, c.name.as_str()))
.collect();
pk_cols.sort_by_key(|(pk, _)| *pk);
let pk_list = pk_cols
.iter()
.map(|(_, n)| *n)
.collect::<Vec<_>>()
.join(", ");
writeln!(out, "diesel::table! {{").unwrap();
writeln!(out, " {} ({}) {{", table.name, pk_list).unwrap();
for col in &cols {
let inner = sqlite_type_to_diesel(&col.col_type);
let diesel_type = if col.notnull == 1 {
inner.to_string()
} else {
format!("Nullable<{inner}>")
};
writeln!(out, " {} -> {},", col.name, diesel_type).unwrap();
}
writeln!(out, " }}").unwrap();
writeln!(out, "}}").unwrap();
writeln!(out).unwrap();
}
std::fs::write(format!("{out_dir}/schema.rs"), out).expect("failed to write schema.rs");
}