Skip to content

Commit 168a4f7

Browse files
feat: add peekable to iterator interface (#108)
1 parent 485ac0b commit 168a4f7

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

include/rusty_iterators/interface.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "interperse.hpp"
1111
#include "map.hpp"
1212
#include "moving_window.hpp"
13+
#include "peekable.hpp"
1314
#include "skip.hpp"
1415
#include "step_by.hpp"
1516
#include "take.hpp"
@@ -50,6 +51,7 @@ using iterator::Inspect;
5051
using iterator::Interperse;
5152
using iterator::Map;
5253
using iterator::MovingWindow;
54+
using iterator::Peekable;
5355
using iterator::Skip;
5456
using iterator::StepBy;
5557
using iterator::Take;
@@ -148,6 +150,7 @@ class IterInterface
148150
[[nodiscard]] auto neBy(Other&& it, Functor&& f) -> bool;
149151

150152
[[nodiscard]] auto nth(size_t element) -> std::optional<T>;
153+
[[nodiscard]] auto peekable() -> Peekable<T, Derived>;
151154

152155
template <class Functor>
153156
requires PositionFunctor<T, Functor>
@@ -426,6 +429,12 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::nth(size_t n) -> std
426429
return self().next();
427430
}
428431

432+
template <class T, class Derived>
433+
auto rusty_iterators::interface::IterInterface<T, Derived>::peekable() -> Peekable<T, Derived>
434+
{
435+
return Peekable<T, Derived>{std::forward<Derived>(self())};
436+
}
437+
429438
template <class T, class Derived>
430439
template <class Functor>
431440
requires rusty_iterators::concepts::PositionFunctor<T, Functor>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#pragma once
2+
3+
#include "interface.fwd.hpp"
4+
5+
#include <optional>
6+
7+
namespace rusty_iterators::iterator
8+
{
9+
using interface::IterInterface;
10+
11+
template <class T, class Other>
12+
class Peekable : public IterInterface<T, Peekable<T, Other>>
13+
{
14+
public:
15+
explicit Peekable(Other&& it) : it(std::forward<Other>(it)) {}
16+
17+
auto next() -> std::optional<T>;
18+
auto peek() -> std::optional<T>;
19+
[[nodiscard]] auto sizeHint() const -> std::optional<size_t>;
20+
21+
private:
22+
Other it;
23+
std::optional<std::optional<T>> peeked = std::nullopt;
24+
};
25+
} // namespace rusty_iterators::iterator
26+
27+
template <class T, class Other>
28+
auto rusty_iterators::iterator::Peekable<T, Other>::next() -> std::optional<T>
29+
{
30+
if (peeked.has_value())
31+
{
32+
auto item = peeked.value();
33+
peeked = std::nullopt;
34+
return std::move(item);
35+
}
36+
return std::move(it.next());
37+
}
38+
39+
template <class T, class Other>
40+
auto rusty_iterators::iterator::Peekable<T, Other>::peek() -> std::optional<T>
41+
{
42+
if (peeked.has_value())
43+
return peeked.value();
44+
45+
peeked = std::make_optional(it.next());
46+
return peeked.value();
47+
}
48+
49+
template <class T, class Other>
50+
auto rusty_iterators::iterator::Peekable<T, Other>::sizeHint() const -> std::optional<size_t>
51+
{
52+
auto underlyingSize = it.sizeHint();
53+
54+
if (!underlyingSize.has_value())
55+
return std::move(underlyingSize);
56+
57+
auto fullSize = underlyingSize.value();
58+
59+
if (peeked.has_value() && peeked.value().has_value())
60+
fullSize += 1;
61+
62+
return fullSize;
63+
}

tests/peekable.test.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include <gmock/gmock.h>
2+
#include <gtest/gtest.h>
3+
4+
#include <rusty_iterators/iterator.hpp>
5+
6+
using ::rusty_iterators::iterator::LazyIterator;
7+
using ::testing::ElementsAreArray;
8+
9+
TEST(TestPeekableIterator, TestNextReturnsNextItem)
10+
{
11+
auto vec = std::vector{1, 2, 3};
12+
auto it = LazyIterator{vec}.peekable();
13+
14+
ASSERT_EQ(it.next(), 1);
15+
ASSERT_EQ(it.next(), 2);
16+
ASSERT_EQ(it.next(), 3);
17+
ASSERT_EQ(it.next(), std::nullopt);
18+
}
19+
20+
TEST(TestPeekableIterator, TestPeekGivesNextItem)
21+
{
22+
auto vec = std::vector{1, 2, 3};
23+
auto it = LazyIterator{vec}.peekable();
24+
25+
ASSERT_EQ(it.peek(), 1);
26+
ASSERT_EQ(it.next(), 1);
27+
ASSERT_EQ(it.peek(), 2);
28+
ASSERT_EQ(it.next(), 2);
29+
}
30+
31+
TEST(TestPeekableIterator, TestPeekMultipleTimes)
32+
{
33+
auto vec = std::vector{1, 2, 3};
34+
auto it = LazyIterator{vec}.peekable();
35+
36+
ASSERT_EQ(it.peek(), 1);
37+
ASSERT_EQ(it.peek(), 1);
38+
ASSERT_EQ(it.peek(), 1);
39+
ASSERT_EQ(it.next(), 1);
40+
}
41+
42+
TEST(TestPeekableIterator, TestCollectPeekedIterator)
43+
{
44+
auto vec = std::vector{1, 2, 3};
45+
auto it = LazyIterator{vec}.peekable();
46+
47+
it.peek();
48+
49+
EXPECT_THAT(it.collect(), ElementsAreArray({1, 2, 3}));
50+
}
51+
52+
TEST(TestPeekableIterator, TestSizeHintOnNotPeeked)
53+
{
54+
auto vec = std::vector{1, 2, 3};
55+
auto it = LazyIterator{vec}.peekable();
56+
57+
ASSERT_EQ(it.sizeHint(), 3);
58+
}
59+
60+
TEST(TestPeekableIterator, TestSizeHinttPeeked)
61+
{
62+
auto vec = std::vector{1, 2, 3};
63+
auto it = LazyIterator{vec}.peekable();
64+
65+
it.peek();
66+
67+
ASSERT_EQ(it.sizeHint(), 3);
68+
}
69+
70+
TEST(TestPeekableIterator, TestSizeHintOnPeekedInfinite)
71+
{
72+
auto vec = std::vector{1, 2, 3};
73+
auto it = LazyIterator{vec}.cycle().peekable();
74+
75+
it.peek();
76+
77+
ASSERT_EQ(it.sizeHint(), std::nullopt);
78+
}

0 commit comments

Comments
 (0)