LCOV - code coverage report
Current view: top level - lib/src/trie_router/src - trie_router.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 62 62 100.0 %
Date: 2021-04-04 17:06:32 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:path/path.dart' as path;
       2             : import 'package:routemaster/routemaster.dart';
       3             : import '../../query_parser.dart';
       4             : import 'errors.dart';
       5             : import 'router_result.dart';
       6             : import 'trie_node.dart';
       7             : 
       8             : class TrieRouter {
       9             :   final Trie<String, PageBuilder> _trie;
      10             : 
      11          20 :   TrieRouter() : _trie = Trie();
      12             : 
      13           9 :   void addAll(Map<String, PageBuilder> routes) {
      14          18 :     routes.forEach((key, value) {
      15           9 :       add(key, value);
      16             :     });
      17             :   }
      18             : 
      19             :   /// Throws a [ConflictingPathError] if there is a conflict.
      20             :   ///
      21             :   /// It is an error to add two segments prefixed with ':' at the same index.
      22          10 :   bool add(String route, PageBuilder value) {
      23          10 :     assert(route.isNotEmpty);
      24             : 
      25          10 :     var pathSegments = path.split(route);
      26          10 :     return addPathComponents(pathSegments, value);
      27             :   }
      28             : 
      29             :   /// Throws a [ConflictingPathError] if there is a conflict.
      30             :   ///
      31             :   /// It is an error to add two segments prefixed with ':' at the same index.
      32          10 :   bool addPathComponents(Iterable<String> pathSegments, PageBuilder value) {
      33          10 :     assert(pathSegments.isNotEmpty);
      34             : 
      35          10 :     var list = List<String>.from(pathSegments);
      36          20 :     TrieNode<String?, PageBuilder?>? current = _trie.root;
      37             :     var isNew = false;
      38             : 
      39             :     // Work downwards through the trie, adding nodes as needed, and keeping
      40             :     // track of whether we add any nodes.
      41          30 :     for (var i = 0; i < list.length; i++) {
      42          10 :       var pathSegment = list[i];
      43             : 
      44             :       // Throw an error when two segments start with ':' at the same index.
      45          10 :       if (pathSegment.startsWith(':') &&
      46           8 :           current!.containsWhere((k) => k!.startsWith(':')) &&
      47           6 :           !current.containsWhere((k) => k == pathSegment)) {
      48           1 :         throw ConflictingPathError(
      49             :             list,
      50           2 :             List<String?>.from(list).sublist(0, i)
      51           5 :               ..add(current.getWhere((k) => k!.startsWith(':'))!.key));
      52             :       }
      53             : 
      54          10 :       if (!current!.contains(pathSegment)) {
      55             :         isNew = true;
      56             : 
      57          30 :         final isLastSegment = i == list.length - 1;
      58             :         if (isLastSegment) {
      59          10 :           current.add(pathSegment, value);
      60             :         } else {
      61           3 :           current.add(pathSegment, null);
      62             :         }
      63             :       }
      64             : 
      65          10 :       current = current.get(pathSegment);
      66             :     }
      67             : 
      68             :     // Explicitly mark the end of a list of path segments. Otherwise, we might
      69             :     // say a path segment is present if it is a prefix of a different, longer
      70             :     // word that was added earlier.
      71          10 :     if (!current!.contains(null)) {
      72             :       isNew = true;
      73          10 :       current.add(null, null);
      74             :     }
      75             :     return isNew;
      76             :   }
      77             : 
      78           7 :   RouterResult? get(String route) {
      79          14 :     var pathSegments = path.split(QueryParser.stripQueryString(route));
      80           7 :     var parameters = <String, String>{};
      81          14 :     TrieNode<String?, PageBuilder?>? current = _trie.root;
      82             : 
      83          14 :     for (var segment in pathSegments) {
      84           7 :       if (current!.contains(segment)) {
      85           7 :         current = current.get(segment);
      86           4 :       } else if (current.containsWhere((k) => k!.startsWith(':'))) {
      87             :         // If there is a segment that starts with `:`, we should match any
      88             :         // route.
      89           3 :         current = current.getWhere((k) => k != null && k.startsWith(':'));
      90             : 
      91             :         // Add the current segment to the parameters. E.g. 'id': '123'
      92           3 :         parameters[current!.key!.substring(1)] = segment;
      93             :       } else {
      94             :         return null;
      95             :       }
      96             :     }
      97             : 
      98          14 :     return RouterResult(current!.value!, parameters, route);
      99             :   }
     100             : 
     101          10 :   List<RouterResult>? getAll(String route) {
     102          20 :     var pathSegments = path.split(QueryParser.stripQueryString(route));
     103          10 :     var parameters = <String, String>{};
     104          10 :     final result = <RouterResult>[];
     105             : 
     106          10 :     void addToResult(int index, TrieNode<String?, PageBuilder?> node) {
     107          20 :       final p = path.joinAll(pathSegments.take(index));
     108          10 :       result.add(
     109          10 :         RouterResult(
     110          10 :           node.value!,
     111          10 :           Map.unmodifiable(parameters),
     112             :           p,
     113             :         ),
     114             :       );
     115             :     }
     116             : 
     117          20 :     TrieNode<String?, PageBuilder?>? current = _trie.root;
     118             : 
     119             :     var i = 0;
     120             : 
     121          20 :     for (var segment in pathSegments) {
     122          10 :       i++;
     123             : 
     124          10 :       if (current!.contains(segment)) {
     125          10 :         current = current.get(segment);
     126          10 :         if (current!.value != null) {
     127          10 :           addToResult(i, current);
     128             :         }
     129          15 :       } else if (current.containsWhere((k) => k!.startsWith(':'))) {
     130             :         final nextSegment =
     131          14 :             i < pathSegments.length - 1 ? pathSegments[i] : null;
     132           2 :         final nextSegmentIsParam = nextSegment?.startsWith(':') ?? false;
     133             : 
     134             :         // If there is a segment that starts with `:`, we should match any
     135             :         // route.
     136          12 :         current = current.getWhere((k) => k != null && k.startsWith(':'));
     137             : 
     138             :         // Add the current segment to the parameters. E.g. ':id': '123'
     139          12 :         parameters[current!.key!.substring(1)] = segment;
     140             : 
     141           4 :         if (!nextSegmentIsParam && current.value != null) {
     142           3 :           addToResult(i, current);
     143             :         }
     144             :       } else {
     145             :         return null;
     146             :       }
     147             :     }
     148             : 
     149             :     return result;
     150             :   }
     151             : }

Generated by: LCOV version 1.15