feat: introduce toolchain helper (#194)

This is not final yet, I just want to test out with rules_python first.
Welcome to review though.

Also due to https://github.com/bazelbuild/bazel/issues/20297 i can't
write unit tests for the incompatible flag. I'll look for a workaround.
diff --git a/MODULE.bazel b/MODULE.bazel
index f34579f..a6ba283 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -15,3 +15,4 @@
 bazel_dep(name = "rules_cc", version = "0.0.1", dev_dependency = True)
 bazel_dep(name = "googletest", version = "1.11.0", dev_dependency = True, repo_name = "com_google_googletest")
 bazel_dep(name = "protobuf", version = "21.7", dev_dependency = True, repo_name = "com_google_protobuf")
+bazel_dep(name = "platforms", version = "0.0.8", dev_dependency = True)
diff --git a/WORKSPACE b/WORKSPACE
index ff6744f..2751746 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -11,6 +11,10 @@
 
 rules_proto_dev_deps()
 
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+
+bazel_skylib_workspace()
+
 load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
 
 protobuf_deps()
diff --git a/proto/proto_common.bzl b/proto/proto_common.bzl
index 6184971..3e15665 100644
--- a/proto/proto_common.bzl
+++ b/proto/proto_common.bzl
@@ -22,3 +22,33 @@
 proto_common = native_proto_common
 
 ProtoLangToolchainInfo = proto_common.ProtoLangToolchainInfo
+
+def _incompatible_toolchains_enabled():
+    return getattr(proto_common, "INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION", False)
+
+def _find_toolchain(ctx, legacy_attr, toolchain_type):
+    if _incompatible_toolchains_enabled():
+        toolchain = ctx.toolchains[toolchain_type]
+        if not toolchain:
+            fail("No toolchains registered for '%s'." % toolchain_type)
+        return toolchain.proto
+    else:
+        return getattr(ctx.attr, legacy_attr)[ProtoLangToolchainInfo]
+
+def _use_toolchain(toolchain_type):
+    if _incompatible_toolchains_enabled():
+        return [config_common.toolchain_type(toolchain_type, mandatory = False)]
+    else:
+        return []
+
+def _if_legacy_toolchain(legacy_attr_dict):
+    if _incompatible_toolchains_enabled():
+        return {}
+    else:
+        return legacy_attr_dict
+
+toolchains = struct(
+    use_toolchain = _use_toolchain,
+    find_toolchain = _find_toolchain,
+    if_legacy_toolchain = _if_legacy_toolchain,
+)
diff --git a/tests/BUILD b/tests/BUILD
index 2381713..57bb308 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -1,4 +1,18 @@
+load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
 load("//proto:defs.bzl", "proto_library")
+load("//proto:proto_common.bzl", "proto_common")
+
+config_setting(
+    name = "incompatible_enable_proto_toolchain_resolution",
+    flag_values = {
+        ":incompatible_enable_proto_toolchain_resolution_flag": "true",
+    },
+)
+
+bool_flag(
+    name = "incompatible_enable_proto_toolchain_resolution_flag",
+    build_setting_default = getattr(proto_common, "INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION", False),
+)
 
 proto_library(
     name = "empty_proto",
diff --git a/tests/proto_common/BUILD.bazel b/tests/proto_common/BUILD.bazel
new file mode 100644
index 0000000..82a7ecd
--- /dev/null
+++ b/tests/proto_common/BUILD.bazel
@@ -0,0 +1,3 @@
+load(":toolchains.bzl", "unittest_toolchains")
+
+unittest_toolchains()
diff --git a/tests/proto_common/toolchains.bzl b/tests/proto_common/toolchains.bzl
new file mode 100644
index 0000000..1f01370
--- /dev/null
+++ b/tests/proto_common/toolchains.bzl
@@ -0,0 +1,51 @@
+"unit tests for proto_common.toolchains"
+
+load("@bazel_skylib//lib:partial.bzl", "partial")
+load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
+load("//proto:proto_common.bzl", "toolchains")
+
+def _test_toolchains_without_incompatible_flag(ctx):
+    env = unittest.begin(ctx)
+
+    asserts.equals(env, {}, toolchains.if_legacy_toolchain({}))
+    asserts.equals(env, None, toolchains.if_legacy_toolchain(None))
+    asserts.equals(env, False, toolchains.if_legacy_toolchain(False))
+
+    return unittest.end(env)
+
+toolchains_without_incompatible_flags_test = unittest.make(_test_toolchains_without_incompatible_flag)
+
+def _test_toolchains_with_incompatible_flag(ctx):
+    env = unittest.begin(ctx)
+
+    asserts.equals(env, {}, toolchains.if_legacy_toolchain({}))
+    asserts.equals(env, {}, toolchains.if_legacy_toolchain(None))
+    asserts.equals(env, {}, toolchains.if_legacy_toolchain(False))
+    toolchain = toolchains.use_toolchain("//nonexistent:toolchain_type")
+    asserts.equals(env, 1, len(toolchain))
+    asserts.equals(env, False, toolchain[0].mandatory)
+    asserts.equals(env, str(Label("//nonexistent:toolchain_type")), str(toolchain[0].toolchain_type))
+
+    return unittest.end(env)
+
+toolchains_with_incompatible_flag_test = unittest.make(_test_toolchains_with_incompatible_flag)
+
+# buildifier: disable=unnamed-macro
+def unittest_toolchains():
+    unittest.suite(
+        "test_toolchains",
+        partial.make(
+            toolchains_without_incompatible_flags_test,
+            target_compatible_with = select({
+                "//tests:incompatible_enable_proto_toolchain_resolution": ["@platforms//:incompatible"],
+                "//conditions:default": [],
+            }),
+        ),
+        partial.make(
+            toolchains_with_incompatible_flag_test,
+            target_compatible_with = select({
+                "//tests:incompatible_enable_proto_toolchain_resolution": [],
+                "//conditions:default": ["@platforms//:incompatible"],
+            }),
+        ),
+    )