blob: cbfa9450dd2cb6bb53e1d8f5eca43030ec412ee2 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class _Directory implements Directory {
static const CREATE_REQUEST = 0;
static const DELETE_REQUEST = 1;
static const EXISTS_REQUEST = 2;
static const CREATE_TEMP_REQUEST = 3;
static const LIST_REQUEST = 4;
static const RENAME_REQUEST = 5;
static const SUCCESS_RESPONSE = 0;
static const ILLEGAL_ARGUMENT_RESPONSE = 1;
static const OSERROR_RESPONSE = 2;
_Directory(String this._path);
_Directory.fromPath(Path path) : this(path.toNativePath());
_Directory.current() : this(_current());
external static String _current();
external static _createTemp(String template);
external static int _exists(String path);
external static _create(String path);
external static _delete(String path, bool recursive);
external static _rename(String path, String newPath);
external static SendPort _newServicePort();
Future<bool> exists() {
_ensureDirectoryService();
List request = new List(2);
request[0] = EXISTS_REQUEST;
request[1] = _path;
return _directoryService.call(request).transform((response) {
if (_isErrorResponse(response)) {
throw _exceptionOrErrorFromResponse(response, "Exists failed");
}
return response == 1;
});
}
bool existsSync() {
if (_path is !String) {
throw new ArgumentError();
}
var result = _exists(_path);
if (result is OSError) {
throw new DirectoryIOException("Exists failed", _path, result);
}
return (result == 1);
}
Future<Directory> create() {
_ensureDirectoryService();
List request = new List(2);
request[0] = CREATE_REQUEST;
request[1] = _path;
return _directoryService.call(request).transform((response) {
if (_isErrorResponse(response)) {
throw _exceptionOrErrorFromResponse(response, "Creation failed");
}
return this;
});
}
void createSync() {
if (_path is !String) {
throw new ArgumentError();
}
var result = _create(_path);
if (result is OSError) {
throw new DirectoryIOException("Creation failed", _path, result);
}
}
Future<Directory> createTemp() {
_ensureDirectoryService();
List request = new List(2);
request[0] = CREATE_TEMP_REQUEST;
request[1] = _path;
return _directoryService.call(request).transform((response) {
if (_isErrorResponse(response)) {
throw _exceptionOrErrorFromResponse(response,
"Creation of temporary directory failed");
}
return new Directory(response);
});
}
Directory createTempSync() {
if (_path is !String) {
throw new ArgumentError();
}
var result = _createTemp(path);
if (result is OSError) {
throw new DirectoryIOException("Creation of temporary directory failed",
_path,
result);
}
return new Directory(result);
}
Future<Directory> _deleteHelper(bool recursive, String errorMsg) {
_ensureDirectoryService();
List request = new List(3);
request[0] = DELETE_REQUEST;
request[1] = _path;
request[2] = recursive;
return _directoryService.call(request).transform((response) {
if (_isErrorResponse(response)) {
throw _exceptionOrErrorFromResponse(response, errorMsg);
}
return this;
});
}
Future<Directory> delete() {
return _deleteHelper(false, "Deletion failed");
}
void deleteSync() {
if (_path is !String) {
throw new ArgumentError();
}
var result = _delete(_path, false);
if (result is OSError) {
throw new DirectoryIOException("Deletion failed", _path, result);
}
}
Future<Directory> deleteRecursively() {
return _deleteHelper(true, "Deletion failed");
}
void deleteRecursivelySync() {
if (_path is !String) {
throw new ArgumentError();
}
var result = _delete(_path, true);
if (result is OSError) {
throw new DirectoryIOException("Deletion failed", _path, result);
}
}
Future<Directory> rename(String newPath) {
_ensureDirectoryService();
List request = new List(3);
request[0] = RENAME_REQUEST;
request[1] = _path;
request[2] = newPath;
return _directoryService.call(request).transform((response) {
if (_isErrorResponse(response)) {
throw _exceptionOrErrorFromResponse(response, "Rename failed");
}
return new Directory(newPath);
});
}
Directory renameSync(String newPath) {
if (_path is !String || newPath is !String) {
throw new ArgumentError();
}
var result = _rename(_path, newPath);
if (result is OSError) {
throw new DirectoryIOException("Rename failed", _path, result);
}
return new Directory(newPath);
}
DirectoryLister list({bool recursive: false}) {
return new _DirectoryLister(_path, recursive);
}
String get path { return _path; }
bool _isErrorResponse(response) {
return response is List && response[0] != _SUCCESS_RESPONSE;
}
_exceptionOrErrorFromResponse(response, String message) {
assert(_isErrorResponse(response));
switch (response[_ERROR_RESPONSE_ERROR_TYPE]) {
case _ILLEGAL_ARGUMENT_RESPONSE:
return new ArgumentError();
case _OSERROR_RESPONSE:
var err = new OSError(response[_OSERROR_RESPONSE_MESSAGE],
response[_OSERROR_RESPONSE_ERROR_CODE]);
return new DirectoryIOException(message, _path, err);
default:
return new Exception("Unknown error");
}
}
void _ensureDirectoryService() {
if (_directoryService == null) {
_directoryService = _newServicePort();
}
}
final String _path;
SendPort _directoryService;
}
class _DirectoryLister implements DirectoryLister {
_DirectoryLister(String path, bool recursive) {
const int LIST_DIRECTORY = 0;
const int LIST_FILE = 1;
const int LIST_ERROR = 2;
const int LIST_DONE = 3;
final int RESPONSE_TYPE = 0;
final int RESPONSE_PATH = 1;
final int RESPONSE_COMPLETE = 1;
final int RESPONSE_ERROR = 2;
List request = new List(3);
request[0] = _Directory.LIST_REQUEST;
request[1] = path;
request[2] = recursive;
ReceivePort responsePort = new ReceivePort();
// Use a separate directory service port for each listing as
// listing operations on the same directory can run in parallel.
_Directory._newServicePort().send(request, responsePort.toSendPort());
responsePort.receive((message, replyTo) {
if (message is !List || message[RESPONSE_TYPE] is !int) {
responsePort.close();
_reportError(new DirectoryIOException("Internal error"));
return;
}
switch (message[RESPONSE_TYPE]) {
case LIST_DIRECTORY:
if (_onDir != null) _onDir(message[RESPONSE_PATH]);
break;
case LIST_FILE:
if (_onFile != null) _onFile(message[RESPONSE_PATH]);
break;
case LIST_ERROR:
var errorType =
message[RESPONSE_ERROR][_ERROR_RESPONSE_ERROR_TYPE];
if (errorType == _ILLEGAL_ARGUMENT_RESPONSE) {
_reportError(new ArgumentError());
} else if (errorType == _OSERROR_RESPONSE) {
var responseError = message[RESPONSE_ERROR];
var err = new OSError(
responseError[_OSERROR_RESPONSE_MESSAGE],
responseError[_OSERROR_RESPONSE_ERROR_CODE]);
var errorPath = message[RESPONSE_PATH];
if (errorPath == null) errorPath = path;
_reportError(new DirectoryIOException("Directory listing failed",
errorPath,
err));
} else {
_reportError(new DirectoryIOException("Internal error"));
}
break;
case LIST_DONE:
responsePort.close();
if (_onDone != null) _onDone(message[RESPONSE_COMPLETE]);
break;
}
});
}
void set onDir(void onDir(String dir)) {
_onDir = onDir;
}
void set onFile(void onFile(String file)) {
_onFile = onFile;
}
void set onDone(void onDone(bool completed)) {
_onDone = onDone;
}
void set onError(void onError(e)) {
_onError = onError;
}
void _reportError(e) {
if (_onError != null) {
_onError(e);
} else {
throw e;
}
}
Function _onDir;
Function _onFile;
Function _onDone;
Function _onError;
}