Skip to content

Commit 8c5894a

Browse files
authored
Merge pull request #18 from jmjoy/0.2.0-alpha.3-dev
2 parents bcf4704 + f468c46 commit 8c5894a

File tree

10 files changed

+215
-50
lines changed

10 files changed

+215
-50
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
os:
2323
- ubuntu-latest
2424
php-version:
25-
# - "7.0"
25+
- "7.0"
2626
- "7.1"
2727
- "7.2"
2828
- "7.3"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ A library that allows us to write PHP extensions using pure Rust and using safe
2525

2626
*version*
2727

28-
- [ ] 7.0
28+
- [x] 7.0
2929
- [x] 7.1
3030
- [x] 7.2
3131
- [x] 7.3

examples/hello/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ use phper::{
33
classes::{DynamicClass, Visibility},
44
functions::Argument,
55
ini::{Ini, Policy},
6-
modules::{Module, ModuleArgs},
6+
modules::{Module, ModuleContext},
77
objects::Object,
88
php_get_module,
99
values::Val,
1010
};
1111

12-
fn module_init(_args: ModuleArgs) -> bool {
12+
fn module_init(_args: ModuleContext) -> bool {
1313
true
1414
}
1515

phper-macros/src/derives.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use proc_macro::TokenStream;
2+
use proc_macro2::TokenStream as TokenStream2;
3+
use quote::quote;
4+
use syn::{Attribute, Data, DeriveInput, Fields, Meta};
5+
6+
pub(crate) fn derive_throwable(input: DeriveInput) -> syn::Result<TokenStream> {
7+
let crate_ident = parse_throwable_crate_ident(&input);
8+
let exception = parse_throwable_attrs(&input)?;
9+
parse_throwable_input(&input, crate_ident, exception)
10+
}
11+
12+
fn parse_throwable_crate_ident(input: &DeriveInput) -> TokenStream2 {
13+
let has_throwable_crate = attributes_find_ident(&input.attrs, "throwable_crate");
14+
let crate_ident = if has_throwable_crate.is_some() {
15+
quote! { crate }
16+
} else {
17+
quote! { phper }
18+
};
19+
crate_ident
20+
}
21+
22+
fn parse_throwable_attrs(input: &DeriveInput) -> syn::Result<TokenStream2> {
23+
let attr = attributes_find_ident(&input.attrs, "throwable");
24+
attr.map(|attr| {
25+
attr.parse_args::<Meta>().and_then(|meta| match meta {
26+
Meta::NameValue(name_value) => {
27+
if !name_value.path.is_ident("class") {
28+
Err(syn::Error::new_spanned(
29+
&attr,
30+
"now only support #[throwable(error = ?)] for enum",
31+
))
32+
} else {
33+
let lit = name_value.lit;
34+
Ok(quote! { #lit })
35+
}
36+
}
37+
_ => Err(syn::Error::new_spanned(
38+
&attr,
39+
"now only support #[throwable(error = ?)] for enum",
40+
)),
41+
})
42+
})
43+
.unwrap_or_else(|| Ok(quote! { "Exception" }))
44+
}
45+
46+
fn parse_throwable_input(
47+
input: &DeriveInput,
48+
crate_ident: TokenStream2,
49+
exception: TokenStream2,
50+
) -> syn::Result<TokenStream> {
51+
let input_ident = &input.ident;
52+
53+
match &input.data {
54+
Data::Enum(e) => {
55+
let mut transparent_idents = Vec::new();
56+
57+
for variant in &e.variants {
58+
let attr = attributes_find_ident(&variant.attrs, "throwable");
59+
match attr {
60+
Some(attr) => {
61+
if attr.tokens.to_string() != "(transparent)" {
62+
return Err(syn::Error::new_spanned(
63+
&attr,
64+
"now only support #[throwable(transparent)] for variant",
65+
));
66+
}
67+
match &variant.fields {
68+
Fields::Unnamed(f) if f.unnamed.len() == 1 => {
69+
transparent_idents.push(variant.ident.clone());
70+
}
71+
_ => {
72+
return Err(syn::Error::new_spanned(
73+
&variant,
74+
"now only support unnamed field with one item mark attribute #[throwable]",
75+
));
76+
}
77+
}
78+
}
79+
None => continue,
80+
}
81+
}
82+
83+
let mut class_entry_arms = transparent_idents
84+
.iter()
85+
.map(|i| {
86+
quote! { Self::#i(e) => #crate_ident::errors::Throwable::class_entry(e), }
87+
})
88+
.collect::<Vec<_>>();
89+
class_entry_arms.push(quote! { _ => ClassEntry::from_globals(#exception).unwrap(), });
90+
91+
let mut code_arms = transparent_idents
92+
.iter()
93+
.map(|i| {
94+
quote! { Self::#i(e) => #crate_ident::errors::Throwable::code(e), }
95+
})
96+
.collect::<Vec<_>>();
97+
code_arms.push(quote! { _ => 0, });
98+
99+
let mut message_arms = transparent_idents
100+
.iter()
101+
.map(|i| {
102+
quote! { Self::#i(e) => #crate_ident::errors::Throwable::message(e), }
103+
})
104+
.collect::<Vec<_>>();
105+
message_arms.push(quote! { _ => std::string::ToString::to_string(&self), });
106+
107+
Ok((quote! {
108+
impl #crate_ident::errors::Throwable for #input_ident {
109+
fn class_entry(&self) -> &#crate_ident::classes::StatelessClassEntry {
110+
match self {
111+
#(#class_entry_arms)*
112+
}
113+
}
114+
115+
fn code(&self) -> u64 {
116+
match self {
117+
#(#code_arms)*
118+
}
119+
}
120+
121+
fn message(&self) -> std::string::String {
122+
match self {
123+
#(#message_arms)*
124+
}
125+
}
126+
}
127+
})
128+
.into())
129+
}
130+
Data::Struct(_) => Err(syn::Error::new_spanned(
131+
&input,
132+
"struct auto derive Throwable is not supported",
133+
)),
134+
Data::Union(_) => Err(syn::Error::new_spanned(
135+
&input,
136+
"union auto derive Throwable is not supported",
137+
)),
138+
}
139+
}
140+
141+
fn attributes_find_ident<'a>(attrs: &'a [Attribute], ident: &'a str) -> Option<&'a Attribute> {
142+
attrs.iter().find(|attr| attr.path.is_ident(ident))
143+
}

phper-macros/src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ The proc-macros for [phper](https://crates.io/crates/phper).
1111
// TODO Write a bridge macro for easy usage about register functions and classes, like `cxx`.
1212

1313
mod alloc;
14+
mod derives;
1415
mod inner;
1516
mod log;
1617
mod utils;
1718

1819
use proc_macro::TokenStream;
20+
use syn::{parse_macro_input, DeriveInput};
1921

2022
/// C style string end with '\0'.
2123
///
@@ -70,3 +72,27 @@ pub fn c_str_ptr(input: TokenStream) -> TokenStream {
7072
pub fn php_get_module(attr: TokenStream, input: TokenStream) -> TokenStream {
7173
inner::php_get_module(attr, input)
7274
}
75+
76+
/// Auto derive for [phper::errors::Throwable].
77+
///
78+
/// # Examples
79+
///
80+
/// ```no_test
81+
/// #[derive(thiserror::Error, crate::Throwable, Debug)]
82+
/// #[throwable(class = "Exception")]
83+
/// pub enum Error {
84+
/// #[error(transparent)]
85+
/// Io(#[from] std::io::Error),
86+
///
87+
/// #[error(transparent)]
88+
/// #[throwable(transparent)]
89+
/// My(#[from] MyError),
90+
/// }
91+
/// ```
92+
///
93+
/// TODO Support struct, attbiute `throwable` with `code` and `message`.
94+
#[proc_macro_derive(Throwable, attributes(throwable, throwable_crate))]
95+
pub fn derive_throwable(input: TokenStream) -> TokenStream {
96+
let input = parse_macro_input!(input as DeriveInput);
97+
derives::derive_throwable(input).unwrap_or_else(|e| e.into_compile_error().into())
98+
}

phper/src/errors.rs

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ pub type Result<T> = std::result::Result<T, self::Error>;
3535
/// Crate level Error, which also can become an exception in php.
3636
///
3737
/// As a php exception, will throw `ErrorException` when the item not implement [Throwable].
38-
#[derive(thiserror::Error, Debug)]
38+
#[derive(thiserror::Error, crate::Throwable, Debug)]
39+
#[throwable(class = "ErrorException")]
40+
#[throwable_crate]
3941
pub enum Error {
4042
#[error(transparent)]
4143
Io(#[from] io::Error),
@@ -47,19 +49,23 @@ pub enum Error {
4749
FromBytesWithNul(#[from] FromBytesWithNulError),
4850

4951
#[error(transparent)]
52+
Other(#[from] anyhow::Error),
53+
54+
#[error(transparent)]
55+
#[throwable(transparent)]
5056
Type(#[from] TypeError),
5157

5258
#[error(transparent)]
59+
#[throwable(transparent)]
5360
ClassNotFound(#[from] ClassNotFoundError),
5461

5562
#[error(transparent)]
63+
#[throwable(transparent)]
5664
ArgumentCount(#[from] ArgumentCountError),
5765

5866
#[error(transparent)]
67+
#[throwable(transparent)]
5968
StateType(#[from] StateTypeError),
60-
61-
#[error(transparent)]
62-
Other(#[from] anyhow::Error),
6369
}
6470

6571
impl Error {
@@ -70,27 +76,6 @@ impl Error {
7076
}
7177
}
7278

73-
// TODO Add message() implement.
74-
impl Throwable for Error {
75-
fn class_entry(&self) -> &StatelessClassEntry {
76-
match self {
77-
Self::Type(e) => e.class_entry(),
78-
Self::ClassNotFound(e) => e.class_entry(),
79-
Self::ArgumentCount(e) => e.class_entry(),
80-
_ => ClassEntry::from_globals("ErrorException").unwrap(),
81-
}
82-
}
83-
84-
fn code(&self) -> u64 {
85-
match self {
86-
Self::Type(e) => e.code(),
87-
Self::ClassNotFound(e) => e.code(),
88-
Self::ArgumentCount(e) => e.code(),
89-
_ => 0,
90-
}
91-
}
92-
}
93-
9479
#[derive(thiserror::Error, Debug)]
9580
#[error("type error: {message}")]
9681
pub struct TypeError {

phper/src/functions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ unsafe extern "C" fn invoke(execute_data: *mut zend_execute_data, return_value:
280280
handler.call(execute_data, &mut arguments, return_value);
281281
}
282282

283-
pub const fn create_zend_arg_info(
283+
pub(crate) const fn create_zend_arg_info(
284284
name: *const c_char,
285285
_pass_by_ref: bool,
286286
) -> zend_internal_arg_info {

phper/src/modules.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub(crate) fn write_global_module<R>(f: impl FnOnce(&mut Module) -> R) -> R {
2929
}
3030

3131
unsafe extern "C" fn module_startup(r#type: c_int, module_number: c_int) -> c_int {
32-
let args = ModuleArgs::new(r#type, module_number);
32+
let args = ModuleContext::new(r#type, module_number);
3333
write_global_module(|module| {
3434
args.register_ini_entries(Ini::entries());
3535
for class_entity in &mut module.class_entities {
@@ -44,7 +44,7 @@ unsafe extern "C" fn module_startup(r#type: c_int, module_number: c_int) -> c_in
4444
}
4545

4646
unsafe extern "C" fn module_shutdown(r#type: c_int, module_number: c_int) -> c_int {
47-
let args = ModuleArgs::new(r#type, module_number);
47+
let args = ModuleContext::new(r#type, module_number);
4848
args.unregister_ini_entries();
4949
read_global_module(|module| match &module.module_shutdown {
5050
Some(f) => f(args) as c_int,
@@ -54,14 +54,14 @@ unsafe extern "C" fn module_shutdown(r#type: c_int, module_number: c_int) -> c_i
5454

5555
unsafe extern "C" fn request_startup(r#type: c_int, request_number: c_int) -> c_int {
5656
read_global_module(|module| match &module.request_init {
57-
Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int,
57+
Some(f) => f(ModuleContext::new(r#type, request_number)) as c_int,
5858
None => 1,
5959
})
6060
}
6161

6262
unsafe extern "C" fn request_shutdown(r#type: c_int, request_number: c_int) -> c_int {
6363
read_global_module(|module| match &module.request_shutdown {
64-
Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int,
64+
Some(f) => f(ModuleContext::new(r#type, request_number)) as c_int,
6565
None => 1,
6666
})
6767
}
@@ -84,10 +84,10 @@ pub struct Module {
8484
name: String,
8585
version: String,
8686
author: String,
87-
module_init: Option<Box<dyn Fn(ModuleArgs) -> bool + Send + Sync>>,
88-
module_shutdown: Option<Box<dyn Fn(ModuleArgs) -> bool + Send + Sync>>,
89-
request_init: Option<Box<dyn Fn(ModuleArgs) -> bool + Send + Sync>>,
90-
request_shutdown: Option<Box<dyn Fn(ModuleArgs) -> bool + Send + Sync>>,
87+
module_init: Option<Box<dyn Fn(ModuleContext) -> bool + Send + Sync>>,
88+
module_shutdown: Option<Box<dyn Fn(ModuleContext) -> bool + Send + Sync>>,
89+
request_init: Option<Box<dyn Fn(ModuleContext) -> bool + Send + Sync>>,
90+
request_shutdown: Option<Box<dyn Fn(ModuleContext) -> bool + Send + Sync>>,
9191
function_entities: Vec<FunctionEntity>,
9292
class_entities: Vec<ClassEntity>,
9393
}
@@ -107,24 +107,27 @@ impl Module {
107107
}
108108
}
109109

110-
pub fn on_module_init(&mut self, func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static) {
110+
pub fn on_module_init(&mut self, func: impl Fn(ModuleContext) -> bool + Send + Sync + 'static) {
111111
self.module_init = Some(Box::new(func));
112112
}
113113

114114
pub fn on_module_shutdown(
115115
&mut self,
116-
func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static,
116+
func: impl Fn(ModuleContext) -> bool + Send + Sync + 'static,
117117
) {
118118
self.module_shutdown = Some(Box::new(func));
119119
}
120120

121-
pub fn on_request_init(&mut self, func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static) {
121+
pub fn on_request_init(
122+
&mut self,
123+
func: impl Fn(ModuleContext) -> bool + Send + Sync + 'static,
124+
) {
122125
self.request_init = Some(Box::new(func));
123126
}
124127

125128
pub fn on_request_shutdown(
126129
&mut self,
127-
func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static,
130+
func: impl Fn(ModuleContext) -> bool + Send + Sync + 'static,
128131
) {
129132
self.request_shutdown = Some(Box::new(func));
130133
}
@@ -201,13 +204,13 @@ impl Module {
201204
}
202205
}
203206

204-
pub struct ModuleArgs {
207+
pub struct ModuleContext {
205208
#[allow(dead_code)]
206209
r#type: c_int,
207210
module_number: c_int,
208211
}
209212

210-
impl ModuleArgs {
213+
impl ModuleContext {
211214
pub const fn new(r#type: c_int, module_number: c_int) -> Self {
212215
Self {
213216
r#type,

0 commit comments

Comments
 (0)