This is the part of the library that allows validating a data container is valid!
There is actually a plan to digest JSON Schema as well, this library just provides a way to define constraints and enforce them. So one could build a JSON schema parser that makes these constraints and then we could test the validity of any data container.
This library focuses on working with garlic layers for validation meaning you can test against any type that conforms to garlic::ViewLayer and that could include std::vector, RapidJSON types and your own custom types so long as you make wrappers for them.
A constraint is the smallest unit of validation, a constraint should take a layer and say if it is valid or not. It has two primary methods.
true
means valid, false
means invalid.There are finite set of constraints and there is documentation for them in garlic/constraints.h
While this may seem like a clunky way to create these constraints, using the documentation and knowing the convention helps a lot to make it easier to work with these.
Every constraint has a name
, message
in case it fails and whether or not it is fatal
. It just means if there are three constraints next to each other, if one of them fails, we break the loop. Otherwise, you continue despite the failure. This will make more sense as you read through this documentation.
NOTE: Every call to garlic::make_constraint() can optionally end with
name
,custom message
andfatal
args.
You can then use a constraint to validate any type conforming to garlic::ViewLayer
garlic::Constraint does not have any constructor, you instead need to either use garlic::Constraint::make or use a more convenient shortcut garlic::make_constraint().
If you need to make an empty (null context really) constraint, you can use garlic::Constraint::empty() but be careful, passing an empty constraint is like passing a nullptr, it can cause crashes if not handled properly.
A garlic::Field is just a group of constraints stored in a single entity with meta data and some properties.
Fields can have meta data which is basically a dictionary mapping strings to strings.
That meta data is meant to be used to store arbitrary information so it can be used for future tools to generate other formats like making migration scripts, or build html forms.
If you set a field to ignore details, it'll return a leaf garlic::ConstraintResult without any details from its inner constraints. This is helpful when you want to use a Field as a constraint that is made of other constraints. For instance, you can pack a bunch of constraints that together validate whether a user name is valid not, but the resulting constraint should only say "invalid username.".
A garlic::Model is a way to describe an object's structure. It is essentially a mapping of keys to a field descriptor object.
key -> { required: bool, field: std::shared_ptr<garlic::Field> }
For example, imagine a User object that must have a username and a phone number but phone is optional.
You can make constraints that validate a model or a field. See garlic::field_tag and garlic::model_tag.
Now a garlic::Module is simply a repository that contains a bunch of models and fields.
The idea is that you can make a module from a layer. Meaning you could load a module from a JSON, Yaml, MessagePack or whatever layer you have.
There is a plan to build module parsers that read JSON schema and other means like maybe defining a set of python files that describe these models.
Curious about the format of this module? See Module Definitions