Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Julius de Bruijn committed Oct 21, 2022
1 parent 035425d commit 3ff55d7
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod field_type;
mod statistics;

use std::borrow::Cow;

use datamodel_renderer as render;
use futures::TryStreamExt;
use introspection_connector::{CompositeTypeDepth, IntrospectionContext, IntrospectionResult, Version};
Expand All @@ -10,6 +12,7 @@ use mongodb::{
Database,
};
use mongodb_schema_describer::MongoSchema;
use psl::dml;
pub(crate) use statistics::Name;
use statistics::*;

Expand Down Expand Up @@ -68,3 +71,71 @@ pub(super) async fn sample(
version: Version::NonPrisma,
})
}

fn render_datamodel(dml: &dml::Datamodel) -> String {
let mut data_model = render::Datamodel::new();

for dml_ct in dml.composite_types() {
let mut composite_type = render::datamodel::CompositeType::new(&dml_ct.name);

for dml_field in dml_ct.fields.iter() {
let (r#type, native_type) = match dml_field.r#type {
dml::CompositeTypeFieldType::CompositeType(ref ct) => (ct.as_str(), None),
dml::CompositeTypeFieldType::Scalar(ref st, ref nt) => {
(st.as_ref(), nt.as_ref().map(|nt| (nt.name(), nt.args())))
}
dml::CompositeTypeFieldType::Enum(ref s) => (s.as_str(), None),
dml::CompositeTypeFieldType::Unsupported(ref s) => (s.as_str(), None),
};

let mut field = match dml_field.arity {
_ if dml_field.r#type.is_unsupported() => {
render::datamodel::CompositeTypeField::new_unsupported(&dml_field.name, r#type)
}
dml::FieldArity::Required => {
render::datamodel::CompositeTypeField::new_required(&dml_field.name, r#type)
}
dml::FieldArity::Optional => {
render::datamodel::CompositeTypeField::new_optional(&dml_field.name, r#type)
}
dml::FieldArity::List => render::datamodel::CompositeTypeField::new_array(&dml_field.array, r#type),
};

if dml_field.is_commented_out {
field.commented_out();
}

if let Some(ref map) = dml_field.database_name {
field.map(map);
}

if let Some(ref dml_def) = dml_field.default_value {
let def_val = match &dml_def.kind {
dml::DefaultKind::Single(val) => match val {
dml::PrismaValue::String(ref val) => render::datamodel::DefaultValue::text(val),
dml::PrismaValue::Boolean(val) => render::datamodel::DefaultValue::constant(val.as_ref()),
dml::PrismaValue::Enum(enm) => render::datamodel::DefaultValue::constant(enm.as_str()),
dml::PrismaValue::Int(val) => render::datamodel::DefaultValue::constant(val.as_ref()),
dml::PrismaValue::Uuid(val) => render::datamodel::DefaultValue::constant(val.as_ref()),
dml::PrismaValue::List(_) => todo!(),
dml::PrismaValue::Json(_) => todo!(),
dml::PrismaValue::Xml(_) => todo!(),
dml::PrismaValue::Object(_) => todo!(),
dml::PrismaValue::Null => todo!(),
dml::PrismaValue::DateTime(_) => todo!(),
dml::PrismaValue::Float(_) => todo!(),
dml::PrismaValue::BigInt(_) => todo!(),
dml::PrismaValue::Bytes(_) => todo!(),
},
dml::DefaultKind::Expression(_) => todo!(),
};
}

composite_type.push_field(field);
}

data_model.push_composite_type(composite_type)
}

todo!()
}
16 changes: 16 additions & 0 deletions introspection-engine/datamodel-renderer/src/datamodel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use std::fmt;
#[derive(Default, Debug)]
pub struct Datamodel<'a> {
enums: Vec<Enum<'a>>,
composite_types: Vec<CompositeType<'a>>,
}

impl<'a> Datamodel<'a> {
Expand All @@ -34,10 +35,25 @@ impl<'a> Datamodel<'a> {
pub fn push_enum(&mut self, r#enum: Enum<'a>) {
self.enums.push(r#enum);
}

/// Add a composite type block to the data model.
///
/// ```ignore
/// type Address { // <
/// street String // < this
/// } // <
/// ```
pub fn push_composite_type(&mut self, r#enum: CompositeType<'a>) {
self.composite_types.push(r#enum);
}
}

impl<'a> fmt::Display for Datamodel<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for ct in self.composite_types.iter() {
writeln!(f, "{ct}")?;
}

for r#enum in self.enums.iter() {
writeln!(f, "{enum}")?;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ use std::fmt;

use crate::{
datamodel::DefaultValue,
value::{Constant, ConstantNameValidationError, Documentation, Function},
value::{Constant, ConstantNameValidationError, Documentation, Function, Text},
};

use super::attributes::FieldAttribute;

/// A type block in a PSL file.
#[derive(Debug)]
pub struct CompositeType<'a> {
name: Constant<'a>,
documentation: Option<Documentation<'a>>,
Expand Down Expand Up @@ -77,6 +78,7 @@ impl<'a> fmt::Display for CompositeType<'a> {
}

/// A type of a field in the datamodel.
#[derive(Debug)]
pub enum FieldType<'a> {
/// The field is required, rendered with only the name of the
/// type. For example: `Int`.
Expand All @@ -87,6 +89,9 @@ pub enum FieldType<'a> {
/// The field is an array, rendered with square brackets after the
/// type name. For example: `Int[]`.
Array(Constant<'a>),
/// The field is not supported by Prisma, rendered as
/// `Unsupported(ts_vector)`.
Unsupported(Text<&'a str>),
}

impl<'a> fmt::Display for FieldType<'a> {
Expand All @@ -101,11 +106,17 @@ impl<'a> fmt::Display for FieldType<'a> {
t.fmt(f)?;
f.write_str("[]")
}
FieldType::Unsupported(ref t) => {
f.write_str("Unsupported(")?;
t.fmt(f)?;
f.write_str(")")
}
}
}
}

/// A field in a composite type block.
#[derive(Debug)]
pub struct CompositeTypeField<'a> {
name: Constant<'a>,
r#type: FieldType<'a>,
Expand Down Expand Up @@ -156,6 +167,19 @@ impl<'a> CompositeTypeField<'a> {
Self::new(name, FieldType::Array(Constant::new_no_validate(type_name)))
}

/// Create a new unsupported composite field declaration.
///
/// ```ignore
/// type Address {
/// street Unsupported("foo")
/// // ^^^ type_name
/// //^^^^^^ name
/// }
/// ```
pub fn new_unsupported(name: &'a str, type_name: &'a str) -> Self {
Self::new(name, FieldType::Unsupported(Text(type_name)))
}

/// Sets the field map attribute.
///
/// ```ignore
Expand Down Expand Up @@ -225,9 +249,9 @@ impl<'a> CompositeTypeField<'a> {
fn new(name: &'a str, r#type: FieldType<'a>) -> Self {
let (name, map, commented_out) = match Constant::new(name) {
Ok(name) => (name, None, false),
Err(ConstantNameValidationError::WasSanitized { sanitized, original }) => {
Err(ConstantNameValidationError::WasSanitized { sanitized }) => {
let mut map = Function::new("map");
map.push_param(original);
map.push_param(name);

let map = FieldAttribute::new(map);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::value::{Constant, Function, Text, Value};
use super::attributes::FieldAttribute;

/// A field default value.
#[derive(Debug)]
pub struct DefaultValue<'a>(FieldAttribute<'a>);

impl<'a> DefaultValue<'a> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ impl<'a> EnumVariant<'a> {
let kind = EnumVariantKind::Valid(constant);
(kind, None)
}
Err(ConstantNameValidationError::WasSanitized { sanitized, original }) => {
Err(ConstantNameValidationError::WasSanitized { sanitized }) => {
let mut fun = Function::new("map");
fun.push_param(original);
fun.push_param(value);

let kind = EnumVariantKind::Valid(sanitized);

Expand Down Expand Up @@ -168,9 +168,9 @@ impl<'a> Enum<'a> {
pub fn new(name: &'a str) -> Self {
let (name, map) = match Constant::new(name) {
Ok(name) => (name, None),
Err(ConstantNameValidationError::WasSanitized { sanitized, original }) => {
Err(ConstantNameValidationError::WasSanitized { sanitized }) => {
let mut fun = Function::new("map");
fun.push_param(original);
fun.push_param(name);

(sanitized, Some(BlockAttribute(fun)))
}
Expand Down
14 changes: 6 additions & 8 deletions introspection-engine/datamodel-renderer/src/value/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ pub enum ConstantNameValidationError<'a> {
WasSanitized {
/// A sanitized value to be used as a valid constant.
sanitized: Constant<'a>,
/// The original value to be used in the corresponding `@@map`
/// or `@map` attributes, or the `map:` argument.
original: &'a str,
},
/// The given value was empty and cannot be used as a constant.
OriginalEmpty,
Expand All @@ -34,28 +31,29 @@ impl<'a> Constant<'a> {
/// actual input value. This input value must be used in a
/// corresponding `@map`, `@@map` or `map:` declaration, depending
/// on the position of the constant.
pub fn new(value: &'a str) -> Result<Self, ConstantNameValidationError<'a>> {
pub fn new(value: impl Into<Cow<'a, str>>) -> Result<Self, ConstantNameValidationError<'a>> {
static CONSTANT_START: Lazy<Regex> = Lazy::new(|| Regex::new("^[^a-zA-Z]+").unwrap());
static CONSTANT: Lazy<Regex> = Lazy::new(|| Regex::new("[^_a-zA-Z0-9]").unwrap());

let value = value.into();

if value.is_empty() {
Err(ConstantNameValidationError::OriginalEmpty)
} else if CONSTANT_START.is_match(value) || CONSTANT.is_match(value) {
let start_cleaned: String = CONSTANT_START.replace_all(value, "").parse().unwrap();
} else if CONSTANT_START.is_match(&value) || CONSTANT.is_match(&value) {
let start_cleaned: String = CONSTANT_START.replace_all(&value, "").parse().unwrap();
let sanitized: String = CONSTANT.replace_all(start_cleaned.as_str(), "_").parse().unwrap();

if !sanitized.is_empty() {
let err = ConstantNameValidationError::WasSanitized {
sanitized: Self(Cow::Owned(sanitized)),
original: value,
};

Err(err)
} else {
Err(ConstantNameValidationError::SanitizedEmpty)
}
} else {
Ok(Self(Cow::Borrowed(value)))
Ok(Self(value))
}
}

Expand Down
4 changes: 2 additions & 2 deletions introspection-engine/datamodel-renderer/src/value/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ impl<'a> Function<'a> {
Self { name, params }
}
// Will render `sanitized(map: "original")`
Err(ConstantNameValidationError::WasSanitized { sanitized, original }) => {
Err(ConstantNameValidationError::WasSanitized { sanitized }) => {
let mut fun = Self {
name: sanitized,
params: Vec::new(),
};

fun.push_param(("map", Text(original)));
fun.push_param(("map", Text(name)));
fun
}
// We just generate an invalid function in this case. It
Expand Down
4 changes: 4 additions & 0 deletions libs/dml/src/composite_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,8 @@ impl CompositeTypeFieldType {
None
}
}

pub fn is_unsupported(&self) -> bool {
matches!(self, Self::Unsupported(_))
}
}
31 changes: 19 additions & 12 deletions libs/dml/src/scalars.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use std::{fmt, str::FromStr};

/// Prisma's builtin scalar types.
#[derive(Debug, Copy, PartialEq, Clone, Serialize, Deserialize, Eq, Hash)]
Expand Down Expand Up @@ -39,6 +39,7 @@ impl ScalarType {

impl FromStr for ScalarType {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Int" => Ok(ScalarType::Int),
Expand All @@ -55,18 +56,24 @@ impl FromStr for ScalarType {
}
}

impl ToString for ScalarType {
fn to_string(&self) -> String {
impl AsRef<str> for ScalarType {
fn as_ref(&self) -> &str {
match self {
ScalarType::Int => String::from("Int"),
ScalarType::BigInt => String::from("BigInt"),
ScalarType::Float => String::from("Float"),
ScalarType::Boolean => String::from("Boolean"),
ScalarType::String => String::from("String"),
ScalarType::DateTime => String::from("DateTime"),
ScalarType::Json => String::from("Json"),
ScalarType::Bytes => String::from("Bytes"),
ScalarType::Decimal => String::from("Decimal"),
ScalarType::Int => "Int",
ScalarType::BigInt => "BigInt",
ScalarType::Float => "Float",
ScalarType::Boolean => "Boolean",
ScalarType::String => "String",
ScalarType::DateTime => "DateTime",
ScalarType::Json => "Json",
ScalarType::Bytes => "Bytes",
ScalarType::Decimal => "Decimal",
}
}
}

impl fmt::Display for ScalarType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
}

0 comments on commit 3ff55d7

Please sign in to comment.