Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ensure counter strictness #52

Merged
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
29 changes: 11 additions & 18 deletions lib/src/core/yaml_transformers/data/matched_node_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ part of '../yaml_transformer.dart';

/// Data object specifically created when a `Finder` finds it based on some
/// predefined condition
@immutable
class MatchedNodeData extends NodeData {
const MatchedNodeData(
super.precedingKeys,
super.key,
super.value,
class MatchedNodeData {
MatchedNodeData(
this.node,
this.matchedKeys,
this.matchedValue,
this.matchedPairs,
Expand All @@ -21,20 +18,21 @@ class MatchedNodeData extends NodeData {
required Map<String, String> matchedPairs,
}) {
return MatchedNodeData(
nodeData.precedingKeys,
nodeData.key,
nodeData.value,
nodeData,
matchedKeys,
matchedValue,
matchedPairs,
);
}

/// Denotes [NodeData] belonging to an indexed node that has been matched
final NodeData node;

/// List of keys in [ NodeData ] path that matched any preset conditions
final List<String> matchedKeys;

/// First/Only value matched from any of the values provided
final String matchedValue;
String matchedValue;

/// Map of pairs that matched any pairs provided
final Map<String, String> matchedPairs;
Expand All @@ -48,11 +46,11 @@ class MatchedNodeData extends NodeData {

/// Get list of keys upto the last renameable key
Iterable<Key> getUptoLastRenameable() {
final keys = super.getKeysAsString();
final keys = node.getKeysAsString();
final lastIndex = matchedKeys.map(keys.lastIndexOf).max;

// Keys to be taken, include last index plus one
return super.getKeys().take(lastIndex + 1);
return node.getKeys().take(lastIndex + 1);
}

/// Get path of keys upto the last renameable key
Expand All @@ -61,10 +59,5 @@ class MatchedNodeData extends NodeData {
}

@override
List<Object> get props => [
...super.props,
matchedKeys,
matchedPairs,
matchedValue,
];
String toString() => node.toString();
}
48 changes: 41 additions & 7 deletions lib/src/core/yaml_transformers/finders/custom_tracker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ part of 'finder.dart';
///
/// See [Counter].
final class MatchCounter extends CounterWithHistory<int, dynamic, dynamic> {
MatchCounter({required int? limit}) : _limit = limit;
MatchCounter({this.limit}) : isStrict = limit != null;

final int? _limit;
final bool isStrict;

final int? limit;

/// Increments from [ MatchedNodeData ].
///
Expand All @@ -17,26 +19,58 @@ final class MatchCounter extends CounterWithHistory<int, dynamic, dynamic> {
bool incrementUsingMatch(MatchedNodeData data) {
// Add any matched keys
if (data.matchedKeys.isNotEmpty) {
increment(data.matchedKeys, origin: Origin.key);
final ignoredKeys = increment(data.matchedKeys, origin: Origin.key);

data.matchedKeys.removeWhere(ignoredKeys.contains); // Remove ignored keys
}

// Add matched value if not empty
if (data.matchedValue.isNotEmpty) {
increment([data.matchedValue], origin: Origin.value);
if (increment([data.matchedValue], origin: Origin.value).isNotEmpty) {
data.matchedValue = ''; // Remove value
}
}

// Add all pairs
if (data.matchedPairs.isNotEmpty) {
increment(data.matchedPairs.entries, origin: Origin.pair);
final ignoredPairs = increment(
data.matchedPairs.entries,
origin: Origin.pair,
) as List<MapEntry<String, String>>;

data.matchedPairs.removeWhere(
(key, value) => ignoredPairs.any(
(element) => element.key == key && element.value == value,
),
);
}

/// All must be equal or greater than limit. Other keys may have
/// been found before more than once.
///
/// Get set of all counts and check if any is below limit
final anyBelowLimit = _limit == null ||
super.trackerState.values.toSet().any((element) => element < _limit!);
final anyBelowLimit = limit == null ||
super.trackerState.values.toSet().any((element) => element < limit!);

return !anyBelowLimit;
}

@override
List<dynamic> increment(Iterable<dynamic> values, {required Origin origin}) {
final ignored = <dynamic>[];
final valuesToAdd = <dynamic>[...values];

// In strict mode, ensure we only add those below limit
if (isStrict) {
for (final value in values) {
if (getCount(value, origin: origin) == limit) {
ignored.add(value);
valuesToAdd.remove(value);
}
}
}

super.increment(valuesToAdd, origin: origin);
return ignored;
}
}
21 changes: 15 additions & 6 deletions lib/src/core/yaml_transformers/finders/finder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ abstract base class Finder {

yield* findAllSync(prefilledCounter: true).takeWhile(
(value) {
/// Last value may be ignored. Last value itself causes the limit
/// to be reached. The limit is never reaches before.
/// Last value may be ignored. The last value itself causes the limit
/// to be reached.
if (value.reachedLimit) lastValue = value;
return !value.reachedLimit;
},
Expand All @@ -150,19 +150,28 @@ abstract base class Finder {
Iterable<FinderOutput> findAllSync({bool prefilledCounter = false}) sync* {
/// Incase this method is called indirectly via [Finder.find]
///
/// [Finder.findByCount] always prefills the counter thus always
/// sets up the [MatchCounter]
/// [Finder.findByCount] always prefills the counter and sets up the
/// [MatchCounter]
if (!prefilledCounter) _setUpCounter(null);

for (final nodeData in _generator) {
// Generate matched node data
final matchedNodeData = generateMatch(nodeData);

// We only yield it if it is valid
/// Try increment with the counter first.
///
/// The counter checks to make sure this [MatchedNodeData] has valid
/// matches. Matches below the limit. The [MatchedNodeData] will be
/// modified any redudant matches dropped.
///
/// Nothing happens if below the limit
final reachedLimit = counter!.incrementUsingMatch(matchedNodeData);

// If still valid after any modification, yield!
if (matchedNodeData.isValidMatch()) {
yield (
data: matchedNodeData,
reachedLimit: counter!.incrementUsingMatch(matchedNodeData),
reachedLimit: reachedLimit,
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/core/yaml_transformers/replacers/replacer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ abstract class Replacer {

// Just return the value instead as a string
return _getReplacementCandidate(
matchedNodeData.data,
matchedNodeData.node.data,
useFirst: useFirst,
) as T;
}
Expand Down
8 changes: 4 additions & 4 deletions lib/src/core/yaml_transformers/replacers/value_replacer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ class ValueReplacer extends Replacer {

final updatedMap = modifiable.updateIndexedMap(
replacement,
target: matchedNodeData.key,
path: matchedNodeData.precedingKeys,
target: matchedNodeData.node.key,
path: matchedNodeData.node.precedingKeys,
keyAndReplacement: {},
value: matchedNodeData.value,
value: matchedNodeData.node.value,
);

return (
mapping: {matchedNodeData.data: replacement},
mapping: {matchedNodeData.node.data: replacement},
updatedMap: YamlMap.wrap(updatedMap),
);
}
Expand Down
Loading