Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions checker/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ cc_library(
hdrs = ["type_check_env.h"],
deps = [
":descriptor_pool_type_introspector",
":proto_type_mask",
":proto_type_mask_registry",
"//common:constant",
"//common:container",
"//common:decl",
Expand All @@ -76,6 +78,7 @@ cc_library(
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
Expand Down Expand Up @@ -129,6 +132,8 @@ cc_library(
deps = [
":format_type_name",
":namespace_generator",
":proto_type_mask",
":proto_type_mask_registry",
":type_check_env",
":type_inference_context",
"//checker:checker_options",
Expand Down Expand Up @@ -310,3 +315,88 @@ cc_test(
"@com_google_absl//absl/types:optional",
],
)

cc_library(
name = "field_path",
srcs = ["field_path.cc"],
hdrs = ["field_path.h"],
deps = [
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/types:span",
],
)

cc_test(
name = "field_path_test",
srcs = ["field_path_test.cc"],
deps = [
":field_path",
"//internal:testing",
"@com_google_absl//absl/strings",
],
)

cc_library(
name = "proto_type_mask",
srcs = ["proto_type_mask.cc"],
hdrs = ["proto_type_mask.h"],
deps = [
":field_path",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/strings",
],
)

cc_test(
name = "proto_type_mask_test",
srcs = ["proto_type_mask_test.cc"],
deps = [
":field_path",
":proto_type_mask",
"//internal:testing",
"@com_google_absl//absl/strings",
],
)

cc_library(
name = "proto_type_mask_registry",
srcs = ["proto_type_mask_registry.cc"],
hdrs = ["proto_type_mask_registry.h"],
deps = [
":field_path",
":proto_type_mask",
"//common:type",
"//internal:status_macros",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
"@com_google_protobuf//:protobuf",
],
)

cc_test(
name = "proto_type_mask_registry_test",
srcs = ["proto_type_mask_registry_test.cc"],
deps = [
":proto_type_mask",
":proto_type_mask_registry",
"//internal:testing",
"//internal:testing_descriptor_pool",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:status_matchers",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
],
)
30 changes: 30 additions & 0 deletions checker/internal/field_path.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "checker/internal/field_path.h"

#include <string>

#include "absl/strings/str_join.h"
#include "absl/strings/substitute.h"

namespace cel::checker_internal {

std::string FieldPath::DebugString() const {
return absl::Substitute(
"FieldPath { field path: '$0', field selection: {'$1'} }", path_,
absl::StrJoin(field_selection_, "', '"));
}

} // namespace cel::checker_internal
73 changes: 73 additions & 0 deletions checker/internal/field_path.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_
#define THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_

#include <string>
#include <utility>
#include <vector>

#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"

namespace cel::checker_internal {

// Represents a single path within a FieldMask.
class FieldPath {
public:
explicit FieldPath(std::string path)
: path_(std::move(path)),
field_selection_(absl::StrSplit(path_, kPathDelimiter)) {}

absl::string_view GetPath() const { return path_; }

absl::Span<const std::string> GetFieldSelection() const {
return field_selection_;
}

// Returns the first field name in the path.
std::string GetFieldName() const { return field_selection_.front(); }

template <typename Sink>
friend void AbslStringify(Sink& sink, const FieldPath& field_path) {
absl::Format(&sink, "%v", field_path.DebugString());
};

private:
static constexpr char kPathDelimiter = '.';

std::string DebugString() const;

// The input path. For example: "f.b.d".
std::string path_;
// The list of nested field names in the path. For example: {"f", "b", "d"}.
std::vector<std::string> field_selection_;
};

inline bool operator==(const FieldPath& lhs, const FieldPath& rhs) {
return lhs.GetFieldSelection() == rhs.GetFieldSelection();
}

// Compares the field selections in the field paths.
// This is only intended as an arbitrary ordering for a set.
inline bool operator<(const FieldPath& lhs, const FieldPath& rhs) {
return lhs.GetFieldSelection() < rhs.GetFieldSelection();
}

} // namespace cel::checker_internal

#endif // THIRD_PARTY_CEL_CPP_CHECKER_FIELD_PATH_H_
79 changes: 79 additions & 0 deletions checker/internal/field_path_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "checker/internal/field_path.h"

#include "absl/strings/str_cat.h"
#include "internal/testing.h"

namespace cel::checker_internal {
namespace {

using ::testing::ElementsAre;

TEST(FieldPathTest, EmptyPathReturnsEmptyString) {
FieldPath field_path("");
EXPECT_EQ(field_path.GetPath(), "");
EXPECT_THAT(field_path.GetFieldSelection(), ElementsAre(""));
EXPECT_EQ(field_path.GetFieldName(), "");
}

TEST(FieldPathTest, DelimiterPathReturnsEmptyStrings) {
FieldPath field_path(".");
EXPECT_EQ(field_path.GetPath(), ".");
EXPECT_THAT(field_path.GetFieldSelection(), ElementsAre("", ""));
EXPECT_EQ(field_path.GetFieldName(), "");
}

TEST(FieldPathTest, FieldPathReturnsFields) {
FieldPath field_path("resource.name.other_field");
EXPECT_EQ(field_path.GetPath(), "resource.name.other_field");
EXPECT_THAT(field_path.GetFieldSelection(),
ElementsAre("resource", "name", "other_field"));
EXPECT_EQ(field_path.GetFieldName(), "resource");
}

TEST(FieldPathTest, AbslStringifyPrintsFieldSelection) {
FieldPath field_path("resource.name");
EXPECT_EQ(absl::StrCat(field_path),
"FieldPath { field path: 'resource.name', field selection: "
"{'resource', 'name'} }");
}

TEST(FieldPathTest, EqualsComparesFieldSelectionAndReturnsTrue) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.name");
EXPECT_TRUE(field_path_1 == field_path_2);
}

TEST(FieldPathTest, EqualsComparesFieldSelectionAndReturnsFalse) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.type");
EXPECT_FALSE(field_path_1 == field_path_2);
}

TEST(FieldPathTest, LessThanComparesFieldSelectionAndReturnsTrue) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.type");
EXPECT_TRUE(field_path_1 < field_path_2);
}

TEST(FieldPathTest, LessThanComparesFieldSelectionAndReturnsFalse) {
FieldPath field_path_1("resource.name");
FieldPath field_path_2("resource.name");
EXPECT_FALSE(field_path_1 < field_path_2);
}

} // namespace
} // namespace cel::checker_internal
38 changes: 38 additions & 0 deletions checker/internal/proto_type_mask.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "checker/internal/proto_type_mask.h"

#include <string>
#include <vector>

#include "absl/strings/str_join.h"
#include "absl/strings/substitute.h"
#include "checker/internal/field_path.h"

namespace cel::checker_internal {

std::string ProtoTypeMask::DebugString() const {
// Represent each FieldPath by its path because it is easiest to read.
std::vector<std::string> paths;
paths.reserve(field_paths_.size());
for (const FieldPath& field_path : field_paths_) {
paths.emplace_back(field_path.GetPath());
}
return absl::Substitute(
"ProtoTypeMask { type name: '$0', field paths: { '$1' } }", type_name_,
absl::StrJoin(paths, "', '"));
}

} // namespace cel::checker_internal
Loading