verdict_rules 0.4.0
verdict_rules: ^0.4.0 copied to clipboard
A small, zero-dependency, async-native rule-evaluation engine. Compose conditions into one explainable pass/fail verdict, with real short-circuiting as a guarantee.
example/verdict_rules_example.dart
// A runnable example. pub.dev looks for `example/` and surfaces it on the
// package page, so this doubles as the first thing a reader sees.
import 'package:verdict_rules/verdict_rules.dart';
/// Builds a rule asserting that a numeric field clears a floor.
FunctionRule<Context> atLeast(String name, String field, num floor) =>
FunctionRule(name, (Context ctx) async {
final value = ctx[field]! as num;
return PredicateOutcome(value >= floor, detail: '$value vs $floor');
});
/// `eligible` below is an AndRule, so its own `detail` is always empty --
/// composing ShortCircuitEvaluator leaves no channel to build a descriptive
/// string from. `failingLeaves` is the replacement: the flattened set of
/// leaf results that actually explain a failure.
String _reason(RuleResult result) => result.failingLeaves
.map((leaf) => leaf.detail.isEmpty
? leaf.ruleName
: '${leaf.ruleName}: ${leaf.detail}')
.join('; ');
Future<void> main() async {
final eligible = AndRule<Context>('eligible', [
atLeast('age_ok', 'age', 18),
atLeast('score_ok', 'score', 60),
]);
// Short-circuits: score_ok is never reached when age_ok fails.
final tooYoung = await eligible.evaluate({'age': 16, 'score': 90});
print('${tooYoung.passed} — ${_reason(tooYoung)}');
print('rules actually evaluated: ${tooYoung.subResults.length}\n');
final failedScore = await eligible.evaluate({'age': 21, 'score': 55});
print('${failedScore.passed} — ${_reason(failedScore)}');
print('rules actually evaluated: ${failedScore.subResults.length}\n');
// The engine is diagnostic: it never short-circuits.
final engine = RulesEngine<Context>([
atLeast('age_ok', 'age', 18),
atLeast('score_ok', 'score', 60),
]);
final everything = await engine.runAll({'age': 16, 'score': 55});
print(
'runAll reported ${everything.results.length} rules, '
'passed=${everything.passed}',
);
}