Skip to content

Commit c1b3734

Browse files
chrisbobbegnprice
authored andcommitted
model: Add makeTopicKeyedMap for case-insensitive-but-preserving topic maps
1 parent a518e4f commit c1b3734

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

lib/model/channel.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:collection';
2+
13
import 'package:flutter/foundation.dart';
24

35
import '../api/model/events.dart';
@@ -369,3 +371,21 @@ class ChannelStoreImpl with ChannelStore {
369371
}
370372
}
371373
}
374+
375+
/// A [Map] with [TopicName] keys and [V] values.
376+
///
377+
/// When one of these is created by [makeTopicKeyedMap],
378+
/// key equality is done case-insensitively; see there.
379+
///
380+
/// This type should only be used for maps created by [makeTopicKeyedMap].
381+
/// It would be nice to enforce that.
382+
typedef TopicKeyedMap<V> = Map<TopicName, V>;
383+
384+
/// Make a case-insensitive, case-preserving [TopicName]-keyed [LinkedHashMap].
385+
///
386+
/// The equality function is [TopicName.isSameAs],
387+
/// and the hash code is [String.hashCode] of [TopicName.canonicalize].
388+
TopicKeyedMap<V> makeTopicKeyedMap<V>() => LinkedHashMap<TopicName, V>(
389+
equals: (a, b) => a.isSameAs(b),
390+
hashCode: (k) => k.canonicalize().hashCode,
391+
);

test/model/channel_test.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import 'package:zulip/api/model/model.dart';
77
import 'package:zulip/model/store.dart';
88
import 'package:zulip/model/channel.dart';
99

10+
import '../api/model/model_checks.dart';
1011
import '../example_data.dart' as eg;
12+
import '../stdlib_checks.dart';
1113
import 'test_store.dart';
1214

1315
void main() {
@@ -412,4 +414,30 @@ void main() {
412414
.equals(UserTopicVisibilityPolicy.none);
413415
});
414416
});
417+
418+
group('makeTopicKeyedMap', () {
419+
test('"a" equals "A"', () {
420+
final map = makeTopicKeyedMap<int>()
421+
..[eg.t('a')] = 1
422+
..[eg.t('A')] = 2;
423+
check(map)
424+
..[eg.t('a')].equals(2)
425+
..[eg.t('A')].equals(2)
426+
..entries.which((it) => it.single
427+
..key.apiName.equals('a')
428+
..value.equals(2));
429+
});
430+
431+
test('"A" equals "a"', () {
432+
final map = makeTopicKeyedMap<int>()
433+
..[eg.t('A')] = 1
434+
..[eg.t('a')] = 2;
435+
check(map)
436+
..[eg.t('A')].equals(2)
437+
..[eg.t('a')].equals(2)
438+
..entries.which((it) => it.single
439+
..key.apiName.equals('A')
440+
..value.equals(2));
441+
});
442+
});
415443
}

test/stdlib_checks.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ extension ListChecks<T> on Subject<List<T>> {
1616
Subject<T> operator [](int index) => has((l) => l[index], '[$index]');
1717
}
1818

19+
extension MapEntryChecks<K, V> on Subject<MapEntry<K, V>> {
20+
Subject<K> get key => has((e) => e.key, 'key');
21+
Subject<V> get value => has((e) => e.value, 'value');
22+
}
23+
1924
extension NullableMapChecks<K, V> on Subject<Map<K, V>?> {
2025
void deepEquals(Map<Object?, Object?>? expected) {
2126
if (expected == null) {

0 commit comments

Comments
 (0)