mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 18:03:25 +00:00
Refactor project structure
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
bd878dd1c9
commit
e3c227ae06
2
kernel/libs/int-to-c-enum/.gitignore
vendored
Normal file
2
kernel/libs/int-to-c-enum/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
/Cargo.lock
|
13
kernel/libs/int-to-c-enum/Cargo.toml
Normal file
13
kernel/libs/int-to-c-enum/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "int-to-c-enum"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
int-to-c-enum-derive = { path = "derive", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["derive"]
|
||||
derive = ["int-to-c-enum-derive"]
|
35
kernel/libs/int-to-c-enum/README.md
Normal file
35
kernel/libs/int-to-c-enum/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# TryFromInt - A convenient derive macro for converting an integer to an enum
|
||||
|
||||
## Quick Start
|
||||
To use this crate, first add this crate to your `Cargo.toml`.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
int-to-c-enum = "0.1.0"
|
||||
```
|
||||
|
||||
You can use this macro for a [C-like enum](https://doc.rust-lang.org/stable/rust-by-example/custom_types/enum/c_like.html).
|
||||
|
||||
```rust
|
||||
use int_to_c_enum::TryFromInt;
|
||||
#[repr(u8)]
|
||||
#[derive(TryFromInt, Debug)]
|
||||
pub enum Color {
|
||||
Red = 1,
|
||||
Blue = 2,
|
||||
Green = 3,
|
||||
}
|
||||
```
|
||||
|
||||
Then, you can use `try_from` function for this enum.
|
||||
```rust
|
||||
fn main() {
|
||||
let color = Color::try_from(1).unwrap();
|
||||
println!("color = {color:?}"); // color = Red;
|
||||
}
|
||||
```
|
||||
|
||||
## Introduction
|
||||
This crate provides a derive procedural macro named `TryFromInt`. This macro will automatically implement [TryFrom](https://doc.rust-lang.org/core/convert/trait.TryFrom.html) trait for enums that meet the following requirements:
|
||||
1. The enum must have a primitive repr, i.e., the enum should have attribute like #[repr(u8)], #[repr(u32)], etc. The type parameter of TryFrom will be the repr, e.g., in the `QuickStart` example, the macro will implment `TryFrom<u8>` for `Color`.
|
||||
2. The enum must consist solely of unit variants, which is called [units only enum](https://doc.rust-lang.org/reference/items/enumerations.html#unit-only-enum). Each field should have an **explicit discriminant**.
|
14
kernel/libs/int-to-c-enum/derive/Cargo.toml
Normal file
14
kernel/libs/int-to-c-enum/derive/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "int-to-c-enum-derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "2.0.15", features = ["parsing"] }
|
110
kernel/libs/int-to-c-enum/derive/src/lib.rs
Normal file
110
kernel/libs/int-to-c-enum/derive/src/lib.rs
Normal file
@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote, TokenStreamExt};
|
||||
use syn::{parse_macro_input, Attribute, Data, DataEnum, DeriveInput, Generics};
|
||||
|
||||
const ALLOWED_REPRS: &[&str] = &[
|
||||
"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize", "isize",
|
||||
];
|
||||
const VALUE_NAME: &str = "value";
|
||||
const REPR_PATH: &str = "repr";
|
||||
|
||||
#[proc_macro_derive(TryFromInt)]
|
||||
pub fn derive_try_from_num(input_token: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input_token as DeriveInput);
|
||||
expand_derive_try_from_num(input).into()
|
||||
}
|
||||
|
||||
fn expand_derive_try_from_num(input: DeriveInput) -> TokenStream {
|
||||
let attrs = input.attrs;
|
||||
let ident = input.ident;
|
||||
let generics = input.generics;
|
||||
if let Data::Enum(data_enum) = input.data {
|
||||
impl_try_from(data_enum, attrs, generics, ident)
|
||||
} else {
|
||||
panic!("cannot derive TryFromInt for structs or union.")
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_try_from(
|
||||
data_enum: DataEnum,
|
||||
attrs: Vec<Attribute>,
|
||||
generics: Generics,
|
||||
ident: Ident,
|
||||
) -> TokenStream {
|
||||
let valid_repr = if let Some(valid_repr) = has_valid_repr(attrs) {
|
||||
format_ident!("{}", valid_repr)
|
||||
} else {
|
||||
panic!(
|
||||
"{} does not have invalid repr to implement TryFromInt.",
|
||||
ident
|
||||
);
|
||||
};
|
||||
|
||||
for variant in &data_enum.variants {
|
||||
if variant.discriminant.is_none() {
|
||||
panic!("Enum can only have fields like Variant=1");
|
||||
}
|
||||
}
|
||||
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
let fn_body = fn_body_tokens(VALUE_NAME, &data_enum, ident.clone());
|
||||
let param = format_ident!("{}", VALUE_NAME);
|
||||
quote!(
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::core::convert::TryFrom<#valid_repr> #type_generics for #ident #where_clause {
|
||||
type Error = ::int_to_c_enum::TryFromIntError;
|
||||
fn try_from(#param: #valid_repr) -> ::core::result::Result<Self, Self::Error> {
|
||||
#fn_body
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn fn_body_tokens(value_name: &str, data_enum: &DataEnum, ident: Ident) -> TokenStream {
|
||||
let mut match_bodys = quote!();
|
||||
for variant in &data_enum.variants {
|
||||
let (_, value) = variant
|
||||
.discriminant
|
||||
.as_ref()
|
||||
.expect("Each field must be assigned a discriminant value explicitly");
|
||||
let vairant_ident = &variant.ident;
|
||||
let statement = quote!(#value => ::core::result::Result::Ok(#ident::#vairant_ident),);
|
||||
match_bodys.append_all(statement);
|
||||
}
|
||||
match_bodys.append_all(
|
||||
quote!(_ => core::result::Result::Err(::int_to_c_enum::TryFromIntError::InvalidValue),),
|
||||
);
|
||||
let param = format_ident!("{}", value_name);
|
||||
quote!(match #param {
|
||||
#match_bodys
|
||||
})
|
||||
}
|
||||
|
||||
fn has_valid_repr(attrs: Vec<Attribute>) -> Option<&'static str> {
|
||||
for attr in attrs {
|
||||
if let Some(repr) = parse_repr(attr) {
|
||||
return Some(repr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_repr(attr: Attribute) -> Option<&'static str> {
|
||||
if !attr.path().is_ident(REPR_PATH) {
|
||||
return None;
|
||||
}
|
||||
let mut repr = None;
|
||||
attr.parse_nested_meta(|meta| {
|
||||
for allowed_repr in ALLOWED_REPRS {
|
||||
if meta.path.is_ident(*allowed_repr) {
|
||||
repr = Some(*allowed_repr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.ok()?;
|
||||
repr
|
||||
}
|
49
kernel/libs/int-to-c-enum/src/lib.rs
Normal file
49
kernel/libs/int-to-c-enum/src/lib.rs
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! This crate provides a derive macro named TryFromInt. This macro can be used to automatically implement TryFrom trait
|
||||
//! for [C-like enums](https://doc.rust-lang.org/stable/rust-by-example/custom_types/enum/c_like.html).
|
||||
//!
|
||||
//! Currently, this macro only supports enums with [explicit discriminants](https://doc.rust-lang.org/reference/items/enumerations.html#explicit-discriminants).
|
||||
//!
|
||||
//! Below is a simple example. We derive macro `TryFromInt` for an enum `Color`.
|
||||
//! ```rust
|
||||
//! use int_to_c_enum::TryFromInt;
|
||||
//! #[repr(u8)]
|
||||
//! #[derive(TryFromInt, Eq, PartialEq)]
|
||||
//! pub enum Color {
|
||||
//! Red = 1,
|
||||
//! Yellow = 2,
|
||||
//! Blue = 3,
|
||||
//! }
|
||||
//! // Then, we can use method `try_from` for `Color`.
|
||||
//! let color = Color::try_from(1).unwrap();
|
||||
//! assert!(color == Color::Red);
|
||||
//! ```
|
||||
//!
|
||||
//! The `TryFromInt` macro will automatically implement trait `TryFrom<u8>` for `Color`.
|
||||
//! After macro expansion, the generated code looks like as follows:
|
||||
//! ```ignore
|
||||
//! impl TryFrom<u8> for Color {
|
||||
//! type Error = TryFromIntError;
|
||||
//! fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
//! match value {
|
||||
//! 1 => Ok(Color::Red),
|
||||
//! 2 => Ok(Color::Yellow),
|
||||
//! 3 => Ok(Color::Blue),
|
||||
//! _ => Err(TryFromIntError::InvalidValue),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
/// Error type for TryFromInt derive macro
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TryFromIntError {
|
||||
InvalidValue,
|
||||
}
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
pub use int_to_c_enum_derive::TryFromInt;
|
24
kernel/libs/int-to-c-enum/tests/regression.rs
Normal file
24
kernel/libs/int-to-c-enum/tests/regression.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use int_to_c_enum::TryFromInt;
|
||||
|
||||
#[derive(TryFromInt, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
enum Color {
|
||||
Red = 1,
|
||||
Blue = 2,
|
||||
Green = 3,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conversion() {
|
||||
let color = Color::try_from(1).unwrap();
|
||||
println!("color = {color:?}");
|
||||
assert!(color == Color::Red);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_value() {
|
||||
let color = Color::try_from(4);
|
||||
assert!(color.is_err());
|
||||
}
|
Reference in New Issue
Block a user