Skip to content

Commit

Permalink
prisma-fmt: Make get_config error tolerant (#4875)
Browse files Browse the repository at this point in the history
* prisma-fmt: Make `parse_confgiration_multi` error tolerant

Now the function never fails and always returns both `Config` and
`Diagnostics`. In case of errors, `Diagnostic` would be non-empty, but
it will still try to return as much of a config as it was able to parse.

This is useful mostly for dev tools - they need to be able to read
preview feature from more invalid files than old `get_config` would've
allowed. It also improves diagnostics in the CLI since we are now also
able to tell that user wanted to have more cases.

This is a breaking change to consumers of `prisma-fmt`: both prisma cli
and language-tools would need to be adapted to the new signature.

Contributes to prisma/team-orm#1143
Contributes to prisma/team-orm#1127 (opportunistic refactor)

* Rename method once more
  • Loading branch information
SevInf committed May 30, 2024
1 parent 36ed37c commit aa2d11f
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 87 deletions.
257 changes: 195 additions & 62 deletions prisma-fmt/src/get_config.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion prisma-fmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub fn referential_actions(schema: String) -> String {
/// type GetConfigResponse = GetConfigErrorResponse | GetConfigSuccessResponse
///
/// ```
pub fn get_config(get_config_params: String) -> Result<String, String> {
pub fn get_config(get_config_params: String) -> String {
get_config::get_config(&get_config_params)
}

Expand Down
26 changes: 11 additions & 15 deletions prisma-fmt/src/text_document_completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use lsp_types::*;
use psl::{
datamodel_connector::Connector,
diagnostics::{FileId, Span},
parse_configuration_multi_file,
error_tolerant_parse_configuration,
parser_database::{ast, ParserDatabase, SourceFile},
Configuration, Datasource, Diagnostics, Generator, PreviewFeature,
};
Expand All @@ -21,9 +21,7 @@ pub(crate) fn empty_completion_list() -> CompletionList {
}

pub(crate) fn completion(schema_files: Vec<(String, SourceFile)>, params: CompletionParams) -> CompletionList {
let config = parse_configuration_multi_file(&schema_files)
.ok()
.map(|(_, config)| config);
let (_, config, _) = error_tolerant_parse_configuration(&schema_files);

let mut list = CompletionList {
is_incomplete: false,
Expand All @@ -50,7 +48,7 @@ pub(crate) fn completion(schema_files: Vec<(String, SourceFile)>, params: Comple
};

let ctx = CompletionContext {
config: config.as_ref(),
config: &config,
params: &params,
db: &db,
position,
Expand All @@ -64,7 +62,7 @@ pub(crate) fn completion(schema_files: Vec<(String, SourceFile)>, params: Comple

#[derive(Debug, Clone, Copy)]
struct CompletionContext<'a> {
config: Option<&'a Configuration>,
config: &'a Configuration,
params: &'a CompletionParams,
db: &'a ParserDatabase,
position: usize,
Expand All @@ -89,19 +87,19 @@ impl<'a> CompletionContext<'a> {
}

fn datasource(self) -> Option<&'a Datasource> {
self.config.and_then(|conf| conf.datasources.first())
self.config.datasources.first()
}

fn generator(self) -> Option<&'a Generator> {
self.config.and_then(|conf| conf.generators.first())
self.config.generators.first()
}
}

fn push_ast_completions(ctx: CompletionContext<'_>, completion_list: &mut CompletionList) {
let relation_mode = match ctx.config.map(|c| c.relation_mode()) {
Some(Some(rm)) => rm,
_ => ctx.connector().default_relation_mode(),
};
let relation_mode = ctx
.config
.relation_mode()
.unwrap_or_else(|| ctx.connector().default_relation_mode());

match ctx.db.ast(ctx.initiating_file_id).find_at_position(ctx.position) {
ast::SchemaPosition::Model(
Expand Down Expand Up @@ -154,9 +152,7 @@ fn push_ast_completions(ctx: CompletionContext<'_>, completion_list: &mut Comple
datasource::relation_mode_completion(completion_list);
}

if let Some(config) = ctx.config {
ctx.connector().datasource_completions(config, completion_list);
}
ctx.connector().datasource_completions(ctx.config, completion_list);
}

ast::SchemaPosition::DataSource(
Expand Down
4 changes: 2 additions & 2 deletions prisma-schema-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ pub fn format(schema: String, params: String) -> String {

/// Docs: https://prisma.github.io/prisma-engines/doc/prisma_fmt/fn.get_config.html
#[wasm_bindgen]
pub fn get_config(params: String) -> Result<String, JsError> {
pub fn get_config(params: String) -> String {
register_panic_hook();
prisma_fmt::get_config(params).map_err(|e| JsError::new(&e))
prisma_fmt::get_config(params)
}

/// Docs: https://prisma.github.io/prisma-engines/doc/prisma_fmt/fn.get_dmmf.html
Expand Down
24 changes: 17 additions & 7 deletions psl/psl-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ mod reformat;
mod set_config_dir;
mod validate;

use std::sync::Arc;

pub use crate::{
common::{PreviewFeature, PreviewFeatures, ALL_PREVIEW_FEATURES},
configuration::{
Expand Down Expand Up @@ -130,16 +132,27 @@ pub fn parse_configuration(
schema: &str,
connectors: ConnectorRegistry<'_>,
) -> Result<Configuration, diagnostics::Diagnostics> {
let mut diagnostics = Diagnostics::default();
let ast = schema_ast::parse_schema(schema, &mut diagnostics, diagnostics::FileId::ZERO);
let out = validate_configuration(&ast, &mut diagnostics, connectors);
let source_file = SourceFile::new_allocated(Arc::from(schema.to_owned().into_boxed_str()));
let (_, out, mut diagnostics) =
error_tolerant_parse_configuration(&[("schema.prisma".into(), source_file)], connectors);
diagnostics.to_result().map(|_| out)
}

pub fn parse_configuration_multi_file(
files: &[(String, SourceFile)],
connectors: ConnectorRegistry<'_>,
) -> Result<(Files, Configuration), (Files, diagnostics::Diagnostics)> {
let (files, configuration, mut diagnostics) = error_tolerant_parse_configuration(files, connectors);
match diagnostics.to_result() {
Ok(_) => Ok((files, configuration)),
Err(err) => Err((files, err)),
}
}

pub fn error_tolerant_parse_configuration(
files: &[(String, SourceFile)],
connectors: ConnectorRegistry<'_>,
) -> (Files, Configuration, Diagnostics) {
let mut diagnostics = Diagnostics::default();
let mut configuration = Configuration::default();

Expand All @@ -150,10 +163,7 @@ pub fn parse_configuration_multi_file(
configuration.extend(out);
}

match diagnostics.to_result() {
Ok(_) => Ok((asts, configuration)),
Err(err) => Err((asts, err)),
}
(asts, configuration, diagnostics)
}

fn validate_configuration(
Expand Down
8 changes: 8 additions & 0 deletions psl/psl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ pub fn parse_configuration_multi_file(
psl_core::parse_configuration_multi_file(files, builtin_connectors::BUILTIN_CONNECTORS)
}

/// Parses and validates Prisma schemas, but skip analyzing everything except datasource and generator
/// blocks. It never fails, but when the returned `Diagnostics` contains errors, it implies that the
/// `Configuration` content is partial.
/// Consumers may then decide whether to convert `Diagnostics` into an error.
pub fn error_tolerant_parse_configuration(files: &[(String, SourceFile)]) -> (Files, Configuration, Diagnostics) {
psl_core::error_tolerant_parse_configuration(files, builtin_connectors::BUILTIN_CONNECTORS)
}

/// Parse and analyze a Prisma schema.
pub fn parse_schema(file: impl Into<SourceFile>) -> Result<ValidatedSchema, String> {
let mut schema = validate(file.into());
Expand Down

0 comments on commit aa2d11f

Please sign in to comment.