A framework for building convergent cross-platform Nextcloud clients using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

138 lines
3.6 KiB

import 'package:built_collection/built_collection.dart';
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
/// A `Uri` like object that is specialized in file path handling.
@immutable
class PathUri {
/// Creates a new path URI.
const PathUri({
required this.isAbsolute,
required this.isDirectory,
required this.pathSegments,
});
/// Creates a new `PathUri` object by parsing a [path] string.
///
/// An empty [path] is considered to be the current working directory.
factory PathUri.parse(final String path) {
final parts = path.split('/');
if (parts.length == 1 && parts.single.isEmpty) {
return PathUri(
isAbsolute: false,
isDirectory: true,
pathSegments: BuiltList(),
);
}
return PathUri(
isAbsolute: parts.first.isEmpty,
isDirectory: parts.last.isEmpty,
pathSegments: BuiltList(parts.where((final element) => element.isNotEmpty)),
);
}
/// Creates a new empty path URI representing the current working directory.
factory PathUri.cwd() => PathUri(
isAbsolute: false,
isDirectory: true,
pathSegments: BuiltList(),
);
/// Whether the path is an absolute path.
///
/// If `true` [path] will start with a slash.
final bool isAbsolute;
/// Whether the path is a directory.
///
/// If `true` [path] will end with a slash.
final bool isDirectory;
/// Returns the path as a list of its segments.
///
/// See [path] for getting the path as a string.
final BuiltList<String> pathSegments;
/// Returns the path as a string.
///
/// See [pathSegments] for getting the path as a list of its segments.
String get path {
final buffer = StringBuffer();
if (isAbsolute) {
buffer.write('/');
}
buffer.writeAll(pathSegments, '/');
if (isDirectory && pathSegments.isNotEmpty) {
buffer.write('/');
}
return buffer.toString();
}
/// Returns the name of the last element in path.
String get name => pathSegments.lastOrNull ?? '';
/// Returns the parent of the path.
PathUri? get parent {
if (pathSegments.isNotEmpty) {
return PathUri(
isAbsolute: isAbsolute,
isDirectory: true,
pathSegments: pathSegments.rebuild((final b) => b.removeLast()),
);
}
return null;
}
/// Joins the current path with another [path].
///
/// If the current path is not a directory a [StateError] will be thrown.
/// See [isDirectory] for checking if the current path is a directory.
PathUri join(final PathUri other) {
if (!isDirectory) {
throw StateError('$this is not a directory.');
}
return PathUri(
isAbsolute: isAbsolute,
isDirectory: other.isDirectory,
pathSegments: pathSegments.rebuild((final b) => b.addAll(other.pathSegments)),
);
}
/// Renames the last path segment and returns a new path URI.
PathUri rename(final String name) {
if (name.contains('/')) {
throw Exception('Path names must not contain /');
}
return PathUri(
isAbsolute: isAbsolute,
isDirectory: isDirectory,
pathSegments: pathSegments.isNotEmpty
? pathSegments.rebuild(
(final b) => b
..removeLast()
..add(name),
)
: BuiltList(),
);
}
@override
bool operator ==(final Object other) =>
other is PathUri &&
isAbsolute == other.isAbsolute &&
isDirectory == other.isDirectory &&
pathSegments == other.pathSegments;
@override
int get hashCode => Object.hashAll([
isAbsolute,
isDirectory,
pathSegments,
]);
@override
String toString() => path;
}