mv method
Moves a file or directory from src to dst.
Matches Kubo's mfs.Mv: a dst ending in / or naming an existing
directory moves the source inside it under its basename; an existing
file at dst is silently replaced; a name collision inside a target
directory is an error.
Implementation
Future<void> mv(String src, String dst) async {
final denylist = _denylistService;
if (denylist != null && _pathLooksBlocked(src)) {
throw DenylistBlockedException(src);
}
final srcParts = _splitPath(src);
if (srcParts.isEmpty) {
throw Exception('Cannot move MFS root: $src');
}
final srcCid = await _resolvePath(_rootCid!, srcParts);
if (srcCid == null) {
throw Exception('Source path not found: $src');
}
final srcName = srcParts.last;
var dstPath = dst;
var redirectedIntoDir = dst.endsWith('/');
if (!redirectedIntoDir) {
final dstCid = await _resolvePath(_rootCid!, _splitPath(dst));
if (dstCid != null) {
final dstType = await _unixfsType(dstCid);
if (dstType == Data_DataType.Directory ||
dstType == Data_DataType.HAMTShard) {
dstPath = '${_normalizePath(dst)}/$srcName';
redirectedIntoDir = true;
}
}
} else {
dstPath = '$dst$srcName';
}
final normalizedDst = _normalizePath(dstPath);
if (_normalizePath(src) == normalizedDst) {
return; // Moving a node onto itself is a no-op.
}
final dstParts = _splitPath(normalizedDst);
final existing = await _resolvePath(_rootCid!, dstParts);
if (existing != null) {
if (redirectedIntoDir) {
// Inside a target directory Kubo's AddChild fails on any name
// collision, file or directory.
throw Exception('file already exists: $normalizedDst');
}
final existingType = await _unixfsType(existing);
if (existingType == Data_DataType.Directory ||
existingType == Data_DataType.HAMTShard) {
// Unreachable: when the destination resolves to a directory the path
// is redirected inside it above, so `existing` here is never a dir.
// coverage:ignore-start
throw Exception('file already exists: $normalizedDst');
// coverage:ignore-end
}
// An existing file at the destination is silently replaced (Kubo
// unlinks it before re-adding the source node).
}
await cp(src, normalizedDst, force: true);
await rm(src, recursive: true);
}