Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/tombi-linter/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ mod min_max_contains_test_schema;
mod non_schema;
#[path = "integration/not_schema.rs"]
mod not_schema;
#[path = "integration/numeric_bounds_test_schema.rs"]
mod numeric_bounds_test_schema;
#[path = "integration/other_schema.rs"]
mod other_schema;
#[path = "integration/prefix_items_test_schema.rs"]
Expand Down
123 changes: 123 additions & 0 deletions crates/tombi-linter/tests/integration/numeric_bounds_test_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use std::path::PathBuf;

use tombi_linter::test_lint;
use tombi_test_lib::project_root_path;

fn schema_path() -> PathBuf {
project_root_path()
.join("schemas")
.join("numeric-bounds-test.schema.json")
}

test_lint! {
#[test]
fn test_numeric_bounds_accept_satisfied_relations(
r#"
integer_maximum = 10
integer_minimum = 10
integer_exclusive_maximum_normal = 9
integer_exclusive_minimum_normal = 11
float_maximum = 10.0
float_minimum = 10.0
float_exclusive_maximum = 9.5
float_exclusive_minimum = 10.5
"#,
SchemaPath(schema_path()),
) -> Ok(_)
}

test_lint! {
#[test]
fn test_integer_maximum_is_inclusive(
"integer_maximum = 11",
SchemaPath(schema_path()),
) -> Err(["The value must be ≤ 10, but found 11"])
}

test_lint! {
#[test]
fn test_integer_minimum_is_inclusive(
"integer_minimum = 9",
SchemaPath(schema_path()),
) -> Err(["The value must be ≥ 10, but found 9"])
}

test_lint! {
#[test]
fn test_integer_exclusive_maximum_uses_schema_boundary(
"integer_exclusive_maximum = -9223372036854775808",
SchemaPath(schema_path()),
) -> Err(["The value must be < -9223372036854775808, but found -9223372036854775808"])
}

test_lint! {
#[test]
fn test_integer_exclusive_minimum_uses_schema_boundary(
"integer_exclusive_minimum = 9223372036854775807",
SchemaPath(schema_path()),
) -> Err(["The value must be > 9223372036854775807, but found 9223372036854775807"])
}

test_lint! {
#[test]
fn test_float_maximum_is_inclusive(
"float_maximum = 10.5",
SchemaPath(schema_path()),
) -> Err(["The value must be ≤ 10, but found 10.5"])
}

test_lint! {
#[test]
fn test_float_minimum_is_inclusive(
"float_minimum = 9.5",
SchemaPath(schema_path()),
) -> Err(["The value must be ≥ 10, but found 9.5"])
}

test_lint! {
#[test]
fn test_float_exclusive_maximum_is_exclusive(
"float_exclusive_maximum = 10.0",
SchemaPath(schema_path()),
) -> Err(["The value must be < 10, but found 10"])
}

test_lint! {
#[test]
fn test_float_exclusive_minimum_is_exclusive(
"float_exclusive_minimum = 10.0",
SchemaPath(schema_path()),
) -> Err(["The value must be > 10, but found 10"])
}

test_lint! {
#[test]
fn test_float_maximum_rejects_nan(
"float_maximum = nan",
SchemaPath(schema_path()),
) -> Err(["The value must be ≤ 10, but found NaN"])
}

test_lint! {
#[test]
fn test_float_minimum_rejects_nan(
"float_minimum = nan",
SchemaPath(schema_path()),
) -> Err(["The value must be ≥ 10, but found NaN"])
}

test_lint! {
#[test]
fn test_float_exclusive_maximum_rejects_nan(
"float_exclusive_maximum = nan",
SchemaPath(schema_path()),
) -> Err(["The value must be < 10, but found NaN"])
}

test_lint! {
#[test]
fn test_float_exclusive_minimum_rejects_nan(
"float_exclusive_minimum = nan",
SchemaPath(schema_path()),
) -> Err(["The value must be > 10, but found NaN"])
}
24 changes: 12 additions & 12 deletions crates/tombi-validator/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,32 +75,32 @@ pub enum DiagnosticKind {
actual: String,
},

#[error("The value must be < {maximum}, but found {actual}")]
#[error("The value must be ≤ {maximum}, but found {actual}")]
IntegerMaximum { maximum: i64, actual: i64 },

#[error("The value must be > {minimum}, but found {actual}")]
#[error("The value must be ≥ {minimum}, but found {actual}")]
IntegerMinimum { minimum: i64, actual: i64 },

#[error("The value must be ≤ {maximum}, but found {actual}")]
IntegerExclusiveMaximum { maximum: i64, actual: i64 },
#[error("The value must be < {exclusive_maximum}, but found {actual}")]
IntegerExclusiveMaximum { exclusive_maximum: i64, actual: i64 },

#[error("The value must be ≥ {minimum}, but found {actual}")]
IntegerExclusiveMinimum { minimum: i64, actual: i64 },
#[error("The value must be > {exclusive_minimum}, but found {actual}")]
IntegerExclusiveMinimum { exclusive_minimum: i64, actual: i64 },

#[error("The value {actual} is not a multiple of {multiple_of}")]
IntegerMultipleOf { multiple_of: i64, actual: i64 },

#[error("The value must be < {maximum}, but found {actual}")]
#[error("The value must be ≤ {maximum}, but found {actual}")]
FloatMaximum { maximum: f64, actual: f64 },

#[error("The value must be > {minimum}, but found {actual}")]
#[error("The value must be ≥ {minimum}, but found {actual}")]
FloatMinimum { minimum: f64, actual: f64 },

#[error("The value must be ≤ {maximum}, but found {actual}")]
FloatExclusiveMaximum { maximum: f64, actual: f64 },
#[error("The value must be < {exclusive_maximum}, but found {actual}")]
FloatExclusiveMaximum { exclusive_maximum: f64, actual: f64 },

#[error("The value must be ≥ {minimum}, but found {actual}")]
FloatExclusiveMinimum { minimum: f64, actual: f64 },
#[error("The value must be > {exclusive_minimum}, but found {actual}")]
FloatExclusiveMinimum { exclusive_minimum: f64, actual: f64 },

#[error("The value {actual} is not a multiple of {multiple_of}")]
FloatMultipleOf { multiple_of: f64, actual: f64 },
Expand Down
20 changes: 20 additions & 0 deletions crates/tombi-validator/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,26 @@ pub trait Validate {
) -> BoxFuture<'b, Result<crate::Valid, crate::Invalid>>;
}

#[inline]
fn check_maximum<T: PartialOrd>(value: &T, maximum: &T) -> bool {
value <= maximum
}

#[inline]
fn check_minimum<T: PartialOrd>(value: &T, minimum: &T) -> bool {
value >= minimum
}

#[inline]
fn check_exclusive_maximum<T: PartialOrd>(value: &T, exclusive_maximum: &T) -> bool {
value < exclusive_maximum
}

#[inline]
fn check_exclusive_minimum<T: PartialOrd>(value: &T, exclusive_minimum: &T) -> bool {
value > exclusive_minimum
}

pub fn project_current_schema_for_value(
value: &impl tombi_document_tree::ValueImpl,
current_schema: Option<&tombi_schema_store::CurrentSchema<'_>>,
Expand Down
13 changes: 7 additions & 6 deletions crates/tombi-validator/src/validate/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use tombi_severity_level::SeverityLevelDefaultError;
use crate::{
comment_directive::get_tombi_key_table_value_rules_and_diagnostics,
validate::{
check_exclusive_maximum, check_exclusive_minimum, check_maximum, check_minimum,
handle_anything_schema, handle_deprecated_value, handle_nothing_schema, handle_unused_noqa,
is_multiple_of_with_tolerance, validate_adjacent_applicators,
},
Expand Down Expand Up @@ -202,7 +203,7 @@ async fn validate_float(
}

if let Some(maximum) = &float_schema.maximum
&& value > *maximum
&& !check_maximum(&value, maximum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand Down Expand Up @@ -236,7 +237,7 @@ async fn validate_float(
}

if let Some(minimum) = &float_schema.minimum
&& value < *minimum
&& !check_minimum(&value, minimum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand Down Expand Up @@ -270,7 +271,7 @@ async fn validate_float(
}

if let Some(exclusive_maximum) = &float_schema.exclusive_maximum
&& value >= *exclusive_maximum
&& !check_exclusive_maximum(&value, exclusive_maximum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -284,7 +285,7 @@ async fn validate_float(

crate::Diagnostic {
kind: Box::new(crate::DiagnosticKind::FloatExclusiveMaximum {
maximum: *exclusive_maximum,
exclusive_maximum: *exclusive_maximum,
actual: value,
}),
range,
Expand All @@ -304,7 +305,7 @@ async fn validate_float(
}

if let Some(exclusive_minimum) = &float_schema.exclusive_minimum
&& value <= *exclusive_minimum
&& !check_exclusive_minimum(&value, exclusive_minimum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -318,7 +319,7 @@ async fn validate_float(

crate::Diagnostic {
kind: Box::new(crate::DiagnosticKind::FloatExclusiveMinimum {
minimum: *exclusive_minimum,
exclusive_minimum: *exclusive_minimum,
actual: value,
}),
range,
Expand Down
25 changes: 13 additions & 12 deletions crates/tombi-validator/src/validate/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use tombi_severity_level::SeverityLevelDefaultError;
use crate::{
comment_directive::get_tombi_key_table_value_rules_and_diagnostics,
validate::{
check_exclusive_maximum, check_exclusive_minimum, check_maximum, check_minimum,
handle_anything_schema, handle_deprecated_value, handle_nothing_schema, handle_unused_noqa,
is_multiple_of_with_tolerance, validate_adjacent_applicators,
},
Expand Down Expand Up @@ -215,7 +216,7 @@ async fn validate_integer_schema(
}

if let Some(maximum) = &integer_schema.maximum
&& value > *maximum
&& !check_maximum(&value, maximum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand Down Expand Up @@ -249,7 +250,7 @@ async fn validate_integer_schema(
}

if let Some(minimum) = &integer_schema.minimum
&& value < *minimum
&& !check_minimum(&value, minimum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand Down Expand Up @@ -283,7 +284,7 @@ async fn validate_integer_schema(
}

if let Some(exclusive_maximum) = &integer_schema.exclusive_maximum
&& value >= *exclusive_maximum
&& !check_exclusive_maximum(&value, exclusive_maximum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -297,7 +298,7 @@ async fn validate_integer_schema(

crate::Diagnostic {
kind: Box::new(crate::DiagnosticKind::IntegerExclusiveMaximum {
maximum: *exclusive_maximum - 1,
exclusive_maximum: *exclusive_maximum,
actual: value,
}),
range,
Expand All @@ -317,7 +318,7 @@ async fn validate_integer_schema(
}

if let Some(exclusive_minimum) = &integer_schema.exclusive_minimum
&& value <= *exclusive_minimum
&& !check_exclusive_minimum(&value, exclusive_minimum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -331,7 +332,7 @@ async fn validate_integer_schema(

crate::Diagnostic {
kind: Box::new(crate::DiagnosticKind::IntegerExclusiveMinimum {
minimum: *exclusive_minimum + 1,
exclusive_minimum: *exclusive_minimum,
actual: value,
}),
range,
Expand Down Expand Up @@ -476,7 +477,7 @@ async fn validate_float_schema_for_integer(
}

if let Some(maximum) = &float_schema.maximum
&& value > *maximum
&& !check_maximum(&value, maximum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -499,7 +500,7 @@ async fn validate_float_schema_for_integer(
}

if let Some(minimum) = &float_schema.minimum
&& value < *minimum
&& !check_minimum(&value, minimum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -522,7 +523,7 @@ async fn validate_float_schema_for_integer(
}

if let Some(exclusive_maximum) = &float_schema.exclusive_maximum
&& value >= *exclusive_maximum
&& !check_exclusive_maximum(&value, exclusive_maximum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -536,7 +537,7 @@ async fn validate_float_schema_for_integer(

crate::Diagnostic {
kind: Box::new(crate::DiagnosticKind::IntegerExclusiveMaximum {
maximum: (*exclusive_maximum as i64) - 1,
exclusive_maximum: *exclusive_maximum as i64,
actual: value as i64,
}),
range,
Expand All @@ -545,7 +546,7 @@ async fn validate_float_schema_for_integer(
}

if let Some(exclusive_minimum) = &float_schema.exclusive_minimum
&& value <= *exclusive_minimum
&& !check_exclusive_minimum(&value, exclusive_minimum)
{
let level = lint_rules
.map(|rules| &rules.value)
Expand All @@ -559,7 +560,7 @@ async fn validate_float_schema_for_integer(

crate::Diagnostic {
kind: Box::new(crate::DiagnosticKind::IntegerExclusiveMinimum {
minimum: (*exclusive_minimum as i64) + 1,
exclusive_minimum: *exclusive_minimum as i64,
actual: value as i64,
}),
range,
Expand Down
Loading
Loading