Last active
November 9, 2023 03:54
-
-
Save TekExplorer/46fb721f9c7a21e763c396d4e6ce8708 to your computer and use it in GitHub Desktop.
Utility classes for ensuring that dir and file paths are consistent (tossed together)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
final class DirUri extends PathUri { | |
DirUri(String path) | |
: _uri = _parsePathUri(path: '$path/'), | |
super._(); | |
final Uri _uri; | |
@override | |
String? get filename => _uri.filename; | |
@override | |
String get directory => _uri.directory; | |
@override | |
String get fullPath => _uri.filenameAndDirectory; | |
@override | |
DirUri copyWith() => DirUri(fullPath); | |
@override | |
String toString() => 'FileUri($fullPath)'; | |
} | |
final class FileUri extends PathUri /* implements DirUri */ { | |
factory FileUri(String path) { | |
if (path.endsWith('/')) { | |
throw InvalidPathException('path ($path) Must not end with /'); | |
} | |
final uri = FileUri._unsafe(path); | |
final _filename = uri.filenameOrNull; | |
if (_filename == null) { | |
throw InvalidPathException('path ($path) Must have a file name'); | |
} | |
return uri; | |
} | |
FileUri._unsafe(String path) | |
: assert(!path.endsWith('/'), "Path can't end with '/' in FileUri!"), | |
_uri = _parsePathUri(path: path), | |
super._(); | |
final Uri _uri; | |
@override | |
String get filename => | |
filenameOrNull ?? | |
(throw StateError("Filename can't be null! Path: '$fullPath'")); | |
String? get filenameOrNull => _uri.filename; | |
@override | |
String get directory => _uri.directory; | |
@override | |
String get fullPath => _uri.filenameAndDirectory; | |
@override | |
FileUri copyWith() => FileUri._unsafe(fullPath); | |
@override | |
String toString() => 'FileUri($fullPath)'; | |
} | |
/// A class that standardizes slashes in a path, and exposes access to | |
/// file name or directory paths, depending on | |
@immutable | |
sealed class PathUri { | |
const PathUri._(); | |
factory PathUri.parse(String path) => | |
_parsePathUri(path: path)._hasFile ? FileUri(path) : DirUri(path); | |
static const dir = DirUri.new; | |
static const file = FileUri.new; | |
String? get filename; | |
String get directory; | |
String get fullPath; | |
PathUri copyWith(); | |
@override | |
String toString() => 'PathUri($fullPath)'; | |
@override | |
int get hashCode => Object.hash(fullPath, directory, filename); | |
@override | |
bool operator ==(Object other) { | |
if (other is! PathUri) return false; | |
return other.fullPath == fullPath; | |
} | |
} | |
class InvalidPathException implements Exception { | |
const InvalidPathException(this.message); | |
final String message; | |
} | |
Uri _parsePathUri({ | |
required String path, | |
bool leadingSlash = true, | |
}) { | |
path = path.withoutDuplicateSlashes; | |
if (path.isEmpty) return Uri(path: '/'); | |
if (path == '/') return Uri(path: '/'); | |
Uri uri = Uri.parse(path); | |
uri = Uri.parse(uri.pathSegments.join('/')); | |
final realSegments = uri.pathSegments.where((e) => e.isNotEmpty); | |
uri = Uri.parse(realSegments.join('/')); | |
String transitionary = uri.pathSegments.join('/'); | |
// cleanup | |
if (path.endsWith('/')) transitionary = '$transitionary/'; | |
if (leadingSlash) transitionary = '/$transitionary'; | |
if (!leadingSlash && transitionary.startsWith('/')) { | |
transitionary = transitionary.substring(1); | |
} | |
return Uri.parse(transitionary); | |
} | |
extension DirUriX on DirUri { | |
bool matches(String other) => DirUri(other).fullPath == fullPath; | |
DirUri get parent { | |
final segments = [_uri.pathSegments]; | |
segments.removeLast(); | |
return DirUri(segments.join('/')); | |
} | |
} | |
extension PathUriX on PathUri { | |
DirUri get directoryUri => DirUri(directory); | |
bool matchesDirectory(String other) => directoryUri.matches(other); | |
bool matchesFullPath(String other) { | |
// == compares full paths | |
// return PathUri.parse(other) == this; | |
// parsing will normalize/standardize the shape of a path. Keep it explicit? | |
return PathUri.parse(other).fullPath == fullPath; | |
} | |
bool matchesFilename(String other) => filename == other; | |
bool get isDirectory => !hasFile; | |
bool get hasFile => filename != null; | |
} | |
extension on String { | |
/// Converts any number of repeating `/` like `//` or `///` into one `/` | |
String get withoutDuplicateSlashes => replaceAll(RegExp(r'(\/\/+)'), '/'); | |
} | |
extension on Uri { | |
String get filenameAndDirectory => directory + (filename ?? ''); | |
String? get filename => fileAndDir.filename; | |
String get directory => fileAndDir.dir; | |
bool get _hasFile => !path.endsWith('/'); | |
({String dir, String? filename}) get fileAndDir { | |
if (pathSegments.isEmpty) { | |
return (dir: '/', filename: null); | |
} | |
final _path = [...pathSegments]; | |
String? _last; | |
if (_hasFile) _last = _path.removeLast(); | |
String _dir = _path.join('/'); | |
if (!_dir.startsWith('/')) _dir = '/$_dir'; | |
if (!_dir.endsWith('/')) _dir += '/'; | |
return (dir: _dir.withoutDuplicateSlashes, filename: _last); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment