From 9f4c40a4dfeb8bd978b5337fce92d75dc4361803 Mon Sep 17 00:00:00 2001 From: matt rice Date: Sun, 17 Dec 2023 15:39:02 -0800 Subject: [PATCH] codify unstable api --- lrlex/Cargo.toml | 4 +++ lrlex/src/lib/ctbuilder.rs | 2 +- lrlex/src/lib/lexer.rs | 3 ++ lrlex/src/lib/mod.rs | 67 ++++++++++++++++++++++++++++++++++++++ lrlex/src/lib/parser.rs | 1 + lrpar/Cargo.toml | 2 ++ lrpar/src/lib/ctbuilder.rs | 4 ++- lrpar/src/lib/mod.rs | 67 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 148 insertions(+), 2 deletions(-) diff --git a/lrlex/Cargo.toml b/lrlex/Cargo.toml index acd337515..e4265c613 100644 --- a/lrlex/Cargo.toml +++ b/lrlex/Cargo.toml @@ -17,6 +17,10 @@ name = "lrlex" name = "lrlex" path = "src/lib/mod.rs" +[features] +_unstable_api = [] +_unsealed_unstable_traits = ["_unstable_api"] + [build-dependencies] vergen = { version = "8", default-features = false, features = ["build"] } diff --git a/lrlex/src/lib/ctbuilder.rs b/lrlex/src/lib/ctbuilder.rs index db716e638..f8013f53a 100644 --- a/lrlex/src/lib/ctbuilder.rs +++ b/lrlex/src/lib/ctbuilder.rs @@ -505,7 +505,7 @@ pub fn lexerdef() -> {lexerdef_type} {{ write!( outs, " - Rule::new({}, {}, {}, {}.to_string(), {}.to_vec(), {}, ®ex_options).unwrap(),", + Rule::new(::lrlex::unstable_api::InternalPublicApi, {}, {}, {}, {}.to_string(), {}.to_vec(), {}, ®ex_options).unwrap(),", tok_id, n, n_span, diff --git a/lrlex/src/lib/lexer.rs b/lrlex/src/lib/lexer.rs index 4d3acfc70..960330cef 100644 --- a/lrlex/src/lib/lexer.rs +++ b/lrlex/src/lib/lexer.rs @@ -74,7 +74,10 @@ impl Rule { /// Create a new `Rule`. This interface is unstable and should only be used by code generated /// by lrlex itself. #[doc(hidden)] + #[allow(private_interfaces)] + #[allow(clippy::too_many_arguments)] pub fn new( + _: crate::unstable_api::InternalPublicApi, tok_id: Option, name: Option, name_span: Span, diff --git a/lrlex/src/lib/mod.rs b/lrlex/src/lib/mod.rs index 190facaa3..64f79b2ca 100644 --- a/lrlex/src/lib/mod.rs +++ b/lrlex/src/lib/mod.rs @@ -183,3 +183,70 @@ macro_rules! lrlex_mod { include!(concat!(env!("OUT_DIR"), "/", $path, ".rs")); }; } + +/// This private module with pub items which is directly related to +/// the "Sealed trait" pattern. These items are used within the current +/// crate. See `unstable_api` module for enabling usage outside the crate. +mod unstable { + #![allow(unused)] + #![allow(unreachable_pub)] + pub struct UnstableApi; + pub trait UnstableTrait {} +} + +/// A module for lifting restrictions on visibility by enabling unstable features. +/// +/// See the sources for a complete list of features, and members. +pub mod unstable_api { + /// Unstable functions that take a value `UnstableApi` require + /// the "_unstable_api" feature. This feature controls + /// whether the value has `pub` visibility outside the crate. + #[cfg(feature = "_unstable_api")] + pub use unstable::UnstableApi; + + /// This is a a supertrait for traits that are considered to be Unstable. + /// Unstable traits do not provide any semver guarantees. + /// + /// Enabling the `_unsealed_unstable traits` makes this supertrait publicly + /// Visible. + /// + /// + /// Declaring an unstable Api within the crate: + /// ``` + /// // Within the crate use `crate::unstable::` . + /// pub trait Foo: crate::unstable::UnstableTrait { + /// fn foo(key: crate::unstable::UnstableApi); + /// } + /// ``` + /// + /// Deriving the trait outside the crate (requires feature `_unsealed_unstable_traits`) + /// ``` + /// struct Bar; + /// impl unstable_api::UnstableTrait for Bar{} + /// impl Foo for Bar { + /// fn foo(key: unstable_api::UnstableApi) { + /// ... + /// } + /// } + /// ``` + /// + /// + /// Calling an implementation of the trait outside the crate (requires feature `_unstable_api`: + /// ``` + /// let x: &dyn Foo = ...; + /// x.foo(unstable_api::UnstableApi); + /// ``` + #[cfg(feature = "_unsealed_unstable_traits")] + pub use unstable::UnstableTrait; + + /// An value that acts as a key to inform callers that they are + /// calling an unstable internal api. This value is public by default. + /// Access to it does not require any features to be enabled. + /// + /// Q. When this should be used? + /// + /// A. When generated code needs to call internal api within it, + /// where you do not want the caller to have to enable any features + /// to use the generated code. + pub struct InternalPublicApi; +} diff --git a/lrlex/src/lib/parser.rs b/lrlex/src/lib/parser.rs index c383840d9..3d8f6295f 100644 --- a/lrlex/src/lib/parser.rs +++ b/lrlex/src/lib/parser.rs @@ -449,6 +449,7 @@ where .unwrap_or_else(|_| panic!("StorageT::try_from \ failed on {} (if StorageT is an unsigned integer type, this probably means that {} exceeds the type's maximum value)", rules_len, rules_len)); let rule = Rule::new( + crate::unstable_api::InternalPublicApi, Some(tok_id), name, name_span, diff --git a/lrpar/Cargo.toml b/lrpar/Cargo.toml index 839f90497..d74b5a49c 100644 --- a/lrpar/Cargo.toml +++ b/lrpar/Cargo.toml @@ -17,6 +17,8 @@ path = "src/lib/mod.rs" [features] serde = [] +_unstable_api = [] +_unsealed_unstable_traits = ["_unstable_api"] [build-dependencies] vergen = { version = "8", default-features = false, features = ["build"] } diff --git a/lrpar/src/lib/ctbuilder.rs b/lrpar/src/lib/ctbuilder.rs index 410c76d3e..8251aa055 100644 --- a/lrpar/src/lib/ctbuilder.rs +++ b/lrpar/src/lib/ctbuilder.rs @@ -1332,8 +1332,10 @@ where /// always return `None`, even if the grammar actually has conflicts. /// /// **Note: The conflicts feature is currently unstable and may change in the future.** + #[allow(private_interfaces)] pub fn conflicts( &self, + _: crate::unstable::UnstableApi, ) -> Option<( &YaccGrammar, &StateGraph, @@ -1378,7 +1380,7 @@ C : 'a';" .output_path(file_path.with_extension("ignored")) .build() .unwrap() - .conflicts() + .conflicts(crate::unstable::UnstableApi) { Some((_, _, _, conflicts)) => { assert_eq!(conflicts.sr_len(), 1); diff --git a/lrpar/src/lib/mod.rs b/lrpar/src/lib/mod.rs index 45af9eed3..6926a7f85 100644 --- a/lrpar/src/lib/mod.rs +++ b/lrpar/src/lib/mod.rs @@ -228,3 +228,70 @@ macro_rules! lrpar_mod { note = "Please import this as `cfgrammar::Span` instead" )] pub use cfgrammar::Span; + +/// This private module with pub items which is directly related to +/// the "Sealed trait" pattern. These items are used within the current +/// crate. See `unstable_api` module for enabling usage outside the crate. +mod unstable { + #![allow(unused)] + #![allow(unreachable_pub)] + pub struct UnstableApi; + pub trait UnstableTrait {} +} + +/// A module for lifting restrictions on visibility by enabling unstable features. +/// +/// See the sources for a complete list of features, and members. +pub mod unstable_api { + /// Unstable functions that take a value `UnstableApi` require + /// the "_unstable_api" feature. This feature controls + /// whether the value has `pub` visibility outside the crate. + #[cfg(feature = "_unstable_api")] + pub use unstable::UnstableApi; + + /// This is a a supertrait for traits that are considered to be Unstable. + /// Unstable traits do not provide any semver guarantees. + /// + /// Enabling the `_unsealed_unstable traits` makes this supertrait publicly + /// Visible. + /// + /// + /// Declaring an unstable Api within the crate: + /// ``` + /// // Within the crate use `crate::unstable::` . + /// pub trait Foo: crate::unstable::UnstableTrait { + /// fn foo(key: crate::unstable::UnstableApi); + /// } + /// ``` + /// + /// Deriving the trait outside the crate (requires feature `_unsealed_unstable_traits`) + /// ``` + /// struct Bar; + /// impl unstable_api::UnstableTrait for Bar{} + /// impl Foo for Bar { + /// fn foo(key: unstable_api::UnstableApi) { + /// ... + /// } + /// } + /// ``` + /// + /// + /// Calling an implementation of the trait outside the crate (requires feature `_unstable_api`: + /// ``` + /// let x: &dyn Foo = ...; + /// x.foo(unstable_api::UnstableApi); + /// ``` + #[cfg(feature = "_unsealed_unstable_traits")] + pub use unstable::UnstableTrait; + + /// An value that acts as a key to inform callers that they are + /// calling an unstable internal api. This value is public by default. + /// Access to it does not require any features to be enabled. + /// + /// Q. When this should be used? + /// + /// A. When generated code needs to call internal api within it, + /// where you do not want the caller to have to enable any features + /// to use the generated code. + pub struct InternalPublicApi; +}