LCOV - code coverage report
Current view: top level - path-1.8.0/lib/src - parsed_path.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 45 80 56.2 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
       2             : // for details. All rights reserved. Use of this source code is governed by a
       3             : // BSD-style license that can be found in the LICENSE file.
       4             : 
       5             : import 'internal_style.dart';
       6             : import 'style.dart';
       7             : 
       8             : class ParsedPath {
       9             :   /// The [InternalStyle] that was used to parse this path.
      10             :   InternalStyle style;
      11             : 
      12             :   /// The absolute root portion of the path, or `null` if the path is relative.
      13             :   /// On POSIX systems, this will be `null` or "/". On Windows, it can be
      14             :   /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
      15             :   /// letters.
      16             :   String? root;
      17             : 
      18             :   /// Whether this path is root-relative.
      19             :   ///
      20             :   /// See `Context.isRootRelative`.
      21             :   bool isRootRelative;
      22             : 
      23             :   /// The path-separated parts of the path. All but the last will be
      24             :   /// directories.
      25             :   List<String> parts;
      26             : 
      27             :   /// The path separators preceding each part.
      28             :   ///
      29             :   /// The first one will be an empty string unless the root requires a separator
      30             :   /// between it and the path. The last one will be an empty string unless the
      31             :   /// path ends with a trailing separator.
      32             :   List<String> separators;
      33             : 
      34             :   /// The file extension of the last non-empty part, or "" if it doesn't have
      35             :   /// one.
      36           0 :   String extension([int level = 1]) => _splitExtension(level)[1];
      37             : 
      38             :   /// `true` if this is an absolute path.
      39          22 :   bool get isAbsolute => root != null;
      40             : 
      41          11 :   factory ParsedPath.parse(String path, InternalStyle style) {
      42             :     // Remove the root prefix, if any.
      43          11 :     final root = style.getRoot(path);
      44          11 :     final isRootRelative = style.isRootRelative(path);
      45          22 :     if (root != null) path = path.substring(root.length);
      46             : 
      47             :     // Split the parts on path separators.
      48          11 :     final parts = <String>[];
      49          11 :     final separators = <String>[];
      50             : 
      51             :     var start = 0;
      52             : 
      53          33 :     if (path.isNotEmpty && style.isSeparator(path.codeUnitAt(0))) {
      54           0 :       separators.add(path[0]);
      55             :       start = 1;
      56             :     } else {
      57          11 :       separators.add('');
      58             :     }
      59             : 
      60          33 :     for (var i = start; i < path.length; i++) {
      61          22 :       if (style.isSeparator(path.codeUnitAt(i))) {
      62          22 :         parts.add(path.substring(start, i));
      63          22 :         separators.add(path[i]);
      64          11 :         start = i + 1;
      65             :       }
      66             :     }
      67             : 
      68             :     // Add the final part, if any.
      69          22 :     if (start < path.length) {
      70          22 :       parts.add(path.substring(start));
      71          11 :       separators.add('');
      72             :     }
      73             : 
      74          11 :     return ParsedPath._(style, root, isRootRelative, parts, separators);
      75             :   }
      76             : 
      77          11 :   ParsedPath._(
      78             :       this.style, this.root, this.isRootRelative, this.parts, this.separators);
      79             : 
      80           0 :   String get basename {
      81           0 :     final copy = clone();
      82           0 :     copy.removeTrailingSeparators();
      83           0 :     if (copy.parts.isEmpty) return root ?? '';
      84           0 :     return copy.parts.last;
      85             :   }
      86             : 
      87           0 :   String get basenameWithoutExtension => _splitExtension()[0];
      88             : 
      89           0 :   bool get hasTrailingSeparator =>
      90           0 :       parts.isNotEmpty && (parts.last == '' || separators.last != '');
      91             : 
      92          11 :   void removeTrailingSeparators() {
      93          55 :     while (parts.isNotEmpty && parts.last == '') {
      94           0 :       parts.removeLast();
      95           0 :       separators.removeLast();
      96             :     }
      97          77 :     if (separators.isNotEmpty) separators[separators.length - 1] = '';
      98             :   }
      99             : 
     100          11 :   void normalize({bool canonicalize = false}) {
     101             :     // Handle '.', '..', and empty parts.
     102             :     var leadingDoubles = 0;
     103          11 :     final newParts = <String>[];
     104          22 :     for (var part in parts) {
     105          22 :       if (part == '.' || part == '') {
     106             :         // Do nothing. Ignore it.
     107          11 :       } else if (part == '..') {
     108             :         // Pop the last part off.
     109           0 :         if (newParts.isNotEmpty) {
     110           0 :           newParts.removeLast();
     111             :         } else {
     112             :           // Backed out past the beginning, so preserve the "..".
     113           0 :           leadingDoubles++;
     114             :         }
     115             :       } else {
     116          11 :         newParts.add(canonicalize ? style.canonicalizePart(part) : part);
     117             :       }
     118             :     }
     119             : 
     120             :     // A relative path can back out from the start directory.
     121          11 :     if (!isAbsolute) {
     122           0 :       newParts.insertAll(0, List.filled(leadingDoubles, '..'));
     123             :     }
     124             : 
     125             :     // If we collapsed down to nothing, do ".".
     126          11 :     if (newParts.isEmpty && !isAbsolute) {
     127           0 :       newParts.add('.');
     128             :     }
     129             : 
     130             :     // Canonicalize separators.
     131          11 :     parts = newParts;
     132          11 :     separators =
     133          55 :         List.filled(newParts.length + 1, style.separator, growable: true);
     134          55 :     if (!isAbsolute || newParts.isEmpty || !style.needsSeparator(root!)) {
     135          22 :       separators[0] = '';
     136             :     }
     137             : 
     138             :     // Normalize the Windows root if needed.
     139          44 :     if (root != null && style == Style.windows) {
     140           0 :       if (canonicalize) root = root!.toLowerCase();
     141           0 :       root = root!.replaceAll('/', '\\');
     142             :     }
     143          11 :     removeTrailingSeparators();
     144             :   }
     145             : 
     146          11 :   @override
     147             :   String toString() {
     148          11 :     final builder = StringBuffer();
     149          33 :     if (root != null) builder.write(root);
     150          44 :     for (var i = 0; i < parts.length; i++) {
     151          33 :       builder.write(separators[i]);
     152          33 :       builder.write(parts[i]);
     153             :     }
     154          33 :     builder.write(separators.last);
     155             : 
     156          11 :     return builder.toString();
     157             :   }
     158             : 
     159             :   /// Returns k-th last index of the `character` in the `path`.
     160             :   ///
     161             :   /// If `k` exceeds the count of `character`s in `path`, the left most index
     162             :   /// of the `character` is returned.
     163           0 :   int _kthLastIndexOf(String path, String character, int k) {
     164             :     var count = 0, leftMostIndexedCharacter = 0;
     165           0 :     for (var index = path.length - 1; index >= 0; --index) {
     166           0 :       if (path[index] == character) {
     167             :         leftMostIndexedCharacter = index;
     168           0 :         ++count;
     169           0 :         if (count == k) {
     170             :           return index;
     171             :         }
     172             :       }
     173             :     }
     174             :     return leftMostIndexedCharacter;
     175             :   }
     176             : 
     177             :   /// Splits the last non-empty part of the path into a `[basename, extension]`
     178             :   /// pair.
     179             :   ///
     180             :   /// Takes an optional parameter `level` which makes possible to return
     181             :   /// multiple extensions having `level` number of dots. If `level` exceeds the
     182             :   /// number of dots, the path is split at the first most dot. The value of
     183             :   /// `level` must be greater than 0, else `RangeError` is thrown.
     184             :   ///
     185             :   /// Returns a two-element list. The first is the name of the file without any
     186             :   /// extension. The second is the extension or "" if it has none.
     187           0 :   List<String> _splitExtension([int level = 1]) {
     188           0 :     if (level <= 0) {
     189           0 :       throw RangeError.value(
     190             :           level, 'level', "level's value must be greater than 0");
     191             :     }
     192             : 
     193             :     final file =
     194           0 :         parts.cast<String?>().lastWhere((p) => p != '', orElse: () => null);
     195             : 
     196           0 :     if (file == null) return ['', ''];
     197           0 :     if (file == '..') return ['..', ''];
     198             : 
     199           0 :     final lastDot = _kthLastIndexOf(file, '.', level);
     200             : 
     201             :     // If there is no dot, or it's the first character, like '.bashrc', it
     202             :     // doesn't count.
     203           0 :     if (lastDot <= 0) return [file, ''];
     204             : 
     205           0 :     return [file.substring(0, lastDot), file.substring(lastDot)];
     206             :   }
     207             : 
     208           0 :   ParsedPath clone() => ParsedPath._(
     209           0 :       style, root, isRootRelative, List.from(parts), List.from(separators));
     210             : }

Generated by: LCOV version 1.14