-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 6a6eade
Showing
9 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "stability" | ||
version = "0.1.0" | ||
description = "Rust API stability attributes for the rest of us." | ||
authors = ["Stephen M. Coakley <[email protected]>"] | ||
license = "MIT" | ||
repository = "https://github.com/sagebind/stability" | ||
documentation = "https://docs.rs/stability/" | ||
readme = "README.md" | ||
edition = "2018" | ||
|
||
[dependencies] | ||
quote = "1" | ||
|
||
[dependencies.syn] | ||
version = "1" | ||
features = ["derive", "full"] | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[workspace] | ||
members = ["example"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2020 Stephen M. Coakley | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Stability | ||
|
||
Rust API stability attributes for the rest of us. | ||
|
||
[](https://crates.io/crates/stability) | ||
[][documentation] | ||
[](LICENSE) | ||
[](https://github.com/sagebind/stability/actions) | ||
|
||
## Overview | ||
|
||
This crate provides attribute macros for specifying API stability of public API items of a crate. For a quick example: | ||
|
||
```rust | ||
/// This function does something really risky! | ||
/// | ||
/// Don't use it yet! | ||
#[stability::unstable(feature = "risky-function")] | ||
pub fn risky_function() { | ||
unimplemented!() | ||
} | ||
``` | ||
|
||
Please check out the [documentation] for detailed usage. | ||
|
||
## Installation | ||
|
||
Install via Cargo by adding to your `Cargo.toml` file: | ||
|
||
```toml | ||
[dependencies] | ||
stability = "0.1" | ||
``` | ||
|
||
### Supported Rust versions | ||
|
||
The current release is only guaranteed to work with the latest stable Rust compiler. | ||
|
||
## License | ||
|
||
This project's source code and documentation are licensed under the MIT license. See the [LICENSE](LICENSE) file for details. | ||
|
||
|
||
[documentation]: https://docs.rs/stability |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "stability-example" | ||
version = "0.0.0" | ||
description = "Example crate demonstrating stablity usage." | ||
authors = ["Stephen M. Coakley <[email protected]>"] | ||
edition = "2018" | ||
publish = false | ||
|
||
[features] | ||
unstable-risky-function = [] | ||
|
||
[dependencies.stability] | ||
path = "../" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//! This is an example library demonstrating various attributes from the | ||
//! stability crate. | ||
/// This function does something really risky! | ||
/// | ||
/// Don't use it yet! | ||
#[stability::unstable(feature = "risky-function")] | ||
pub fn risky_function() { | ||
unimplemented!() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
//! This crate provides attribute macros for specifying API stability of public | ||
//! API items of a crate. | ||
//! | ||
//! The Rust standard library has a concept of [API | ||
//! stability](https://rustc-dev-guide.rust-lang.org/stability.html) and custom | ||
//! attributes for managing that on a per-item basis, but most of these | ||
//! attributes are not available for normal crates to use, with the exception of | ||
//! the | ||
//! [`#[deprecated]`](https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute) | ||
//! attribute. This crate seeks to provide similar attributes on stable Rust, | ||
//! though tuned more toward what the needs of normal crate authors. | ||
//! | ||
//! For complete examples of how to use this crate, check out the source code | ||
//! for the [`stability-example` | ||
//! crate](https://github.com/sagebind/stability/tree/master/example) included | ||
//! in the stability repository. | ||
//! | ||
//! Currently, only the [`#[unstable]`][macro@unstable] attribute is available. | ||
use proc_macro::TokenStream; | ||
use syn::{Item, parse_macro_input}; | ||
|
||
mod unstable; | ||
|
||
/// Mark an API as unstable. | ||
/// | ||
/// You can apply this attribute to an item in your public API that you would | ||
/// like to expose to users, but are not yet ready for general use. This is | ||
/// useful when you want to let users try out some new functionality for an API | ||
/// you haven't finished testing or designing, or for whatever reason do not | ||
/// want to commit any stability guarantees for. | ||
/// | ||
/// This attribute does the following things to annotated items: | ||
/// | ||
/// - Changes the visibility of the item from `pub` to `pub(crate)`, unless a | ||
/// certain crate feature is enabled. | ||
/// - Appends an "Availability" section to the item's documentation that notes | ||
/// that the item is unstable, and indicates the name of the crate feature to | ||
/// enable it. | ||
/// | ||
/// Note that unlike the `#[unstable]` attribute used [in the standard | ||
/// library](https://rustc-dev-guide.rust-lang.org/stability.html), this | ||
/// attribute does not apply itself recursively to child items. | ||
/// | ||
/// Applying this attribute to non-`pub` items is pointless and does nothing. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// The `unstable` attribute supports optional arguments that can be passed to | ||
/// control its behavior. | ||
/// | ||
/// - `feature`: Specify the name of the unstable feature that should control | ||
/// this item's availability. The crate feature will have the string | ||
/// `unstable-` prepended to it. If not specified, it will be guarded by a | ||
/// catch-all `unstable` feature. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// /// This function does something really risky! | ||
/// /// | ||
/// /// Don't use it yet! | ||
/// #[stability::unstable(feature = "risky-function")] | ||
/// pub fn risky_function() { | ||
/// unimplemented!() | ||
/// } | ||
/// ``` | ||
#[proc_macro_attribute] | ||
pub fn unstable(args: TokenStream, item: TokenStream) -> TokenStream { | ||
let args = parse_macro_input!(args as syn::AttributeArgs); | ||
let attr = unstable::UnstableAttribute::from(args); | ||
|
||
match parse_macro_input!(item as Item) { | ||
Item::Enum(item_enum) => attr.expand(item_enum), | ||
Item::Fn(item_fn) => attr.expand(item_fn), | ||
Item::Mod(item_mod) => attr.expand(item_mod), | ||
Item::Struct(item_struct) => attr.expand(item_struct), | ||
Item::Trait(item_trait) => attr.expand(item_trait), | ||
_ => panic!("unsupported item type"), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use proc_macro::TokenStream; | ||
use quote::{quote, ToTokens}; | ||
use syn::{parse_quote, Visibility}; | ||
|
||
#[derive(Debug)] | ||
pub(crate) struct UnstableAttribute { | ||
feature: Option<String>, | ||
} | ||
|
||
impl UnstableAttribute { | ||
fn crate_feature_name(&self) -> String { | ||
if let Some(name) = self.feature.as_deref() { | ||
format!("unstable-{}", name) | ||
} else { | ||
String::from("unstable") | ||
} | ||
} | ||
|
||
pub(crate) fn expand(&self, mut item: impl ItemLike + ToTokens + Clone) -> TokenStream { | ||
// We only care about public items. | ||
if item.is_public() { | ||
let feature_name = self.crate_feature_name(); | ||
|
||
let doc_addendum = format!("\n\ | ||
# Availability\n\ | ||
\n\ | ||
**This API is marked as unstable** and is only available when \ | ||
the `{}` crate feature is enabled. This comes with no stability \ | ||
guarantees, and could be changed or removed at any time.\ | ||
", feature_name); | ||
item.push_attr(parse_quote! { | ||
#[doc = #doc_addendum] | ||
}); | ||
|
||
let mut hidden_item = item.clone(); | ||
*hidden_item.visibility_mut() = parse_quote! { | ||
pub(crate) | ||
}; | ||
|
||
TokenStream::from(quote! { | ||
#[cfg(feature = #feature_name)] | ||
#item | ||
|
||
#[cfg(not(feature = #feature_name))] | ||
#[allow(dead_code)] | ||
#hidden_item | ||
}) | ||
} else { | ||
item.into_token_stream().into() | ||
} | ||
} | ||
} | ||
|
||
impl From<syn::AttributeArgs> for UnstableAttribute { | ||
fn from(args: syn::AttributeArgs) -> Self { | ||
let mut feature = None; | ||
|
||
for arg in args { | ||
match arg { | ||
syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) => { | ||
if name_value.path.is_ident("feature") { | ||
match name_value.lit { | ||
syn::Lit::Str(s) => feature = Some(s.value()), | ||
_ => panic!(), | ||
} | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
Self { | ||
feature, | ||
} | ||
} | ||
} | ||
|
||
pub(crate) trait ItemLike { | ||
fn attrs(&self) -> &[syn::Attribute]; | ||
|
||
fn push_attr(&mut self, attr: syn::Attribute); | ||
|
||
fn visibility(&self) -> &Visibility; | ||
|
||
fn visibility_mut(&mut self) -> &mut Visibility; | ||
|
||
fn is_public(&self) -> bool { | ||
matches!(self.visibility(), Visibility::Public(_)) | ||
} | ||
} | ||
|
||
macro_rules! impl_has_visibility { | ||
($($ty:ty),+) => { | ||
$( | ||
impl ItemLike for $ty { | ||
fn attrs(&self) -> &[syn::Attribute] { | ||
&self.attrs | ||
} | ||
|
||
fn push_attr(&mut self, attr: syn::Attribute) { | ||
self.attrs.push(attr); | ||
} | ||
|
||
fn visibility(&self) -> &Visibility { | ||
&self.vis | ||
} | ||
|
||
fn visibility_mut(&mut self) -> &mut Visibility { | ||
&mut self.vis | ||
} | ||
} | ||
)* | ||
}; | ||
} | ||
|
||
impl_has_visibility!(syn::ItemEnum, syn::ItemFn, syn::ItemMod, syn::ItemStruct, syn::ItemTrait); |