Skip to content

Commit eb8a894

Browse files
jnthntatumcopybara-github
authored andcommitted
Add support for creating a subspan view of a cel::Source.
PiperOrigin-RevId: 939969141
1 parent 7d327d6 commit eb8a894

3 files changed

Lines changed: 116 additions & 0 deletions

File tree

common/source.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,51 @@ absl::optional<std::pair<int32_t, SourcePosition>> Source::FindLine(
585585
return std::make_pair(line, line_offsets[static_cast<size_t>(line) - 2]);
586586
}
587587

588+
SourceSubrange::SourceSubrange(const Source& source, SourceRange range)
589+
: source_(source), range_(range) {
590+
SourcePosition size = source_.content().size();
591+
ABSL_DCHECK(range_.begin >= 0);
592+
ABSL_DCHECK(range_.begin <= size);
593+
ABSL_DCHECK(range_.end >= range_.begin);
594+
ABSL_DCHECK(range_.end <= size);
595+
if (range_.begin < 0) {
596+
range_.begin = 0;
597+
}
598+
if (range_.begin > size) {
599+
range_.begin = size;
600+
}
601+
if (range_.end < range_.begin) {
602+
range_.end = range_.begin;
603+
}
604+
if (range_.end > size) {
605+
range_.end = size;
606+
}
607+
for (const auto& line_offset : source_.line_offsets()) {
608+
if (line_offset > range_.begin && line_offset <= range_.end) {
609+
line_offsets_.push_back(line_offset - range_.begin);
610+
}
611+
}
612+
line_offsets_.push_back(range_.end - range_.begin + 1);
613+
}
614+
615+
SourceContentView SourceSubrange::content() const {
616+
auto parent_content = source_.content();
617+
if (parent_content.empty() || range_.begin >= range_.end) {
618+
return EmptyContentView();
619+
}
620+
return absl::visit(
621+
[this](auto view) {
622+
return SourceContentView(
623+
view.subspan(static_cast<size_t>(range_.begin),
624+
static_cast<size_t>(range_.end - range_.begin)));
625+
},
626+
parent_content.view_);
627+
}
628+
629+
absl::Span<const SourcePosition> SourceSubrange::line_offsets() const {
630+
return absl::MakeConstSpan(line_offsets_);
631+
}
632+
588633
absl::StatusOr<absl_nonnull SourcePtr> NewSource(absl::string_view content,
589634
std::string description) {
590635
return common_internal::NewSourceImpl(std::move(description), content,

common/source.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "absl/base/attributes.h"
2424
#include "absl/base/nullability.h"
25+
#include "absl/container/inlined_vector.h"
2526
#include "absl/status/statusor.h"
2627
#include "absl/strings/cord.h"
2728
#include "absl/strings/string_view.h"
@@ -36,6 +37,7 @@ class SourceImpl;
3637
} // namespace common_internal
3738

3839
class Source;
40+
class SourceSubrange;
3941

4042
// SourcePosition represents an offset in source text.
4143
using SourcePosition = int32_t;
@@ -94,6 +96,7 @@ class SourceContentView final {
9496

9597
private:
9698
friend class Source;
99+
friend class SourceSubrange;
97100

98101
constexpr SourceContentView() = default;
99102

@@ -178,6 +181,7 @@ class Source {
178181

179182
private:
180183
friend class common_internal::SourceImpl;
184+
friend class SourceSubrange;
181185

182186
Source() = default;
183187

@@ -187,6 +191,34 @@ class Source {
187191
SourcePosition position) const;
188192
};
189193

194+
// `SourceSubrange` is a view of a subrange fo an underlying `Source` object.
195+
// Intended to be used when the CEL expression is embedded in a larger text
196+
// representation (such as a.celpolicy).
197+
//
198+
// The parent `Source` must outlive this object.
199+
class SourceSubrange final : public Source {
200+
public:
201+
SourceSubrange(const Source& source ABSL_ATTRIBUTE_LIFETIME_BOUND,
202+
SourceRange range);
203+
204+
absl::string_view description() const ABSL_ATTRIBUTE_LIFETIME_BOUND override {
205+
return source_.description();
206+
}
207+
208+
// Returns a view of the underlying expression text.
209+
ContentView content() const ABSL_ATTRIBUTE_LIFETIME_BOUND override;
210+
211+
// Returns a `absl::Span` of `SourcePosition` which represent the positions
212+
// where new lines occur.
213+
absl::Span<const SourcePosition> line_offsets() const
214+
ABSL_ATTRIBUTE_LIFETIME_BOUND override;
215+
216+
private:
217+
const Source& source_;
218+
SourceRange range_;
219+
absl::InlinedVector<SourcePosition, 1> line_offsets_;
220+
};
221+
190222
using SourcePtr = std::unique_ptr<Source>;
191223

192224
absl::StatusOr<absl_nonnull SourcePtr> NewSource(

common/source_test.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,44 @@ TEST(Source, DisplayErrorLocationFullWidth) {
223223
"\n | ..^");
224224
}
225225

226+
TEST(SourceSubrange, Description) {
227+
ASSERT_OK_AND_ASSIGN(auto source, NewSource("hello world", "subrange-test"));
228+
SourceSubrange subrange(*source, SourceRange{0, 5});
229+
EXPECT_THAT(subrange.description(), Eq("subrange-test"));
230+
}
231+
232+
TEST(SourceSubrange, Content) {
233+
ASSERT_OK_AND_ASSIGN(auto source, NewSource("hello world", "subrange-test"));
234+
SourceSubrange subrange(*source, SourceRange{6, 11});
235+
EXPECT_THAT(subrange.content().ToString(), Eq("world"));
236+
}
237+
238+
TEST(SourceSubrange, ContentEmpty) {
239+
ASSERT_OK_AND_ASSIGN(auto source, NewSource("hello world", "subrange-test"));
240+
SourceSubrange subrange(*source, SourceRange{5, 5});
241+
EXPECT_THAT(subrange.content().ToString(), Eq(""));
242+
}
243+
244+
TEST(SourceSubrange, LineOffsetsNoNewlines) {
245+
ASSERT_OK_AND_ASSIGN(auto source,
246+
NewSource("hello\nworld\n", "subrange-test"));
247+
SourceSubrange subrange(*source, SourceRange{0, 5});
248+
EXPECT_THAT(subrange.line_offsets(), ElementsAre(6));
249+
}
250+
251+
TEST(SourceSubrange, LineOffsetsWithNewlines) {
252+
ASSERT_OK_AND_ASSIGN(auto source,
253+
NewSource("hello\nworld\ncel", "subrange-test"));
254+
SourceSubrange subrange(*source, SourceRange{0, 11});
255+
EXPECT_THAT(subrange.line_offsets(), ElementsAre(6, 12));
256+
}
257+
258+
TEST(SourceSubrange, LineOffsetsMiddleSubrange) {
259+
ASSERT_OK_AND_ASSIGN(auto source,
260+
NewSource("hello\nworld\ncel\ncpp", "subrange-test"));
261+
SourceSubrange subrange(*source, SourceRange{6, 15});
262+
EXPECT_THAT(subrange.line_offsets(), ElementsAre(6, 10));
263+
}
264+
226265
} // namespace
227266
} // namespace cel

0 commit comments

Comments
 (0)