Skip to content

Refactor errors and exceptions. #84

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/complex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn say_hello(arguments: &mut [ZVal]) -> phper::Result<String> {
}

fn throw_exception(_: &mut [ZVal]) -> phper::Result<()> {
Err(phper::Error::other("I am sorry"))
Err(phper::Error::Boxed("I am sorry".into()))
}

#[php_get_module]
Expand Down
12 changes: 6 additions & 6 deletions examples/http-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub fn make_client_builder_class() -> StatefulClass<ClientBuilder> {
let state = this.as_mut_state();
let builder: ClientBuilder = take(state);
*state = builder.timeout(Duration::from_millis(ms as u64));
Ok::<_, HttpClientError>(this.to_ref_owned())
Ok::<_, phper::Error>(this.to_ref_owned())
})
.argument(Argument::by_val("ms"));

Expand All @@ -43,20 +43,20 @@ pub fn make_client_builder_class() -> StatefulClass<ClientBuilder> {
let state = this.as_mut_state();
let builder: ClientBuilder = take(state);
*state = builder.cookie_store(enable);
Ok::<_, HttpClientError>(this.to_ref_owned())
Ok::<_, phper::Error>(this.to_ref_owned())
})
.argument(Argument::by_val("enable"));

// Inner call the `ClientBuilder::build`, and wrap the result `Client` in
// Object.
class.add_method("build", Visibility::Public, |this, _arguments| {
let state = take(this.as_mut_state());
let client = ClientBuilder::build(state)?;
let client = ClientBuilder::build(state).map_err(HttpClientError::Reqwest)?;
let mut object = ClassEntry::from_globals(HTTP_CLIENT_CLASS_NAME)?.init_object()?;
unsafe {
*object.as_mut_state() = Some(client);
}
Ok::<_, HttpClientError>(object)
Ok::<_, phper::Error>(object)
});

class
Expand All @@ -76,7 +76,7 @@ pub fn make_client_class() -> StatefulClass<Option<Client>> {
unsafe {
*object.as_mut_state() = Some(request_builder);
}
Ok::<_, HttpClientError>(object)
Ok::<_, phper::Error>(object)
})
.argument(Argument::by_val("url"));

Expand All @@ -89,7 +89,7 @@ pub fn make_client_class() -> StatefulClass<Option<Client>> {
unsafe {
*object.as_mut_state() = Some(request_builder);
}
Ok::<_, HttpClientError>(object)
Ok::<_, phper::Error>(object)
})
.argument(Argument::by_val("url"));

Expand Down
39 changes: 23 additions & 16 deletions examples/http-client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,25 @@
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

use phper::classes::{ClassEntry, StatefulClass};
use phper::{
classes::{ClassEntry, StatefulClass},
errors::{exception_class, Throwable},
};

/// The exception class name of extension.
const EXCEPTION_CLASS_NAME: &str = "HttpClient\\HttpClientException";

/// The struct implemented `phper::Throwable` will throw php Exception
/// when return as `Err(e)` in extension functions.
#[derive(Debug, thiserror::Error, phper::Throwable)]
#[throwable_class(EXCEPTION_CLASS_NAME)]
pub enum HttpClientError {
/// Generally, implement `From` for `phper::Error`.
#[error(transparent)]
#[throwable(transparent)]
Phper(#[from] phper::Error),
pub fn make_exception_class() -> StatefulClass<()> {
let mut exception_class = StatefulClass::new(EXCEPTION_CLASS_NAME);
// The `extends` is same as the PHP class `extends`.
exception_class.extends("Exception");
exception_class
}

#[derive(Debug, thiserror::Error)]
pub enum HttpClientError {
#[error(transparent)]
Reqwest(#[from] reqwest::Error),
Reqwest(reqwest::Error),

#[error("should call '{method_name}()' before call 'body()'")]
ResponseAfterRead { method_name: String },
Expand All @@ -33,9 +35,14 @@ pub enum HttpClientError {
ResponseHadRead,
}

pub fn make_exception_class() -> StatefulClass<()> {
let mut exception_class = StatefulClass::new(EXCEPTION_CLASS_NAME);
// The `extends` is same as the PHP class `extends`.
exception_class.extends("Exception");
exception_class
impl Throwable for HttpClientError {
fn get_class(&self) -> &ClassEntry {
ClassEntry::from_globals(EXCEPTION_CLASS_NAME).unwrap_or_else(|_| exception_class())
}
}

impl From<HttpClientError> for phper::Error {
fn from(e: HttpClientError) -> Self {
phper::Error::throw(e)
}
}
13 changes: 9 additions & 4 deletions examples/http-client/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
// See the Mulan PSL v2 for more details.

use crate::{errors::HttpClientError, response::RESPONSE_CLASS_NAME};
use phper::classes::{ClassEntry, StatefulClass, Visibility};
use phper::{
classes::{ClassEntry, StatefulClass, Visibility},
errors::ThrowObject,
};
use reqwest::blocking::RequestBuilder;
use std::mem::take;

Expand All @@ -23,12 +26,14 @@ pub fn make_request_builder_class() -> StatefulClass<Option<RequestBuilder>> {

class.add_method("send", Visibility::Public, |this, _arguments| {
let state = take(this.as_mut_state());
let response = state.unwrap().send()?;
let mut object = ClassEntry::from_globals(RESPONSE_CLASS_NAME)?.new_object([])?;
let response = state.unwrap().send().map_err(HttpClientError::Reqwest)?;
let mut object = ClassEntry::from_globals(RESPONSE_CLASS_NAME)
.map_err(ThrowObject::from_throwable)?
.new_object([])?;
unsafe {
*object.as_mut_state() = Some(response);
}
Ok::<_, HttpClientError>(object)
Ok::<_, phper::Error>(object)
});

class
Expand Down
7 changes: 3 additions & 4 deletions examples/http-client/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ pub fn make_response_class() -> StatefulClass<Option<Response>> {

class.add_method("body", Visibility::Public, |this, _arguments| {
let response = take(this.as_mut_state());
let body = response
.ok_or(HttpClientError::ResponseHadRead)
.and_then(|response| response.bytes().map_err(Into::into))?;
Ok::<_, HttpClientError>(body.to_vec())
let response = response.ok_or(HttpClientError::ResponseHadRead)?;
let body = response.bytes().map_err(HttpClientError::Reqwest)?;
Ok::<_, phper::Error>(body.to_vec())
});

class.add_method("status", Visibility::Public, |this, _arguments| {
Expand Down
42 changes: 17 additions & 25 deletions examples/http-server/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,28 @@
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

use hyper::header::{InvalidHeaderName, InvalidHeaderValue};
use phper::classes::{ClassEntry, StatefulClass};
use std::{net::AddrParseError, str::Utf8Error};
use phper::{
classes::{ClassEntry, StatefulClass},
errors::{exception_class, Throwable},
};
use std::error::Error;

const EXCEPTION_CLASS_NAME: &str = "HttpServer\\HttpServerException";

#[derive(Debug, thiserror::Error, phper::Throwable)]
#[throwable_class(EXCEPTION_CLASS_NAME)]
pub enum HttpServerError {
#[error(transparent)]
#[throwable(transparent)]
Phper(#[from] phper::Error),
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct HttpServerError(pub Box<dyn Error>);

#[error(transparent)]
Utf8Error(#[from] Utf8Error),

#[error(transparent)]
AddrParse(#[from] AddrParseError),

#[error(transparent)]
Io(#[from] std::io::Error),

#[error(transparent)]
Hyper(#[from] hyper::Error),

#[error(transparent)]
InvalidHeaderName(#[from] InvalidHeaderName),
impl Throwable for HttpServerError {
fn get_class(&self) -> &ClassEntry {
ClassEntry::from_globals(EXCEPTION_CLASS_NAME).unwrap_or_else(|_| exception_class())
}
}

#[error(transparent)]
InvalidHeaderValue(#[from] InvalidHeaderValue),
impl From<HttpServerError> for phper::Error {
fn from(e: HttpServerError) -> Self {
phper::Error::throw(e)
}
}

pub fn make_exception_class() -> StatefulClass<()> {
Expand Down
9 changes: 6 additions & 3 deletions examples/http-server/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ pub fn make_response_class() -> StatefulClass<Response<Body>> {

class
.add_method("header", Visibility::Public, |this, arguments| {
let name = arguments[0].expect_z_str()?.to_bytes();
let value = arguments[1].expect_z_str()?.to_bytes();

let response: &mut Response<Body> = this.as_mut_state();
response.headers_mut().insert(
HeaderName::from_bytes(arguments[0].as_z_str().unwrap().to_bytes())?,
HeaderValue::from_bytes(arguments[1].as_z_str().unwrap().to_bytes())?,
HeaderName::from_bytes(name).map_err(|e| HttpServerError(Box::new(e)))?,
HeaderValue::from_bytes(value).map_err(|e| HttpServerError(Box::new(e)))?,
);
Ok::<_, HttpServerError>(())
Ok::<_, phper::Error>(())
})
.argument(Argument::by_val("data"));

Expand Down
39 changes: 20 additions & 19 deletions examples/http-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use hyper::{
use phper::{
alloc::{EBox, RefClone, ToRefOwned},
classes::{ClassEntry, StatefulClass, Visibility},
errors::Error::Throw,
functions::Argument,
values::ZVal,
};
Expand Down Expand Up @@ -48,12 +47,17 @@ pub fn make_server_class() -> StatefulClass<Option<Builder<AddrIncoming>>> {
.add_method("__construct", Visibility::Public, |this, arguments| {
let host = arguments[0].expect_z_str()?;
let port = arguments[1].expect_long()?;

this.set_property("host", host.to_owned());
this.set_property("port", port);
let addr = format!("{}:{}", host.to_str()?, port).parse::<SocketAddr>()?;

let addr = format!("{}:{}", host.to_str()?, port)
.parse::<SocketAddr>()
.map_err(|e| HttpServerError(Box::new(e)))?;
let builder = Server::bind(&addr);
*this.as_mut_state() = Some(builder);
Ok::<_, HttpServerError>(())

Ok::<_, phper::Error>(())
})
.arguments([Argument::by_val("host"), Argument::by_val("port")]);

Expand All @@ -73,7 +77,7 @@ pub fn make_server_class() -> StatefulClass<Option<Builder<AddrIncoming>>> {

let make_svc = make_service_fn(move |_conn| async move {
Ok::<_, Infallible>(service_fn(move |_: Request<Body>| async move {
match async move {
let fut = async move {
let handle = unsafe { HANDLE.load(Ordering::SeqCst).as_mut().unwrap() };

let request =
Expand All @@ -85,21 +89,16 @@ pub fn make_server_class() -> StatefulClass<Option<Builder<AddrIncoming>>> {
let response_val = response.to_ref_owned();
let response_val = ZVal::from(response_val);

match handle.call([request, response_val]) {
Err(Throw(ex)) => {
let state = unsafe { response.as_mut_state::<Response<Body>>() };
*state.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
*state.body_mut() = ex.to_string().into();
}
Err(e) => return Err(e.into()),
_ => {}
}
if let Err(err) = handle.call([request, response_val]) {
let state = unsafe { response.as_mut_state::<Response<Body>>() };
*state.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
*state.body_mut() = err.to_string().into();
};

let response = replace_and_get(unsafe { response.as_mut_state() });
Ok::<Response<Body>, HttpServerError>(response)
}
.await
{
Ok::<Response<Body>, phper::Error>(response)
};
match fut.await {
Ok(response) => Ok::<Response<Body>, Infallible>(response),
Err(e) => {
let mut response = Response::new("".into());
Expand All @@ -112,9 +111,11 @@ pub fn make_server_class() -> StatefulClass<Option<Builder<AddrIncoming>>> {
});

let server = builder.serve(make_svc);
Handle::current().block_on(server)?;
Handle::current()
.block_on(server)
.map_err(|e| HttpServerError(Box::new(e)))?;

Ok::<_, HttpServerError>(())
Ok::<_, phper::Error>(())
});

class
Expand Down
1 change: 0 additions & 1 deletion examples/logging/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ license = { workspace = true }
crate-type = ["lib", "cdylib"]

[dependencies]
anyhow = "1.0.66"
phper = { version = "0.7.0", path = "../../phper" }

[dev-dependencies]
Expand Down
29 changes: 23 additions & 6 deletions examples/logging/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

use anyhow::Context;
use phper::{
deprecated, echo, error, functions::Argument, modules::Module, notice, php_get_module,
values::ZVal, warning,
Expand All @@ -25,7 +24,11 @@ pub fn get_module() -> Module {
module
.add_function("log_say", |params: &mut [ZVal]| -> phper::Result<()> {
params[0].convert_to_string();
let message = params[0].as_z_str().unwrap().to_str().context("to str")?;
let message = params[0]
.as_z_str()
.unwrap()
.to_str()
.map_err(phper::Error::boxed)?;
echo!("Hello, {}!", message);
Ok(())
})
Expand All @@ -34,7 +37,11 @@ pub fn get_module() -> Module {
module
.add_function("log_notice", |params: &mut [ZVal]| -> phper::Result<()> {
params[0].convert_to_string();
let message = params[0].as_z_str().unwrap().to_str().context("to str")?;
let message = params[0]
.as_z_str()
.unwrap()
.to_str()
.map_err(phper::Error::boxed)?;
notice!("Something happened: {}", message);
Ok(())
})
Expand All @@ -43,7 +50,11 @@ pub fn get_module() -> Module {
module
.add_function("log_warning", |params: &mut [ZVal]| -> phper::Result<()> {
params[0].convert_to_string();
let message = params[0].as_z_str().unwrap().to_str().context("to str")?;
let message = params[0]
.as_z_str()
.unwrap()
.to_str()
.map_err(phper::Error::boxed)?;
warning!("Something warning: {}", message);
Ok(())
})
Expand All @@ -52,7 +63,10 @@ pub fn get_module() -> Module {
module
.add_function("log_error", |params: &mut [ZVal]| -> phper::Result<()> {
params[0].convert_to_string();
let message = params[0].as_z_str().unwrap().to_str().context("to str")?;
let message = params[0]
.expect_z_str()?
.to_str()
.map_err(phper::Error::boxed)?;
error!("Something gone failed: {}", message);
Ok(())
})
Expand All @@ -63,7 +77,10 @@ pub fn get_module() -> Module {
"log_deprecated",
|params: &mut [ZVal]| -> phper::Result<()> {
params[0].convert_to_string();
let message = params[0].as_z_str().unwrap().to_str().context("to str")?;
let message = params[0]
.expect_z_str()?
.to_str()
.map_err(phper::Error::boxed)?;
deprecated!("Something deprecated: {}", message);
Ok(())
},
Expand Down
Loading