whynot/
lib.rs

1//! # whyNot
2//!
3//! Rust ↔ PHP bridge: call PHP functions from Rust, capture return values,
4//! printed output, and exceptions. Supports async via threads or Tokio.
5//!
6//! ## Examples
7//!
8//! ```rust
9//! use whynot::{new_runtime, PhpRuntime, RuntimeConfig, RuntimeKind};
10//!
11//! fn main() {
12//!     let cfg = RuntimeConfig::default();
13//!     let mut rt = new_runtime(RuntimeKind::Process(cfg)).unwrap();
14//!     let result = rt.call("add", &[7.into(), 5.into()]).unwrap();
15//!     println!("add.result = {:?}", result.result);
16//! }
17//! ```
18
19use std::collections::HashMap;
20
21pub mod value;
22pub mod process;
23pub mod embedded;
24pub mod macros;
25#[cfg(feature = "async_tokio")]
26pub mod async_tokio;
27
28pub use value::PhpValue;
29
30#[derive(Debug, Clone)]
31pub struct PhpException { pub message: String, pub class: String, pub trace: String, pub code: i64 }
32
33#[derive(Debug)]
34pub struct PhpError { pub message: String }
35
36impl std::fmt::Display for PhpError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.message) } }
37impl std::error::Error for PhpError {}
38
39#[derive(Debug, Clone)]
40pub struct PhpCallResult {
41    pub result: PhpValue,
42    pub output: String,
43    pub info: HashMap<String, PhpValue>,
44    pub exception: Option<PhpException>,
45}
46
47#[derive(Debug, Clone)]
48pub struct RuntimeConfig { pub php_path: String, pub runner_path: String, pub debug: bool }
49impl Default for RuntimeConfig { fn default() -> Self { Self { php_path: "php".to_string(), runner_path: "php/runner.php".to_string(), debug: false } } }
50
51pub trait PhpRuntime {
52    fn call(&mut self, function: &str, args: &[PhpValue]) -> Result<PhpCallResult, PhpError>;
53    fn eval(&mut self, code: &str) -> Result<PhpCallResult, PhpError>;
54    fn include(&mut self, path: &str) -> Result<PhpCallResult, PhpError>;
55}
56
57pub enum RuntimeKind { Process(RuntimeConfig), Embedded(RuntimeConfig) }
58
59pub fn new_runtime(kind: RuntimeKind) -> Result<Box<dyn PhpRuntime + Send>, PhpError> {
60    match kind {
61        RuntimeKind::Process(cfg) => Ok(Box::new(process::ProcRuntime::spawn(cfg)?)),
62        RuntimeKind::Embedded(_cfg) => Ok(Box::new(embedded::EmbeddedRuntime::init()?)),
63    }
64}