Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ fn validate_raw(value: &toml::Value) -> Result<(), String> {
}

pub(crate) fn validate(config: &RulesConfig) -> Result<(), String> {
const THRESHOLDS: [&str; 10] = [
const THRESHOLDS: [&str; 12] = [
"max_literal_length",
"max_ranking_order_by",
"min_audits_per_model",
"min_tests_per_model",
"min_custom_rule_test_cases",
Expand Down Expand Up @@ -160,6 +162,14 @@ pub(crate) fn validate(config: &RulesConfig) -> Result<(), String> {
{
return Err(format!("unknown rule thresholds: {name}"));
}
if let Some(name) = ["max_literal_length", "max_ranking_order_by"]
.into_iter()
.find(|name| config.thresholds.get(*name) == Some(&0))
{
return Err(format!(
"rules.thresholds.{name} must be a positive integer"
));
}
for entry in &config.threshold_overrides {
if entry.paths.is_empty() || entry.thresholds.is_empty() || entry.reason.trim().is_empty() {
return Err("rules.threshold_overrides requires paths, thresholds, and reason".into());
Expand Down
2 changes: 2 additions & 0 deletions crates/sqlbuild-rules-native/src/configuration/main/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pub(crate) mod load;
pub(crate) mod setting_help;
pub(crate) mod toml_string_array;
pub(crate) mod validate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! Remediation text that shows the exact `sqlbuild_project.toml` setting to write.

use crate::constants::{PROJECT_CONFIG_FILE, SETTING_SNIPPET_INDENT};

/// `<purpose>, set this in sqlbuild_project.toml:` followed by the section and `key = value`.
pub(crate) fn setting_help(purpose: &str, section: &str, key: &str, value: &str) -> String {
format!(
"{purpose}, set this in {PROJECT_CONFIG_FILE}:\n{SETTING_SNIPPET_INDENT}[{section}]\n{SETTING_SNIPPET_INDENT}{key} = {value}"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! TOML array rendering for remediation snippets.

/// A TOML array of strings, in the given order.
pub(crate) fn toml_string_array<'a>(values: impl IntoIterator<Item = &'a str>) -> String {
let quoted: Vec<String> = values
.into_iter()
.map(|value| format!("{value:?}"))
.collect();
format!("[{}]", quoted.join(", "))
}
3 changes: 3 additions & 0 deletions crates/sqlbuild-rules-native/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub(crate) const ENFORCED_CONTRACT: &str = "enforced";
pub(crate) const SELECT_STAR_MODEL_RULE_CODE: &str = "SQBRMODEL102";
pub(crate) const BOOLEAN_TYPE: &str = "BOOLEAN";
pub(crate) const TIMESTAMP_TYPE: &str = "TIMESTAMP";
pub(crate) const DATETIME_TYPE: &str = "DATETIME";
pub(crate) const DATE_TYPE: &str = "DATE";
pub(crate) const NEGATION_OPERATOR: &str = "-";
pub(crate) const DECLARATION_DOMAIN_COMPONENTS: usize = 3;
Expand Down Expand Up @@ -53,3 +54,5 @@ pub(crate) const SQL_TEST_ACTUAL_CTE: &str = "__actual";
pub(crate) const SQL_TEST_EXPECTED_CTE: &str = "__expected";
pub(crate) const SQL_TEST_ACTUAL_CTE_PREFIX: &str = "__actual__";
pub(crate) const QUOTED_IDENTIFIER_DELIMITER_BYTES: usize = 2;
pub(crate) const PROJECT_CONFIG_FILE: &str = "sqlbuild_project.toml";
pub(crate) const SETTING_SNIPPET_INDENT: &str = " ";
1 change: 1 addition & 0 deletions crates/sqlbuild-rules-native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ mod scope_metadata;
mod semantic_usage;
mod semantic_validation;
mod sql_lint;
mod sql_quality;
mod sql_scan;
mod sql_tokens;
33 changes: 26 additions & 7 deletions crates/sqlbuild-rules-native/src/rules/_helpers/domain_layout.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::configuration::main::setting_help::setting_help;
use crate::models::{DeclarationKind, Fault, Model, RuleMetadata, ScopeDeclaration, ScopeKind};
use crate::rules::_helpers::evaluation::path_fault;
use crate::rules::models::ProjectEvaluationRequest;
Expand Down Expand Up @@ -187,7 +188,12 @@ fn evaluate_models(evaluation: &ProjectEvaluationRequest<'_>) -> Vec<Fault> {
"model has ambiguous domain-root and level candidates: {}",
candidates.join(", ")
),
"Set rules.layout.domain_roots to the intended normalized roots.".into(),
setting_help(
"List the intended normalized domain roots",
"rules.layout",
"domain_roots",
r#"["<domain root>"]"#,
),
));
}
continue;
Expand All @@ -207,10 +213,15 @@ fn evaluate_models(evaluation: &ProjectEvaluationRequest<'_>) -> Vec<Fault> {
max_depth
),
format!(
"Flatten this path beneath {}/{}, promote part of it into the domain root, or set rules.thresholds.max_subdomain_depth to at least {}.",
"Flatten this path beneath {}/{}, or promote part of it into the domain root. {}",
resolved.domain,
resolved.level,
resolved.owners.len()
setting_help(
&format!("To allow this depth (the current value is {max_depth})"),
"rules.thresholds",
"max_subdomain_depth",
&resolved.owners.len().to_string(),
)
),
));
}
Expand Down Expand Up @@ -513,12 +524,20 @@ fn inspect_container(
format!(
"declaration role {:?} has bucket depth {}; configured maximum is {}",
inspection.root,
entry.buckets.len()
,inspection.max_depth
entry.buckets.len(),
inspection.max_depth
),
format!(
"Flatten this bucket path or set rules.thresholds.max_role_container_depth to at least {}.",
entry.buckets.len()
"Flatten this bucket path. {}",
setting_help(
&format!(
"To allow this depth (the current value is {})",
inspection.max_depth
),
"rules.thresholds",
"max_role_container_depth",
&entry.buckets.len().to_string(),
)
),
));
}
Expand Down
36 changes: 34 additions & 2 deletions crates/sqlbuild-rules-native/src/rules/_helpers/evaluation.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::configuration::main::setting_help::setting_help;
use crate::configuration::main::toml_string_array::toml_string_array;
use crate::constants::{
DECLARATION_DOMAIN_COMPONENTS, ENFORCED_CONTRACT, NEGATION_OPERATOR, REFERENCE_KIND,
SOURCE_REFERENCE_KIND, VIEW_MATERIALIZATION,
Expand Down Expand Up @@ -1102,7 +1104,22 @@ fn name_grammar(
"model {:?} uses unknown domain {:?}",
parsed.model.name, parts.domain
),
Some("Rename the model into a configured rules domain, or add this domain to rules.domains when it is an intentional project owner.".into()),
Some(format!(
"Rename the model into a configured rules domain. The current value is [rules] domains = {}. {}",
toml_string_array(config.domains.iter().map(String::as_str)),
setting_help(
"If this domain is an intentional project owner, add it",
"rules",
"domains",
&toml_string_array(
config
.domains
.iter()
.map(String::as_str)
.chain([parts.domain.as_str()]),
),
)
)),
));
}
return;
Expand Down Expand Up @@ -1208,7 +1225,22 @@ fn source_token_rule(
"model {:?} uses unapproved source token {:?}",
parsed.model.name, token
),
Some("Rename the source suffix to a token listed in rules.approved_source_tokens at this model path.".into()),
Some(format!(
"Rename the source suffix to an approved token. The current value is [rules] approved_source_tokens = {}. {}",
toml_string_array(config.approved_source_tokens.iter().map(String::as_str)),
setting_help(
"To approve this token",
"rules",
"approved_source_tokens",
&toml_string_array(
config
.approved_source_tokens
.iter()
.map(String::as_str)
.chain([token.as_str()]),
),
)
)),
));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::semantic_validation::_helpers::catalog::diagnostic_row;
use crate::semantic_validation::_helpers::function_names;
use crate::semantic_validation::_helpers::type_locations::locate_type_findings;
use crate::semantic_validation::models::FunctionProbes;
use crate::semantic_validation::types::DiagnosticRow;
use polyglot_sql::expressions::Select;
Expand Down Expand Up @@ -153,8 +154,10 @@ pub(crate) fn map_diagnostics(
dialect,
statements: None,
};
let mut errors: Vec<ValidationError> = Vec::with_capacity(result.errors.len());
for error in result.errors {
let mut native = result.errors;
locate_type_findings(sql, dialect, &mut native)?;
let mut errors: Vec<ValidationError> = Vec::with_capacity(native.len());
for error in native {
if let Some(error) = context.map(error)? {
errors.push(error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pub(crate) mod function_names;
pub(crate) mod identifiers;
pub(crate) mod normalization;
pub(crate) mod positions;
pub(crate) mod type_locations;
Loading
Loading