Skip to content

Commit

Permalink
Track Boolean values which are constant
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelmior committed Nov 9, 2023
1 parent d79ae98 commit 4cac5e8
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Set `items` to `false` in tuple schemas
- Declare lack of support for `unevaluatedItems` and `if`/`then`
- Declare partial support for `additionalProperties`
- Track Boolean values which are constant

## [0.17.0]
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,23 @@ object BooleanSchema {
)
}

lazy val MinProperties: SchemaProperties[Boolean] =
SchemaProperties.empty[Boolean]
lazy val MinProperties: SchemaProperties[Boolean] = {
val props = SchemaProperties.empty[Boolean]
props.add(BooleanConstantProperty())

lazy val SimpleProperties: SchemaProperties[Boolean] =
SchemaProperties.empty[Boolean]
props
}

lazy val SimpleProperties: SchemaProperties[Boolean] = {
val props = SchemaProperties.empty[Boolean]
props.add(BooleanConstantProperty())

props
}

lazy val AllProperties: SchemaProperties[Boolean] = {
val props = SchemaProperties.empty[Boolean]
props.add(BooleanConstantProperty())
props.add(BooleanPercentProperty())

props
Expand Down Expand Up @@ -54,6 +63,16 @@ final case class BooleanSchema(
newSchema
}

override def toJson()(implicit p: JsonoidParams): JObject = {
properties.getOrNone[BooleanConstantProperty] match {
case Some(BooleanConstantProperty(Some(true), _)) =>
("const" -> JBool(true))
case Some(BooleanConstantProperty(_, Some(true))) =>
("const" -> JBool(false))
case _ => super.toJson()(p)
}
}

override def collectAnomalies[S <: JValue](
value: S,
path: String
Expand Down Expand Up @@ -108,3 +127,72 @@ final case class BooleanPercentProperty(
)
}
}

/** Tracks whether all values are either true or false
*
* @constructor Create a new Boolean constant property
* @param allTrue whether all values are true
* @param allFalse whether all values are false
*/
final case class BooleanConstantProperty(
allTrue: Option[Boolean] = None,
allFalse: Option[Boolean] = None
) extends SchemaProperty[Boolean] {
override type S = BooleanConstantProperty

override def newDefault()(implicit
p: JsonoidParams
): BooleanConstantProperty =
BooleanConstantProperty()

override val isInformational = false

override def toJson()(implicit p: JsonoidParams): JObject = Nil

override def unionMerge(
otherProp: BooleanConstantProperty
)(implicit p: JsonoidParams): BooleanConstantProperty = {
BooleanConstantProperty(
(allTrue, otherProp.allTrue) match {
case (None, None) => None
case _ =>
Some(allTrue.getOrElse(false) && otherProp.allTrue.getOrElse(false))
},
(allFalse, otherProp.allFalse) match {
case (None, None) => None
case _ =>
Some(allFalse.getOrElse(false) && otherProp.allFalse.getOrElse(false))
}
)
}

override def mergeValue(
value: Boolean
)(implicit p: JsonoidParams): BooleanConstantProperty = {
BooleanConstantProperty(
Some(allTrue.getOrElse(true) && value),
Some(allFalse.getOrElse(true) && !value)
)
}

override def isSubsetOf(
other: BooleanConstantProperty,
recursive: Boolean = true
)(implicit p: JsonoidParams): Boolean = {
val trueCompat = (allTrue, other.allTrue) match {
case (Some(a), Some(b)) => a >= b
case _ => true
}
val falseCompat = (allFalse, other.allFalse) match {
case (Some(a), Some(b)) => a >= b
case _ => true
}
trueCompat && falseCompat
}

override def expandTo(
other: Option[BooleanConstantProperty]
): BooleanConstantProperty = {
BooleanConstantProperty(Some(false), Some(false))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ object JsonSchema {
case e: org.json4s.MappingException => Set.empty[JValue]
// $COVERAGE-ON$
}
EnumSchema(values)

if (values.size === 1 && values.head.isInstanceOf[JBool]) {
// Convert Boolean constants to BooleanSchema
BooleanSchema(values.head.asInstanceOf[JBool].value)
} else {
EnumSchema(values)
}
} else {
val schemaTypes: List[String] = if ((schema \ "type") =/= JNothing) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ class BooleanSchemaSpec extends UnitSpec with ScalaCheckPropertyChecks {
booleanSchema.validTypes shouldBe Set(classOf[JBool])
}

it should "convert to constant where possible" in {
val trueSchema = BooleanSchema(true)
trueSchema.toJson() shouldBe JObject(List(("const" -> JBool(true))))
}

behavior of "BooleanPercentProperty"

it should "track the percentage of true values" in {
Expand Down

0 comments on commit 4cac5e8

Please sign in to comment.