Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F139944
validator.hpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
validator.hpp
View Options
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include
<libkazv-config.hpp>
#include
<functional>
#include
<types.hpp>
namespace
Kazv
{
/**
* Run validator against `input[k]` and add it to `ret`.
*
* A validator is a function taking a Jsonish value and
* return a pair of boolean (the status) and a Jsonish value (the output).
*
* If the status is true, the output will be added to `ret[k]`.
* If the status is false, the output will be ignored.
*
* Example:
* ```
* json j = json::object();
* Kazv::cast(
* j,
* json{{"foo", "1"}},
* "foo",
* [](const json &v) -> std::pair<bool, json> {
* return v.is_string()
* ? {true, v.template get<std::string>() + "x"}
* : {false, v};
* }
* ); // true, j = {{"foo", "1x"}}};
*
* json j2 = json::object();
* Kazv::cast(
* j,
* json{{"foo", 1}},
* "foo",
* [](const json &v) -> std::pair<bool, json> {
* return v.is_string()
* ? {true, v.template get<std::string>() + "x"}
* : {false, v};
* }
* ); // true, j2 = json::object();
* ```
*
* @param input The original json taken from user input.
* @param k The key (usually a property name of a json object) to process on.
* @param f The validator.
* @param ret The read-write reference of the returning json.
*
* @return Whether the cast is successful. The cast is successful if and only if
* the validator returns a status of true.
*/
template
<
class
Jsonish
,
class
Key
,
class
Validator
>
bool
cast
(
Jsonish
&
ret
,
const
Jsonish
&
input
,
Key
&&
k
,
Validator
&&
f
)
{
if
(
!
input
.
contains
(
k
))
{
return
false
;
}
auto
[
res
,
output
]
=
std
::
invoke
(
std
::
forward
<
Validator
>
(
f
),
input
[
k
]);
if
(
!
res
)
{
return
false
;
}
ret
[
k
]
=
output
;
return
true
;
}
/**
* Make an identity validator.
*
* The returned validator runs a predicate `f` against a value `val`.
* If the return value is true, return a pair of `true` and `val`.
* Otherwise, return a pair of `false` and `val`.
*
* @param f The predicate that judges the validity of a value.
* @return A validator.
*/
template
<
class
Func
>
auto
identValidate
(
Func
&&
f
)
{
return
[
f
=
std
::
forward
<
Func
>
(
f
)](
auto
&&
val
)
mutable
{
auto
res
=
std
::
invoke
(
std
::
forward
<
Func
>
(
f
),
val
);
return
std
::
pair
<
bool
,
json
>
(
res
,
std
::
forward
<
decltype
(
val
)
>
(
val
));
};
}
/**
* Strategy used for castArray().
*/
enum
class
CastArrayStrategy
{
/// If any item is invalid, ignore them and add all valid entries into the output.
IgnoreInvalid
,
/// If any item is invalid, fail the entire cast.
FailAll
,
};
/**
* Cast an array of items.
*
* Run the validator on each item of `input[k]` and accumulate the result
* into `ret`.
*
* Example:
* ```
* json j = json::object();
* Kazv::castArray(
* j,
* json{{"foo", {"1", "2", "3", 4}}},
* "foo",
* [](const json &v) -> std::pair<bool, json> {
* return v.is_string()
* ? {true, v.template get<std::string>() + "x"}
* : {false, v};
* },
* Kazv::CastArrayStrategy::IgnoreInvalid
* ); // true, j = {{"foo", {"1x", "2x", "3x"}}};
*
* json j2 = json::object();
* Kazv::castArray(
* j2,
* json{{"foo", {"1", "2", "3", 4}}},
* "foo",
* [](const json &v) -> std::pair<bool, json> {
* return v.is_string()
* ? {true, v.template get<std::string>() + "x"}
* : {false, v};
* },
* Kazv::CastArrayStrategy::FailAll
* ); // false, j2 = json::object();
* ```
*
* @param input The original json taken from user input.
* @param k The key (usually a property name of a json object) to process on.
* @param f The validator.
* @param ret The read-write reference of the returning json.
* @param strategy The strategy to use when handling invalid items.
* `CastArrayStrategy::IgnoreInvalid` makes the cast always successful
* (providing `input[k]` is an array), and all valid items will be added
* to `ret`.
* `CastArrayStrategy::FailAll` makes the cast successful only when all
* input items can be casted.
*
* @return Whether the cast is successful. If `input[k]` is not an array,
* it be unsuccessful.
*/
template
<
class
Jsonish
,
class
Key
,
class
Validator
>
bool
castArray
(
Jsonish
&
ret
,
const
Jsonish
&
input
,
Key
&&
k
,
Validator
&&
f
,
CastArrayStrategy
strategy
)
{
using
JsonT
=
std
::
decay_t
<
Jsonish
>
;
if
(
!
(
input
.
contains
(
k
)
&&
input
[
k
].
is_array
()))
{
return
false
;
}
auto
array
=
JsonT
::
array
();
for
(
const
auto
&
inputItem
:
input
[
k
])
{
auto
[
res
,
outputItem
]
=
std
::
invoke
(
f
,
inputItem
);
if
(
!
res
)
{
if
(
strategy
==
CastArrayStrategy
::
FailAll
)
{
return
false
;
}
}
else
{
array
.
push_back
(
std
::
move
(
outputItem
));
}
}
ret
[
k
]
=
std
::
move
(
array
);
return
true
;
}
/**
* Strategy used for castObject().
*/
enum
class
CastObjectStrategy
{
/// If any item is invalid, ignore them and add all valid entries into the output.
IgnoreInvalid
,
/// If any item is invalid, fail the entire cast.
FailAll
,
};
/**
* Cast an object of items.
*
* Run the validator on each key-value pair (as a json array) of `input[k]`
* and accumulate the result into `ret`.
*
* Example:
* ```
* json j = json::object();
* Kazv::castObject(
* j,
* json{{"foo", {{"1", "2"}, {"3", 4}}}},
* "foo",
* [](const json &kv) -> std::pair<bool, json> {
* const key = kv[0];
* const value = kv[1];
* return value.is_string()
* ? {true, json::array({key, v.template get<std::string>() + "x"})}
* : {false, v};
* },
* Kazv::CastObjectStrategy::IgnoreInvalid
* ); // true, j = {{"foo", {{"1", "2x"}}}};
*
* json j2 = json::object();
* Kazv::castObject(
* j2,
* json{{"foo", {{"1", "2"}, {"3", 4}}}},
* "foo",
* [](const json &v) -> std::pair<bool, json> {
* const key = kv[0];
* const value = kv[1];
* return value.is_string()
* ? {true, json::array({key, v.template get<std::string>() + "x"})}
* : {false, v};
* },
* Kazv::CastObjectStrategy::FailAll
* ); // false, j2 = json::object();
* ```
*
* @param input The original json taken from user input.
* @param k The key (usually a property name of a json object) to process on.
* @param f The validator.
* @param ret The read-write reference of the returning json.
* @param strategy The strategy to use when handling invalid items.
* `CastObjectStrategy::IgnoreInvalid` makes the cast always successful
* (providing `input[k]` is an object), and all valid items will be added
* to `ret`.
* `CastObjectStrategy::FailAll` makes the cast successful only when all
* input items can be casted.
*
* @return Whether the cast is successful. If `input[k]` is not an object,
* it be unsuccessful.
*/
template
<
class
Jsonish
,
class
Key
,
class
Validator
>
bool
castObject
(
Jsonish
&
ret
,
const
Jsonish
&
input
,
Key
&&
k
,
Validator
&&
f
,
CastObjectStrategy
strategy
)
{
using
JsonT
=
std
::
decay_t
<
Jsonish
>
;
if
(
!
(
input
.
contains
(
k
)
&&
input
[
k
].
is_object
()))
{
return
false
;
}
auto
obj
=
JsonT
::
object
();
for
(
const
auto
&
[
inputKey
,
inputValue
]
:
input
[
k
].
items
())
{
auto
inputItem
=
JsonT
::
array
({
JsonT
(
inputKey
),
inputValue
});
auto
[
res
,
outputItem
]
=
std
::
invoke
(
f
,
inputItem
);
if
(
!
res
)
{
if
(
strategy
==
CastObjectStrategy
::
FailAll
)
{
return
false
;
}
}
else
{
auto
outputKey
=
outputItem
[
0
];
auto
outputValue
=
std
::
move
(
outputItem
)[
1
];
obj
[
std
::
move
(
outputKey
)]
=
std
::
move
(
outputValue
);
}
}
ret
[
k
]
=
std
::
move
(
obj
);
return
true
;
}
/**
* Replace a non-existent value with a default one.
*
* If `input` contains `k`, return `input` as-is.
* Otherwise return `input` but with `[k]` replaced with `defaultValue`.
*
* @param input The input value.
* @param k The key to process on.
* @param defaultValue The default value to replace with.
* @return `input` if `input` contains `k`, otherwise, `input` where `[k]`
* is replaced with `defaultValue`.
*/
template
<
class
Jsonish
,
class
Key
,
class
Jsonish2
>
std
::
decay_t
<
Jsonish
>
makeDefaultValue
(
Jsonish
&&
input
,
Key
&&
k
,
Jsonish2
&&
defaultValue
)
{
using
JsonT
=
std
::
decay_t
<
Jsonish
>
;
if
(
input
.
contains
(
k
))
{
return
std
::
forward
<
Jsonish
>
(
input
);
}
else
{
JsonT
output
=
std
::
forward
<
Jsonish
>
(
input
);
output
[
std
::
forward
<
Key
>
(
k
)]
=
std
::
forward
<
Jsonish2
>
(
defaultValue
);
return
output
;
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Sun, Jan 19, 8:35 AM (1 h, 21 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55032
Default Alt Text
validator.hpp (9 KB)
Attached To
Mode
rL libkazv
Attached
Detach File
Event Timeline
Log In to Comment