platform/packages/file_service/lib/protevus_file_service.dart

147 lines
3.8 KiB
Dart
Raw Normal View History

2017-06-13 16:55:07 +00:00
import 'dart:async';
2018-10-19 17:00:35 +00:00
import 'dart:convert';
import 'package:protevus_framework/protevus_framework.dart';
2017-12-21 06:37:03 +00:00
import 'package:file/file.dart';
2017-06-13 16:55:07 +00:00
import 'package:pool/pool.dart';
/// Persists in-memory changes to a file on disk.
2018-10-19 17:00:35 +00:00
class JsonFileService extends Service<String, Map<String, dynamic>> {
2021-06-10 08:47:05 +00:00
FileStat? _lastStat;
2021-06-26 12:06:30 +00:00
final Pool _mutex = Pool(1);
2021-06-10 08:47:05 +00:00
late MapService _store;
2017-06-13 16:55:07 +00:00
final File file;
JsonFileService(this.file,
2021-06-26 12:06:30 +00:00
{bool allowRemoveAll = false,
bool allowQuery = true,
MapService? store}) {
2017-12-21 06:37:03 +00:00
_store = store ??
2021-06-26 12:06:30 +00:00
MapService(
2017-12-21 06:37:03 +00:00
allowRemoveAll: allowRemoveAll == true,
allowQuery: allowQuery != false);
2017-06-13 16:55:07 +00:00
}
2018-07-12 20:29:16 +00:00
Map<String, dynamic> _coerceStringDynamic(Map m) {
return m.keys.fold<Map<String, dynamic>>(
<String, dynamic>{}, (out, k) => out..[k.toString()] = m[k]);
}
2018-07-12 19:58:30 +00:00
Future _load() {
return _mutex.withResource(() async {
if (!await file.exists()) await file.writeAsString(json.encode([]));
var stat = await file.stat();
//
2017-06-13 16:55:07 +00:00
2018-07-12 19:58:30 +00:00
if (_lastStat == null ||
stat.modified.millisecondsSinceEpoch >
2021-06-10 08:47:05 +00:00
_lastStat!.modified.millisecondsSinceEpoch) {
2018-07-12 19:58:30 +00:00
_lastStat = stat;
2017-06-13 16:55:07 +00:00
2018-07-12 19:58:30 +00:00
var contents = await file.readAsString();
2017-06-13 16:55:07 +00:00
2018-07-12 20:29:16 +00:00
var list = json.decode(contents) as Iterable;
_store.items.clear(); // Clear exist in-memory copy
_store.items.addAll(list.map((x) =>
_coerceStringDynamic(_revive(x) as Map))); // Insert all new entries
2017-06-13 16:55:07 +00:00
}
2018-07-12 19:58:30 +00:00
});
2017-06-13 16:55:07 +00:00
}
2021-06-26 12:06:30 +00:00
Future<File> _save() {
2018-07-12 19:58:30 +00:00
return _mutex.withResource(() {
return file
.writeAsString(json.encode(_store.items.map(_jsonify).toList()));
});
2017-06-13 16:55:07 +00:00
}
2017-12-21 06:37:03 +00:00
@override
2018-07-12 19:58:30 +00:00
Future close() async {
_store.close();
2017-12-21 06:37:03 +00:00
}
2017-06-13 16:55:07 +00:00
@override
2018-10-19 17:00:35 +00:00
Future<List<Map<String, dynamic>>> index(
2021-06-10 08:47:05 +00:00
[Map<String, dynamic>? params]) async =>
2019-04-17 21:03:10 +00:00
_load()
.then((_) => _store.index(params))
.then((it) => it.map(_jsonifyToSD).toList());
2017-06-13 16:55:07 +00:00
@override
2021-06-10 08:47:05 +00:00
Future<Map<String, dynamic>> read(id, [Map<String, dynamic>? params]) =>
2019-04-17 21:03:10 +00:00
_load().then((_) => _store.read(id, params)).then(_jsonifyToSD);
2017-06-13 16:55:07 +00:00
@override
2018-10-19 17:00:35 +00:00
Future<Map<String, dynamic>> create(data,
2021-06-10 08:47:05 +00:00
[Map<String, dynamic>? params]) async {
2017-06-13 16:55:07 +00:00
await _load();
2019-04-17 21:03:10 +00:00
var created = await _store.create(data, params).then(_jsonifyToSD);
2017-06-13 16:55:07 +00:00
await _save();
2017-12-22 13:04:38 +00:00
return created;
2017-06-13 16:55:07 +00:00
}
@override
2021-06-10 08:47:05 +00:00
Future<Map<String, dynamic>> remove(id,
[Map<String, dynamic>? params]) async {
2017-06-13 16:55:07 +00:00
await _load();
2019-04-17 21:03:10 +00:00
var r = await _store.remove(id, params).then(_jsonifyToSD);
2017-06-13 16:55:07 +00:00
await _save();
return r;
}
@override
2018-10-19 17:00:35 +00:00
Future<Map<String, dynamic>> update(id, data,
2021-06-10 08:47:05 +00:00
[Map<String, dynamic>? params]) async {
2017-06-13 16:55:07 +00:00
await _load();
2019-04-17 21:03:10 +00:00
var r = await _store.update(id, data, params).then(_jsonifyToSD);
2017-06-13 16:55:07 +00:00
await _save();
return r;
}
@override
2018-10-19 17:00:35 +00:00
Future<Map<String, dynamic>> modify(id, data,
2021-06-10 08:47:05 +00:00
[Map<String, dynamic>? params]) async {
2017-06-13 16:55:07 +00:00
await _load();
2019-04-17 21:03:10 +00:00
var r = await _store.update(id, data, params).then(_jsonifyToSD);
2017-06-13 16:55:07 +00:00
await _save();
return r;
}
}
2021-06-26 12:06:30 +00:00
dynamic _safeForJson(x) {
if (x is DateTime) {
2017-06-13 16:55:07 +00:00
return x.toIso8601String();
2021-06-26 12:06:30 +00:00
} else if (x is Map) {
2017-06-13 16:55:07 +00:00
return _jsonify(x);
2021-06-26 12:06:30 +00:00
} else if (x is num || x is String || x is bool || x == null) {
2017-06-13 16:55:07 +00:00
return x;
2021-06-26 12:06:30 +00:00
} else if (x is Iterable) {
2017-06-13 16:55:07 +00:00
return x.map(_safeForJson).toList();
2021-06-26 12:06:30 +00:00
} else {
2017-06-13 16:55:07 +00:00
return x.toString();
2021-06-26 12:06:30 +00:00
}
2017-06-13 16:55:07 +00:00
}
Map _jsonify(Map map) {
return map.keys.fold<Map>({}, (out, k) => out..[k] = _safeForJson(map[k]));
}
2019-04-17 21:03:10 +00:00
Map<String, dynamic> _jsonifyToSD(Map<String, dynamic> map) =>
_jsonify(map).cast<String, dynamic>();
2017-12-21 06:38:49 +00:00
dynamic _revive(x) {
2017-06-13 16:55:07 +00:00
if (x is Map) {
2018-10-19 17:00:35 +00:00
return x.keys.fold<Map<String, dynamic>>(
{}, (out, k) => out..[k.toString()] = _revive(x[k]));
2021-06-26 12:06:30 +00:00
} else if (x is Iterable) {
2017-06-13 16:55:07 +00:00
return x.map(_revive).toList();
2021-06-26 12:06:30 +00:00
} else if (x is String) {
2017-06-13 16:55:07 +00:00
try {
return DateTime.parse(x);
} catch (e) {
return x;
}
2021-06-26 12:06:30 +00:00
} else {
2017-06-13 16:55:07 +00:00
return x;
2021-06-26 12:06:30 +00:00
}
2017-06-13 16:55:07 +00:00
}