|
| 1 | +//! Metadata information regarding the database and tracked information. |
| 2 | +
|
| 3 | +// This code was copied from a different place that predated the introduction of clippy to the |
| 4 | +// project. Therefore we disregard certain clippy lints: |
| 5 | +#![allow( |
| 6 | + clippy::enum_variant_names, |
| 7 | + clippy::upper_case_acronyms, |
| 8 | + clippy::wrong_self_convention |
| 9 | +)] |
| 10 | +use schemars::JsonSchema; |
| 11 | +use serde::{Deserialize, Serialize}; |
| 12 | +use std::collections::{BTreeMap, BTreeSet}; |
| 13 | + |
| 14 | +/// Map of all known composite types. |
| 15 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 16 | +#[serde(rename_all = "camelCase")] |
| 17 | +pub struct CompositeTypes(pub BTreeMap<String, CompositeType>); |
| 18 | + |
| 19 | +/// A Scalar Type. |
| 20 | +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)] |
| 21 | +#[serde(rename_all = "camelCase")] |
| 22 | +pub struct ScalarType(pub String); |
| 23 | + |
| 24 | +/// The type of values that a column, field, or argument may take. |
| 25 | +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema)] |
| 26 | +#[serde(rename_all = "camelCase")] |
| 27 | +pub enum Type { |
| 28 | + ScalarType(ScalarType), |
| 29 | + CompositeType(String), |
| 30 | + ArrayType(Box<Type>), |
| 31 | +} |
| 32 | + |
| 33 | +/// Information about a composite type. These are very similar to tables, but with the crucial |
| 34 | +/// difference that composite types do not support constraints (such as NOT NULL). |
| 35 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 36 | +#[serde(rename_all = "camelCase")] |
| 37 | +pub struct CompositeType { |
| 38 | + pub name: String, |
| 39 | + pub fields: BTreeMap<String, FieldInfo>, |
| 40 | + #[serde(default)] |
| 41 | + pub description: Option<String>, |
| 42 | +} |
| 43 | + |
| 44 | +/// Information about a composite type field. |
| 45 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 46 | +#[serde(rename_all = "camelCase")] |
| 47 | +pub struct FieldInfo { |
| 48 | + pub name: String, |
| 49 | + pub r#type: Type, |
| 50 | + #[serde(default)] |
| 51 | + pub description: Option<String>, |
| 52 | +} |
| 53 | + |
| 54 | +/// The complete list of supported binary operators for scalar types. |
| 55 | +/// Not all of these are supported for every type. |
| 56 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 57 | +#[serde(rename_all = "camelCase")] |
| 58 | +pub struct ComparisonOperators(pub BTreeMap<ScalarType, BTreeMap<String, ComparisonOperator>>); |
| 59 | + |
| 60 | +/// Represents a postgres binary comparison operator |
| 61 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 62 | +#[serde(rename_all = "camelCase")] |
| 63 | +pub struct ComparisonOperator { |
| 64 | + pub operator_name: String, |
| 65 | + pub operator_kind: OperatorKind, |
| 66 | + pub argument_type: ScalarType, |
| 67 | + |
| 68 | + #[serde(default = "default_true")] |
| 69 | + pub is_infix: bool, |
| 70 | +} |
| 71 | + |
| 72 | +/// Is it a built-in operator, or a custom operator. |
| 73 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 74 | +#[serde(rename_all = "camelCase")] |
| 75 | +pub enum OperatorKind { |
| 76 | + Equal, |
| 77 | + In, |
| 78 | + Custom, |
| 79 | +} |
| 80 | + |
| 81 | +/// This is quite unfortunate: https://github.com/serde-rs/serde/issues/368 |
| 82 | +/// TL;DR: we can't set default literals for serde, so if we want 'is_infix' to |
| 83 | +/// default to 'true', we have to set its default as a function that returns 'true'. |
| 84 | +fn default_true() -> bool { |
| 85 | + true |
| 86 | +} |
| 87 | + |
| 88 | +/// Mapping from a "table" name to its information. |
| 89 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 90 | +#[serde(rename_all = "camelCase")] |
| 91 | +pub struct TablesInfo(pub BTreeMap<String, TableInfo>); |
| 92 | + |
| 93 | +/// Information about a database table (or any other kind of relation). |
| 94 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 95 | +#[serde(rename_all = "camelCase")] |
| 96 | +pub struct TableInfo { |
| 97 | + pub schema_name: String, |
| 98 | + pub table_name: String, |
| 99 | + pub columns: BTreeMap<String, ColumnInfo>, |
| 100 | + #[serde(default)] |
| 101 | + pub uniqueness_constraints: UniquenessConstraints, |
| 102 | + #[serde(default)] |
| 103 | + pub foreign_relations: ForeignRelations, |
| 104 | + #[serde(default)] |
| 105 | + pub description: Option<String>, |
| 106 | +} |
| 107 | + |
| 108 | +/// Can this column contain null values |
| 109 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 110 | +#[serde(rename_all = "camelCase")] |
| 111 | +pub enum Nullable { |
| 112 | + #[default] |
| 113 | + Nullable, |
| 114 | + NonNullable, |
| 115 | +} |
| 116 | + |
| 117 | +/// Does this column have a default value. |
| 118 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 119 | +#[serde(rename_all = "camelCase")] |
| 120 | +pub enum HasDefault { |
| 121 | + #[default] |
| 122 | + NoDefault, |
| 123 | + HasDefault, |
| 124 | +} |
| 125 | + |
| 126 | +/// Is this column an identity column. |
| 127 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 128 | +#[serde(rename_all = "camelCase")] |
| 129 | +pub enum IsIdentity { |
| 130 | + #[default] |
| 131 | + NotIdentity, |
| 132 | + IdentityByDefault, |
| 133 | + IdentityAlways, |
| 134 | +} |
| 135 | + |
| 136 | +/// Is this column a generated column. |
| 137 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 138 | +#[serde(rename_all = "camelCase")] |
| 139 | +pub enum IsGenerated { |
| 140 | + #[default] |
| 141 | + NotGenerated, |
| 142 | + Stored, |
| 143 | +} |
| 144 | + |
| 145 | +/// Information about a database column. |
| 146 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 147 | +#[serde(rename_all = "camelCase")] |
| 148 | +pub struct ColumnInfo { |
| 149 | + pub name: String, |
| 150 | + pub r#type: Type, |
| 151 | + #[serde(default)] |
| 152 | + pub nullable: Nullable, |
| 153 | + #[serde(skip_serializing_if = "does_not_have_default")] |
| 154 | + #[serde(default)] |
| 155 | + pub has_default: HasDefault, |
| 156 | + #[serde(skip_serializing_if = "is_not_identity")] |
| 157 | + #[serde(default)] |
| 158 | + pub is_identity: IsIdentity, |
| 159 | + #[serde(skip_serializing_if = "is_not_generated")] |
| 160 | + #[serde(default)] |
| 161 | + pub is_generated: IsGenerated, |
| 162 | + #[serde(default)] |
| 163 | + pub description: Option<String>, |
| 164 | +} |
| 165 | + |
| 166 | +fn does_not_have_default(has_default: &HasDefault) -> bool { |
| 167 | + matches!(has_default, HasDefault::NoDefault) |
| 168 | +} |
| 169 | + |
| 170 | +fn is_not_identity(is_identity: &IsIdentity) -> bool { |
| 171 | + matches!(is_identity, IsIdentity::NotIdentity) |
| 172 | +} |
| 173 | + |
| 174 | +fn is_not_generated(is_generated: &IsGenerated) -> bool { |
| 175 | + matches!(is_generated, IsGenerated::NotGenerated) |
| 176 | +} |
| 177 | + |
| 178 | +/// A mapping from the name of a unique constraint to its value. |
| 179 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 180 | +#[serde(rename_all = "camelCase")] |
| 181 | +pub struct UniquenessConstraints(pub BTreeMap<String, UniquenessConstraint>); |
| 182 | + |
| 183 | +/// The set of columns that make up a uniqueness constraint. |
| 184 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 185 | +#[serde(rename_all = "camelCase")] |
| 186 | +pub struct UniquenessConstraint(pub BTreeSet<String>); |
| 187 | + |
| 188 | +/// A mapping from the name of a foreign key constraint to its value. |
| 189 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 190 | +#[serde(rename_all = "camelCase")] |
| 191 | +pub struct ForeignRelations(pub BTreeMap<String, ForeignRelation>); |
| 192 | + |
| 193 | +/// A foreign key constraint. |
| 194 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 195 | +#[serde(rename_all = "camelCase")] |
| 196 | +pub struct ForeignRelation { |
| 197 | + #[serde(skip_serializing_if = "Option::is_none")] |
| 198 | + pub foreign_schema: Option<String>, |
| 199 | + pub foreign_table: String, |
| 200 | + pub column_mapping: BTreeMap<String, String>, |
| 201 | +} |
| 202 | + |
| 203 | +/// All supported aggregate functions, grouped by type. |
| 204 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 205 | +#[serde(rename_all = "camelCase")] |
| 206 | +pub struct AggregateFunctions(pub BTreeMap<ScalarType, BTreeMap<String, AggregateFunction>>); |
| 207 | + |
| 208 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 209 | +#[serde(rename_all = "camelCase")] |
| 210 | +pub struct AggregateFunction { |
| 211 | + pub return_type: ScalarType, |
| 212 | +} |
| 213 | + |
| 214 | +/// Type representation of scalar types, grouped by type. |
| 215 | +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)] |
| 216 | +#[serde(rename_all = "camelCase")] |
| 217 | +pub struct TypeRepresentations(pub BTreeMap<ScalarType, TypeRepresentation>); |
| 218 | + |
| 219 | +/// Type representation of a scalar type. |
| 220 | +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] |
| 221 | +#[serde(rename_all = "camelCase")] |
| 222 | +pub enum TypeRepresentation { |
| 223 | + /// JSON booleans |
| 224 | + Boolean, |
| 225 | + /// Any JSON string |
| 226 | + String, |
| 227 | + /// float4 |
| 228 | + Float32, |
| 229 | + /// float8 |
| 230 | + Float64, |
| 231 | + /// int2 |
| 232 | + Int16, |
| 233 | + /// int4 |
| 234 | + Int32, |
| 235 | + /// int8 |
| 236 | + Int64, |
| 237 | + /// numeric |
| 238 | + BigDecimal, |
| 239 | + /// timestamp |
| 240 | + Timestamp, |
| 241 | + /// timestamp with timezone |
| 242 | + Timestamptz, |
| 243 | + /// time |
| 244 | + Time, |
| 245 | + /// time with timezone |
| 246 | + Timetz, |
| 247 | + /// date |
| 248 | + Date, |
| 249 | + /// uuid |
| 250 | + UUID, |
| 251 | + /// geography |
| 252 | + Geography, |
| 253 | + /// geometry |
| 254 | + Geometry, |
| 255 | + /// Any JSON number |
| 256 | + Number, |
| 257 | + /// Any JSON number, with no decimal part |
| 258 | + Integer, |
| 259 | + /// An arbitrary json. |
| 260 | + Json, |
| 261 | + /// One of the specified string values |
| 262 | + Enum(Vec<String>), |
| 263 | +} |
| 264 | + |
| 265 | +// tests |
| 266 | + |
| 267 | +#[cfg(test)] |
| 268 | +mod tests { |
| 269 | + use super::{ScalarType, TypeRepresentation, TypeRepresentations}; |
| 270 | + |
| 271 | + #[test] |
| 272 | + fn parse_type_representations() { |
| 273 | + assert_eq!( |
| 274 | + serde_json::from_str::<TypeRepresentations>( |
| 275 | + r#"{"int4": "integer", "card_suit": {"enum": ["hearts", "clubs", "diamonds", "spades"]}}"# |
| 276 | + ) |
| 277 | + .unwrap(), |
| 278 | + TypeRepresentations( |
| 279 | + [( |
| 280 | + ScalarType("int4".to_string()), |
| 281 | + TypeRepresentation::Integer |
| 282 | + ), ( |
| 283 | + ScalarType("card_suit".to_string()), |
| 284 | + TypeRepresentation::Enum(vec![ |
| 285 | + "hearts".into(), |
| 286 | + "clubs".into(), |
| 287 | + "diamonds".into(), |
| 288 | + "spades".into() |
| 289 | + ]) |
| 290 | + )] |
| 291 | + .into() |
| 292 | + ) |
| 293 | + ); |
| 294 | + } |
| 295 | +} |
0 commit comments