forked from prisma/prisma1
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request prisma#4832 from prisma/feature/rust-introspection…
…-model Implement generating of data models in Rust
- Loading branch information
Showing
9 changed files
with
243 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "introspection-command" | ||
version = "0.1.0" | ||
authors = ["Arve Knudsen <arve.knudsen@gmail.com>"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
database-introspection = { path = "../../libs/database-introspection" } | ||
failure = "0.1" | ||
datamodel = { path = "../../libs/datamodel" } | ||
log = "0.4" | ||
|
||
[dev-dependencies] | ||
fern = "0.5" | ||
pretty_assertions = "0.6" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Prisma Data Model v2 Generator from Introspection Results | ||
|
||
Language: Rust | ||
|
||
Build System: Cargo | ||
|
||
## Overview | ||
|
||
This package is responsible for generating a Prisma data model from a set of introspection | ||
results, as obtained via the database-introspection package. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#![warn(missing_docs)] | ||
//! Logic for generating Prisma data models from database introspection. | ||
use database_introspection::{ColumnArity, ColumnTypeFamily, DatabaseSchema}; | ||
use datamodel::{common::PrismaType, Datamodel, Field, FieldArity, FieldType, Model}; | ||
use failure::Error; | ||
use log::debug; | ||
|
||
/// The result type. | ||
pub type Result<T> = core::result::Result<T, Error>; | ||
|
||
/// Calculate a data model from a database schema. | ||
pub fn calculate_model(schema: &DatabaseSchema) -> Result<Datamodel> { | ||
debug!("Calculating data model"); | ||
|
||
let mut data_model = Datamodel::new(); | ||
for table in schema.tables.iter() { | ||
let mut model = Model::new(&table.name); | ||
for column in table.columns.iter() { | ||
debug!("Handling column {:?}", column); | ||
let field_type = match column.tpe.family { | ||
ColumnTypeFamily::Boolean => FieldType::Base(PrismaType::Boolean), | ||
ColumnTypeFamily::DateTime => FieldType::Base(PrismaType::DateTime), | ||
ColumnTypeFamily::Float => FieldType::Base(PrismaType::Float), | ||
ColumnTypeFamily::Int => FieldType::Base(PrismaType::Int), | ||
ColumnTypeFamily::String => FieldType::Base(PrismaType::String), | ||
// XXX: We made a conscious decision to punt on mapping of ColumnTypeFamily | ||
// variants that don't yet have corresponding PrismaType variants | ||
_ => FieldType::Base(PrismaType::String), | ||
}; | ||
let arity = match column.arity { | ||
ColumnArity::Required => FieldArity::Required, | ||
ColumnArity::Nullable => FieldArity::Optional, | ||
ColumnArity::List => FieldArity::List, | ||
}; | ||
let field = Field { | ||
name: column.name.clone(), | ||
arity, | ||
field_type, | ||
database_name: None, | ||
default_value: None, | ||
is_unique: false, | ||
id_info: None, | ||
scalar_list_strategy: None, | ||
documentation: None, | ||
is_generated: false, | ||
is_updated_at: false, | ||
}; | ||
model.add_field(field); | ||
} | ||
data_model.add_model(model); | ||
} | ||
Ok(data_model) | ||
} |
126 changes: 126 additions & 0 deletions
126
server/prisma-rs/libs/introspection-command/tests/introspection_command_tests.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
use database_introspection::*; | ||
use datamodel::{common::PrismaType, Datamodel, Field, FieldArity, FieldType, Model}; | ||
use introspection_command::calculate_model; | ||
use log::LevelFilter; | ||
use pretty_assertions::assert_eq; | ||
use std::sync::atomic::{AtomicBool, Ordering}; | ||
|
||
static IS_SETUP: AtomicBool = AtomicBool::new(false); | ||
|
||
fn setup() { | ||
let is_setup = IS_SETUP.load(Ordering::Relaxed); | ||
if is_setup { | ||
return; | ||
} | ||
|
||
let log_level = match std::env::var("TEST_LOG") | ||
.unwrap_or("warn".to_string()) | ||
.to_lowercase() | ||
.as_ref() | ||
{ | ||
"trace" => LevelFilter::Trace, | ||
"debug" => LevelFilter::Debug, | ||
"info" => LevelFilter::Info, | ||
"warn" => LevelFilter::Warn, | ||
"error" => LevelFilter::Error, | ||
_ => LevelFilter::Warn, | ||
}; | ||
fern::Dispatch::new() | ||
.format(|out, message, record| { | ||
out.finish(format_args!("[{}][{}] {}", record.target(), record.level(), message)) | ||
}) | ||
.level(log_level) | ||
.chain(std::io::stdout()) | ||
.apply() | ||
.expect("fern configuration"); | ||
|
||
IS_SETUP.store(true, Ordering::Relaxed); | ||
} | ||
|
||
#[test] | ||
fn a_data_model_can_be_generated_from_a_schema() { | ||
setup(); | ||
|
||
let col_types = vec![ | ||
ColumnTypeFamily::Int, | ||
ColumnTypeFamily::Float, | ||
ColumnTypeFamily::Boolean, | ||
ColumnTypeFamily::String, | ||
ColumnTypeFamily::DateTime, | ||
ColumnTypeFamily::Binary, | ||
ColumnTypeFamily::Json, | ||
ColumnTypeFamily::Uuid, | ||
ColumnTypeFamily::Geometric, | ||
ColumnTypeFamily::LogSequenceNumber, | ||
ColumnTypeFamily::TextSearch, | ||
ColumnTypeFamily::TransactionId, | ||
]; | ||
|
||
let ref_data_model = Datamodel { | ||
models: vec![Model { | ||
database_name: None, | ||
name: "Table1".to_string(), | ||
documentation: None, | ||
is_embedded: false, | ||
is_generated: false, | ||
fields: col_types | ||
.iter() | ||
.map(|col_type| { | ||
let field_type = match col_type { | ||
ColumnTypeFamily::Boolean => FieldType::Base(PrismaType::Boolean), | ||
ColumnTypeFamily::DateTime => FieldType::Base(PrismaType::DateTime), | ||
ColumnTypeFamily::Float => FieldType::Base(PrismaType::Float), | ||
ColumnTypeFamily::Int => FieldType::Base(PrismaType::Int), | ||
ColumnTypeFamily::String => FieldType::Base(PrismaType::String), | ||
// XXX: We made a conscious decision to punt on mapping of ColumnTypeFamily | ||
// variants that don't yet have corresponding PrismaType variants | ||
_ => FieldType::Base(PrismaType::String), | ||
}; | ||
Field { | ||
name: col_type.to_string(), | ||
arity: FieldArity::Optional, | ||
field_type, | ||
database_name: None, | ||
default_value: None, | ||
is_unique: false, | ||
id_info: None, | ||
scalar_list_strategy: None, | ||
documentation: None, | ||
is_generated: false, | ||
is_updated_at: false, | ||
} | ||
}) | ||
.collect(), | ||
}], | ||
enums: vec![], | ||
}; | ||
|
||
let schema = DatabaseSchema { | ||
tables: vec![Table { | ||
name: "Table1".to_string(), | ||
columns: col_types | ||
.iter() | ||
.map(|family| Column { | ||
name: family.to_string(), | ||
tpe: ColumnType { | ||
raw: "raw".to_string(), | ||
family: family.to_owned(), | ||
}, | ||
arity: ColumnArity::Nullable, | ||
default: None, | ||
auto_increment: false, | ||
}) | ||
.collect(), | ||
indices: vec![], | ||
primary_key: Some(PrimaryKey { | ||
columns: vec!["primary_col".to_string()], | ||
}), | ||
foreign_keys: vec![], | ||
}], | ||
enums: vec![], | ||
sequences: vec![], | ||
}; | ||
let data_model = calculate_model(&schema).expect("calculate data model"); | ||
|
||
assert_eq!(data_model, ref_data_model); | ||
} |