use justapi_core::rate_limit::RateLimiter; use pyo3::prelude::*; use std::sync::Arc; #[pyclass(name = "RateLimiter")] pub struct PyRateLimiter { inner: Arc, } #[pymethods] impl PyRateLimiter { #[staticmethod] pub fn new_redis<'py>(py: Python<'py>, redis_url: String) -> PyResult> { let fut = async move { let limiter = RateLimiter::new_redis(&redis_url) .await .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?; Ok(PyRateLimiter { inner: Arc::new(limiter) }) }; pyo3_async_runtimes::tokio::future_into_py(py, fut) } pub fn check_limit<'py>( &self, py: Python<'py>, key: String, capacity: u64, replenish_rate: u64, tokens: u64, ) -> PyResult> { let limiter = self.inner.clone(); let fut = async move { let res = limiter .check_limit(&key, capacity, replenish_rate, tokens) .await .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?; Ok(PyRateLimitResult { allowed: res.allowed, retry_after_ms: res.retry_after_ms }) }; pyo3_async_runtimes::tokio::future_into_py(py, fut) } } #[pyclass(name = "RateLimitResult", get_all, from_py_object)] #[derive(Clone)] pub struct PyRateLimitResult { pub allowed: bool, pub retry_after_ms: u64, } pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; Ok(()) }