refactor!: use IterableMixin where possible

This commit is contained in:
Aleksey Kulikov 2021-09-20 13:03:43 +03:00
parent 6845286af2
commit cf677e488a
8 changed files with 100 additions and 57 deletions

View file

@ -1,3 +1,4 @@
import 'dart:collection';
import 'dart:ffi'; import 'dart:ffi';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/libgit2dart.dart';
@ -10,7 +11,7 @@ import 'git_types.dart';
import 'repository.dart'; import 'repository.dart';
import 'util.dart'; import 'util.dart';
class Index { class Index with IterableMixin<IndexEntry> {
/// Initializes a new instance of [Index] class from provided /// Initializes a new instance of [Index] class from provided
/// pointer to index object in memory. /// pointer to index object in memory.
/// ///
@ -36,10 +37,7 @@ class Index {
} }
/// Checks whether entry at provided [path] is in the git index or not. /// Checks whether entry at provided [path] is in the git index or not.
bool contains(String path) => bindings.find(_indexPointer, path); bool find(String path) => bindings.find(_indexPointer, path);
/// Returns the count of entries currently in the index.
int get count => bindings.entryCount(_indexPointer);
/// Checks if the index contains entries representing file conflicts. /// Checks if the index contains entries representing file conflicts.
bool get hasConflicts => bindings.hasConflicts(_indexPointer); bool get hasConflicts => bindings.hasConflicts(_indexPointer);
@ -232,6 +230,9 @@ class Index {
/// Releases memory allocated for index object. /// Releases memory allocated for index object.
void free() => bindings.free(_indexPointer); void free() => bindings.free(_indexPointer);
@override
Iterator<IndexEntry> get iterator => _IndexIterator(_indexPointer);
} }
class IndexEntry { class IndexEntry {
@ -307,3 +308,28 @@ class ConflictEntry {
String toString() => String toString() =>
'ConflictEntry{ancestor: $ancestor, our: $our, their: $their}'; 'ConflictEntry{ancestor: $ancestor, our: $our, their: $their}';
} }
class _IndexIterator implements Iterator<IndexEntry> {
_IndexIterator(this._indexPointer) {
count = bindings.entryCount(_indexPointer);
}
final Pointer<git_index> _indexPointer;
IndexEntry? _currentEntry;
int _index = 0;
late final int count;
@override
IndexEntry get current => _currentEntry!;
@override
bool moveNext() {
if (_index == count) {
return false;
} else {
_currentEntry = IndexEntry(bindings.getByIndex(_indexPointer, _index));
_index++;
return true;
}
}
}

View file

@ -1,3 +1,4 @@
import 'dart:collection';
import 'dart:ffi'; import 'dart:ffi';
import 'bindings/libgit2_bindings.dart'; import 'bindings/libgit2_bindings.dart';
import 'bindings/reflog.dart' as bindings; import 'bindings/reflog.dart' as bindings;
@ -5,7 +6,7 @@ import 'reference.dart';
import 'signature.dart'; import 'signature.dart';
import 'util.dart'; import 'util.dart';
class RefLog { class RefLog with IterableMixin<RefLogEntry> {
/// Initializes a new instance of [RefLog] class from provided [Reference]. /// Initializes a new instance of [RefLog] class from provided [Reference].
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
@ -20,20 +21,6 @@ class RefLog {
/// Pointer to memory address for allocated reflog object. /// Pointer to memory address for allocated reflog object.
late final Pointer<git_reflog> _reflogPointer; late final Pointer<git_reflog> _reflogPointer;
/// Returns a list with entries of reference log.
List<RefLogEntry> get entries {
var log = <RefLogEntry>[];
for (var i = 0; i < count; i++) {
log.add(RefLogEntry(bindings.getByIndex(_reflogPointer, i)));
}
return log;
}
/// Returns the number of log entries in a reflog.
int get count => bindings.entryCount(_reflogPointer);
/// Lookup an entry by its index. /// Lookup an entry by its index.
/// ///
/// Requesting the reflog entry with an index of 0 (zero) will return /// Requesting the reflog entry with an index of 0 (zero) will return
@ -44,6 +31,9 @@ class RefLog {
/// Releases memory allocated for reflog object. /// Releases memory allocated for reflog object.
void free() => bindings.free(_reflogPointer); void free() => bindings.free(_reflogPointer);
@override
Iterator<RefLogEntry> get iterator => _RefLogIterator(_reflogPointer);
} }
class RefLogEntry { class RefLogEntry {
@ -62,3 +52,30 @@ class RefLogEntry {
@override @override
String toString() => 'ReflogEntry{message: $message}'; String toString() => 'ReflogEntry{message: $message}';
} }
class _RefLogIterator implements Iterator<RefLogEntry> {
_RefLogIterator(this._reflogPointer) {
_count = bindings.entryCount(_reflogPointer);
}
/// Pointer to memory address for allocated reflog object.
final Pointer<git_reflog> _reflogPointer;
RefLogEntry? _currentEntry;
int _index = 0;
late final int _count;
@override
RefLogEntry get current => _currentEntry!;
@override
bool moveNext() {
if (_index == _count) {
return false;
} else {
_currentEntry = RefLogEntry(bindings.getByIndex(_reflogPointer, _index));
_index++;
return true;
}
}
}

View file

@ -1,15 +1,14 @@
import 'dart:ffi'; import 'dart:ffi';
import 'package:libgit2dart/libgit2dart.dart';
import 'bindings/libgit2_bindings.dart'; import 'bindings/libgit2_bindings.dart';
import 'bindings/tag.dart' as bindings; import 'bindings/tag.dart' as bindings;
import 'bindings/object.dart' as object_bindings; import 'bindings/object.dart' as object_bindings;
import 'blob.dart';
import 'commit.dart'; import 'commit.dart';
import 'oid.dart'; import 'oid.dart';
import 'repository.dart'; import 'repository.dart';
import 'signature.dart'; import 'signature.dart';
import 'git_types.dart'; import 'git_types.dart';
import 'tree.dart';
import 'util.dart'; import 'util.dart';
class Tag { class Tag {

View file

@ -49,8 +49,7 @@ class Tree {
/// ///
/// If string [value] is provided, lookup is done by entry filename. /// If string [value] is provided, lookup is done by entry filename.
/// ///
/// If provided string [value] is a path to file, lookup is done by path. In that case /// If provided string [value] is a path to file, lookup is done by path.
/// returned object should be freed explicitly.
TreeEntry operator [](Object value) { TreeEntry operator [](Object value) {
if (value is int) { if (value is int) {
return TreeEntry(bindings.getByIndex(_treePointer, value)); return TreeEntry(bindings.getByIndex(_treePointer, value));

View file

@ -60,10 +60,10 @@ void main() {
final to = repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'] as Commit; final to = repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'] as Commit;
final from = repo['821ed6e80627b8769d170a293862f9fc60825226'] as Commit; final from = repo['821ed6e80627b8769d170a293862f9fc60825226'] as Commit;
final index = repo.index; final index = repo.index;
expect(index.contains('dir/dir_file.txt'), true); expect(index.find('dir/dir_file.txt'), true);
final revertIndex = repo.revertCommit(revertCommit: from, ourCommit: to); final revertIndex = repo.revertCommit(revertCommit: from, ourCommit: to);
expect(revertIndex.contains('dir/dir_file.txt'), false); expect(revertIndex.find('dir/dir_file.txt'), false);
revertIndex.free(); revertIndex.free();
index.free(); index.free();

View file

@ -32,7 +32,7 @@ void main() {
const featureFileSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc'; const featureFileSha = '9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc';
test('returns number of entries', () { test('returns number of entries', () {
expect(index.count, 4); expect(index.length, 4);
}); });
test('returns mode of index entry', () { test('returns mode of index entry', () {
@ -40,8 +40,8 @@ void main() {
}); });
test('returns index entry at provided position', () { test('returns index entry at provided position', () {
expect(index[2].path, 'feature_file'); expect(index[3].path, 'file');
expect(index['file'].sha, fileSha); expect(index[3].sha, fileSha);
}); });
test('returns index entry at provided path', () { test('returns index entry at provided path', () {
@ -74,9 +74,9 @@ void main() {
}); });
test('clears the contents', () { test('clears the contents', () {
expect(index.count, 4); expect(index.length, 4);
index.clear(); index.clear();
expect(index.count, 0); expect(index.length, 0);
}); });
group('add()', () { group('add()', () {
@ -85,13 +85,13 @@ void main() {
index.add(entry); index.add(entry);
expect(index['file'].sha, fileSha); expect(index['file'].sha, fileSha);
expect(index.count, 4); expect(index.length, 4);
}); });
test('successfully adds with provided path string', () { test('successfully adds with provided path string', () {
index.add('file'); index.add('file');
expect(index['file'].sha, fileSha); expect(index['file'].sha, fileSha);
expect(index.count, 4); expect(index.length, 4);
}); });
test('throws if file not found at provided path', () { test('throws if file not found at provided path', () {
@ -114,27 +114,27 @@ void main() {
index.clear(); index.clear();
index.addAll(['file', 'feature_file']); index.addAll(['file', 'feature_file']);
expect(index.count, 2); expect(index.length, 2);
expect(index['file'].sha, fileSha); expect(index['file'].sha, fileSha);
expect(index['feature_file'].sha, featureFileSha); expect(index['feature_file'].sha, featureFileSha);
index.clear(); index.clear();
index.addAll(['[f]*']); index.addAll(['[f]*']);
expect(index.count, 2); expect(index.length, 2);
expect(index['file'].sha, fileSha); expect(index['file'].sha, fileSha);
expect(index['feature_file'].sha, featureFileSha); expect(index['feature_file'].sha, featureFileSha);
index.clear(); index.clear();
index.addAll(['feature_f???']); index.addAll(['feature_f???']);
expect(index.count, 1); expect(index.length, 1);
expect(index['feature_file'].sha, featureFileSha); expect(index['feature_file'].sha, featureFileSha);
}); });
}); });
test('writes to disk', () { test('writes to disk', () {
expect(index.count, 4); expect(index.length, 4);
File('$tmpDir/new_file').createSync(); File('$tmpDir/new_file').createSync();
@ -144,43 +144,43 @@ void main() {
index.clear(); index.clear();
index.read(); index.read();
expect(index['new_file'].path, 'new_file'); expect(index['new_file'].path, 'new_file');
expect(index.count, 5); expect(index.length, 5);
}); });
test('removes an entry', () { test('removes an entry', () {
expect(index.contains('feature_file'), true); expect(index.find('feature_file'), true);
index.remove('feature_file'); index.remove('feature_file');
expect(index.contains('feature_file'), false); expect(index.find('feature_file'), false);
}); });
test('removes all entries with matching pathspec', () { test('removes all entries with matching pathspec', () {
expect(index.contains('file'), true); expect(index.find('file'), true);
expect(index.contains('feature_file'), true); expect(index.find('feature_file'), true);
index.removeAll(['[f]*']); index.removeAll(['[f]*']);
expect(index.contains('file'), false); expect(index.find('file'), false);
expect(index.contains('feature_file'), false); expect(index.find('feature_file'), false);
}); });
group('read tree', () { group('read tree', () {
const treeSha = 'df2b8fc99e1c1d4dbc0a854d9f72157f1d6ea078'; const treeSha = 'df2b8fc99e1c1d4dbc0a854d9f72157f1d6ea078';
test('successfully reads with provided SHA hex', () { test('successfully reads with provided SHA hex', () {
expect(index.count, 4); expect(index.length, 4);
index.readTree(treeSha); index.readTree(treeSha);
expect(index.count, 1); expect(index.length, 1);
// make sure the index is only modified in memory // make sure the index is only modified in memory
index.read(); index.read();
expect(index.count, 4); expect(index.length, 4);
}); });
test('successfully reads with provided short SHA hex', () { test('successfully reads with provided short SHA hex', () {
expect(index.count, 4); expect(index.length, 4);
index.readTree(treeSha.substring(0, 5)); index.readTree(treeSha.substring(0, 5));
expect(index.count, 1); expect(index.length, 1);
}); });
}); });

View file

@ -343,7 +343,7 @@ void main() {
test('returns log for reference', () { test('returns log for reference', () {
final ref = repo.references['refs/heads/master']; final ref = repo.references['refs/heads/master'];
final reflog = ref.log; final reflog = ref.log;
expect(reflog.entries.last.message, 'commit (initial): init'); expect(reflog.last.message, 'commit (initial): init');
reflog.free(); reflog.free();
ref.free(); ref.free();
@ -384,9 +384,9 @@ void main() {
ref.setTarget('refs/heads/feature', 'log message'); ref.setTarget('refs/heads/feature', 'log message');
expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
final reflog = ref.log; final reflog = ref.log;
expect(reflog.entries.first.message, 'log message'); expect(reflog.first.message, 'log message');
expect(reflog.entries.first.committer.name, 'name'); expect(reflog.first.committer.name, 'name');
expect(reflog.entries.first.committer.email, 'email'); expect(reflog.first.committer.email, 'email');
reflog.free(); reflog.free();
ref.free(); ref.free();

View file

@ -7,6 +7,7 @@ import 'helpers/util.dart';
void main() { void main() {
late Repository repo; late Repository repo;
late RefLog reflog; late RefLog reflog;
late Reference head;
final tmpDir = '${Directory.systemTemp.path}/reflog_testrepo/'; final tmpDir = '${Directory.systemTemp.path}/reflog_testrepo/';
setUp(() async { setUp(() async {
@ -18,12 +19,13 @@ void main() {
to: await Directory(tmpDir).create(), to: await Directory(tmpDir).create(),
); );
repo = Repository.open(tmpDir); repo = Repository.open(tmpDir);
reflog = RefLog(repo.head); head = repo.head;
reflog = RefLog(head);
}); });
tearDown(() async { tearDown(() async {
repo.head.free();
reflog.free(); reflog.free();
head.free();
repo.free(); repo.free();
await Directory(tmpDir).delete(recursive: true); await Directory(tmpDir).delete(recursive: true);
}); });
@ -33,7 +35,7 @@ void main() {
}); });
test('returns correct number of log entries', () { test('returns correct number of log entries', () {
expect(reflog.count, 4); expect(reflog.length, 4);
}); });
test('returns the log message', () { test('returns the log message', () {