part of '../neon_files.dart'; class FilesSyncSources implements SyncSources { FilesSyncSources( final NextcloudClient client, final PathUri webdavBaseDir, final Directory ioBaseDir, ) : sourceA = FilesSyncSourceWebDavFile(client, webdavBaseDir), sourceB = FilesSyncSourceFileSystemEntity(client, ioBaseDir); @override final SyncSource sourceA; @override final SyncSource sourceB; @override SyncConflictSolution? findSolution(final SyncObject objectA, final SyncObject objectB) { if (objectA.data.isDirectory && objectB.data is Directory) { return SyncConflictSolution.overwriteA; } return null; } } class FilesSyncSourceWebDavFile implements SyncSource { FilesSyncSourceWebDavFile( this.client, this.baseDir, ); /// [NextcloudClient] used by the WebDAV part. final NextcloudClient client; /// Base directory on the WebDAV server. final PathUri baseDir; final props = WebDavPropWithoutValues.fromBools( davgetetag: true, davgetlastmodified: true, nchaspreview: true, ocsize: true, ocfavorite: true, ); PathUri _uri(final SyncObject object) => baseDir.join(PathUri.parse(object.id)); @override Future>> listObjects() async => (await client.webdav.propfind( baseDir, prop: props, depth: WebDavDepth.infinity, )) .toWebDavFiles() .sublist(1) .map( (final file) => ( id: file.path.pathSegments.sublist(baseDir.pathSegments.length).join('/'), data: file, ), ) .toList(); @override Future getObjectETag(final SyncObject object) async => object.data.isDirectory ? '' : object.data.etag!; @override Future> writeObject(final SyncObject object) async { if (object.data is File) { final stat = await object.data.stat(); await client.webdav.putFile( object.data as File, stat, _uri(object), lastModified: stat.modified, ); } else if (object.data is Directory) { await client.webdav.mkcol(_uri(object)); } else { throw Exception('Unable to sync FileSystemEntity of type ${object.data.runtimeType}'); } return ( id: object.id, data: (await client.webdav.propfind( _uri(object), prop: props, depth: WebDavDepth.zero, )) .toWebDavFiles() .single, ); } @override Future deleteObject(final SyncObject object) async => client.webdav.delete(_uri(object)); } class FilesSyncSourceFileSystemEntity implements SyncSource { FilesSyncSourceFileSystemEntity( this.client, this.baseDir, ); /// [NextcloudClient] used by the WebDAV part. final NextcloudClient client; /// Base directory on the local filesystem. final Directory baseDir; @override Future>> listObjects() async => baseDir.listSync(recursive: true).map( (final e) { var path = p.relative(e.path, from: baseDir.path); if (path.endsWith('/')) { path = path.substring(0, path.length - 1); } return (id: path, data: e); }, ).toList(); @override Future getObjectETag(final SyncObject object) async => object.data is Directory ? '' : object.data.statSync().modified.millisecondsSinceEpoch.toString(); @override Future> writeObject(final SyncObject object) async { if (object.data.isDirectory) { final dir = Directory(p.join(baseDir.path, object.id))..createSync(); return (id: object.id, data: dir); } else { final file = File(p.join(baseDir.path, object.id)); await client.webdav.getFile(object.data.path, file); await file.setLastModified(object.data.lastModified!); return (id: object.id, data: file); } } @override Future deleteObject(final SyncObject object) async => object.data.delete(); }