Skip to content

Commit

Permalink
feat(psl): add support for renamed preview feature, add support for n…
Browse files Browse the repository at this point in the history
…ative preview features, add tests
  • Loading branch information
jkomyno committed Nov 26, 2024
1 parent 91fc22e commit 52b8ed7
Show file tree
Hide file tree
Showing 12 changed files with 385 additions and 79 deletions.
28 changes: 27 additions & 1 deletion psl/diagnostics/src/warning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use colored::{ColoredString, Colorize};
use indoc::indoc;
use std::fmt::Display;

/// A non-fatal warning emitted by the schema parser.
/// For fancy printing, please use the `pretty_print_error` function.
Expand All @@ -20,13 +21,38 @@ impl DatamodelWarning {
DatamodelWarning { message, span }
}

pub fn new_feature_deprecated(feature: &str, span: Span) -> DatamodelWarning {
pub fn new_preview_feature_deprecated(feature: &str, span: Span) -> DatamodelWarning {
let message = format!(
"Preview feature \"{feature}\" is deprecated. The functionality can be used without specifying it as a preview feature."
);
Self::new(message, span)
}

pub fn new_preview_feature_renamed(
deprecated_feature: &str,
renamed_feature: impl Display,
prisly_link_endpoint: &str,
span: Span,
) -> DatamodelWarning {
let message = format!(
"Preview feature \"{deprecated_feature}\" has been renamed as \"{renamed_feature}\". Learn more at https://pris.ly/d/{prisly_link_endpoint}."
);
Self::new(message, span)
}

pub fn new_preview_feature_renamed_for_provider(
provider: &'static str,
deprecated_feature: &str,
renamed_feature: impl Display,
prisly_link_endpoint: &str,
span: Span,
) -> DatamodelWarning {
let message = format!(
"On `provider = \"{provider}\"`, preview feature \"{deprecated_feature}\" has been renamed as \"{renamed_feature}\". Learn more at https://pris.ly/d/{prisly_link_endpoint}."
);
Self::new(message, span)
}

pub fn new_referential_integrity_attr_deprecation_warning(span: Span) -> DatamodelWarning {
let message = "The `referentialIntegrity` attribute is deprecated. Please use `relationMode` instead. Learn more at https://pris.ly/d/relation-mode";
Self::new(message.to_string(), span)
Expand Down
3 changes: 2 additions & 1 deletion psl/psl-core/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
mod preview_features;

pub use self::preview_features::{FeatureMap, PreviewFeature, PreviewFeatures, ALL_PREVIEW_FEATURES};
pub(crate) use self::preview_features::RenamedFeature;
pub use self::preview_features::{FeatureMapWithProvider, PreviewFeature, PreviewFeatures, ALL_PREVIEW_FEATURES};
230 changes: 168 additions & 62 deletions psl/psl-core/src/common/preview_features.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::{Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;
use std::sync::LazyLock;

/// A set of preview features.
pub type PreviewFeatures = enumflags2::BitFlags<PreviewFeature>;
Expand All @@ -8,7 +10,7 @@ macro_rules! features {
($( $variant:ident $(,)? ),*) => {
#[enumflags2::bitflags]
#[repr(u64)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum PreviewFeature {
$( $variant,)*
}
Expand Down Expand Up @@ -86,88 +88,192 @@ features!(
StrictUndefinedChecks
);

/// Generator preview features (alphabetically sorted)
pub const ALL_PREVIEW_FEATURES: FeatureMap = FeatureMap {
active: enumflags2::make_bitflags!(PreviewFeature::{
Deno
| DriverAdapters
| Metrics
| MultiSchema
| NativeDistinct
| PostgresqlExtensions
| Tracing
| Views
| RelationJoins
| OmitApi
| PrismaSchemaFolder
| StrictUndefinedChecks
}),
deprecated: enumflags2::make_bitflags!(PreviewFeature::{
AtomicNumberOperations
| AggregateApi
| ClientExtensions
| Cockroachdb
| ConnectOrCreate
| CreateMany
| DataProxy
| Distinct
| ExtendedIndexes
| ExtendedWhereUnique
| FieldReference
| FilteredRelationCount
| FilterJson
| FullTextIndex
| FullTextSearch
| GroupBy
| ImprovedQueryRaw
| InteractiveTransactions
| JsonProtocol
| MicrosoftSqlServer
| Middlewares
| MongoDb
| NamedConstraints
| NApi
| NativeTypes
| OrderByAggregateGroup
| OrderByNulls
| OrderByRelation
| ReferentialActions
| ReferentialIntegrity
| SelectRelationCount
| TransactionApi
| UncheckedScalarInputs
}),
hidden: enumflags2::make_bitflags!(PreviewFeature::{ReactNative | TypedSql | NativeFullTextSearchPostgres}),
};

#[derive(Debug)]
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
struct RenamedFeatureKey {
/// The old, deprecated preview feature that was renamed.
pub from: PreviewFeature,

/// The provider that the feature was renamed for.
pub provider: Option<&'static str>,
}

#[derive(Debug, Copy, Clone)]
pub(crate) struct RenamedFeatureValue {
/// The new preview feature.
pub to: PreviewFeature,

/// The Pris.ly link endpoint for the feature, i.e., what comes after `https://pris.ly/d/`.
pub prisly_link_endpoint: &'static str,
}

#[derive(Debug, Clone)]
pub(crate) enum RenamedFeature {
/// The preview feature was renamed for a specific provider.
ForProvider((&'static str, RenamedFeatureValue)),

/// The preview feature was renamed for all providers.
AllProviders(RenamedFeatureValue),
}

#[derive(Debug, Clone)]
pub struct FeatureMap {
/// Valid, visible features.
active: PreviewFeatures,

/// Valid, but connector-specific features that are only visible on matching provider key.
native: BTreeMap<&'static str, PreviewFeatures>,

/// Deprecated features.
deprecated: PreviewFeatures,

/// History of renamed deprecated features.
renamed: BTreeMap<RenamedFeatureKey, RenamedFeatureValue>,

/// Hidden preview features are valid features, but are not propagated into the tooling
/// (as autocomplete or similar) or into error messages (eg. showing a list of valid features).
hidden: PreviewFeatures,
}

impl FeatureMap {
pub const fn active_features(&self) -> PreviewFeatures {
self.active
#[derive(Debug, Clone)]
pub struct FeatureMapWithProvider {
provider: Option<&'static str>,
feature_map: FeatureMap,
}

/// The default feature map with an unknown provider.
/// This is used for convenience in `prisma/language-tools`, which needs the list of all available preview features
/// before a provider is necessarily known.
pub static ALL_PREVIEW_FEATURES: LazyLock<FeatureMapWithProvider> = LazyLock::new(|| FeatureMapWithProvider::new(None));

impl FeatureMapWithProvider {
pub fn new(connector_provider: Option<&'static str>) -> FeatureMapWithProvider {
// Generator preview features (alphabetically sorted)
let feature_map: FeatureMap = FeatureMap {
active: enumflags2::make_bitflags!(PreviewFeature::{
Deno
| DriverAdapters
| Metrics
| MultiSchema
| NativeDistinct
| OmitApi
| PostgresqlExtensions
| PrismaSchemaFolder
| RelationJoins
| StrictUndefinedChecks
| Tracing
| Views
}),
native: BTreeMap::from([
#[cfg(feature = "postgresql")]
(
"postgresql",
enumflags2::make_bitflags!(PreviewFeature::{
NativeFullTextSearchPostgres
}),
),
]),
renamed: BTreeMap::from([
#[cfg(feature = "postgresql")]
(
RenamedFeatureKey {
from: PreviewFeature::FullTextSearch,
provider: Some("postgresql"),
},
RenamedFeatureValue {
to: PreviewFeature::NativeFullTextSearchPostgres,
prisly_link_endpoint: "native-fts-postgres",
},
),
]),
deprecated: enumflags2::make_bitflags!(PreviewFeature::{
AtomicNumberOperations
| AggregateApi
| ClientExtensions
| Cockroachdb
| ConnectOrCreate
| CreateMany
| DataProxy
| Distinct
| ExtendedIndexes
| ExtendedWhereUnique
| FieldReference
| FilteredRelationCount
| FilterJson
| FullTextIndex
| FullTextSearch
| GroupBy
| ImprovedQueryRaw
| InteractiveTransactions
| JsonProtocol
| MicrosoftSqlServer
| Middlewares
| MongoDb
| NamedConstraints
| NApi
| NativeTypes
| OrderByAggregateGroup
| OrderByNulls
| OrderByRelation
| ReferentialActions
| ReferentialIntegrity
| SelectRelationCount
| TransactionApi
| UncheckedScalarInputs
}),
hidden: enumflags2::make_bitflags!(PreviewFeature::{ReactNative | TypedSql}),
};

Self {
provider: connector_provider,
feature_map,
}
}

pub fn native_features(&self) -> PreviewFeatures {
self.provider
.and_then(|provider| self.feature_map.native.get(provider).copied())
.unwrap_or_default()
}

pub fn active_features(&self) -> PreviewFeatures {
self.feature_map.active | self.native_features()
}

pub const fn hidden_features(&self) -> PreviewFeatures {
self.hidden
self.feature_map.hidden
}

pub(crate) fn is_valid(&self, flag: PreviewFeature) -> bool {
(self.active | self.hidden).contains(flag)
(self.active_features() | self.feature_map.hidden).contains(flag)
}

pub(crate) fn is_deprecated(&self, flag: PreviewFeature) -> bool {
self.deprecated.contains(flag)
self.feature_map.deprecated.contains(flag)
}

/// Was the given preview feature deprecated and renamed?
pub(crate) fn is_renamed<'f>(&self, flag: PreviewFeature) -> Option<RenamedFeature> {

Check failure on line 255 in psl/psl-core/src/common/preview_features.rs

View workflow job for this annotation

GitHub Actions / clippy linting

this lifetime isn't used in the function definition
// Check for a renamed feature specific to the provider. This is only possible if a provider is not None.
let provider_specific = self.provider.and_then(|provider| {
self.feature_map
.renamed
.get(&RenamedFeatureKey {
from: flag,
provider: Some(provider),
})
.map(|renamed| RenamedFeature::ForProvider((provider, renamed.clone())))

Check failure on line 264 in psl/psl-core/src/common/preview_features.rs

View workflow job for this annotation

GitHub Actions / clippy linting

using `clone` on type `RenamedFeatureValue` which implements the `Copy` trait
});

// Fallback to provider-independent renamed feature
provider_specific.or_else(|| {
self.feature_map
.renamed
.get(&RenamedFeatureKey {
from: flag,
provider: None,
})
.map(|renamed| RenamedFeature::AllProviders(renamed.clone()))

Check failure on line 275 in psl/psl-core/src/common/preview_features.rs

View workflow job for this annotation

GitHub Actions / clippy linting

using `clone` on type `RenamedFeatureValue` which implements the `Copy` trait
})
}
}

Expand Down
14 changes: 12 additions & 2 deletions psl/psl-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod validate;
use std::sync::Arc;

pub use crate::{
common::{PreviewFeature, PreviewFeatures, ALL_PREVIEW_FEATURES},
common::{FeatureMapWithProvider, PreviewFeature, PreviewFeatures, ALL_PREVIEW_FEATURES},
configuration::{
Configuration, Datasource, DatasourceConnectorData, Generator, GeneratorConfigValue, StringFromEnvVar,
},
Expand Down Expand Up @@ -171,8 +171,18 @@ fn validate_configuration(
diagnostics: &mut Diagnostics,
connectors: ConnectorRegistry<'_>,
) -> Configuration {
let generators = generator_loader::load_generators_from_ast(schema_ast, diagnostics);
let datasources = datasource_loader::load_datasources_from_ast(schema_ast, diagnostics, connectors);

// We need to know the active provider to determine which features are active.
// This was originally introduced because the `fullTextSearch` preview feature will hit GA stage
// one connector at a time (Prisma 6 GAs it for MySQL, other connectors may follow in future releases).
let feature_map_with_provider: FeatureMapWithProvider = datasources
.first()
.map(|ds| Some(ds.active_provider))
.map(|provider| FeatureMapWithProvider::new(provider))

Check failure on line 182 in psl/psl-core/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy linting

redundant closure
.unwrap_or_else(|| (*ALL_PREVIEW_FEATURES).clone());

let generators = generator_loader::load_generators_from_ast(schema_ast, diagnostics, &feature_map_with_provider);

Configuration::new(generators, datasources, diagnostics.warnings().to_owned())
}
Loading

0 comments on commit 52b8ed7

Please sign in to comment.