Implemented the client handle interface. TODO: controllers

Former-commit-id: 6b273dffce4e28a1e41f31a71f5a4ce78ecfe264
This commit is contained in:
Camillo Schenone 2023-08-23 18:22:11 +02:00
parent 31f296a55c
commit bfad81f958
2 changed files with 190 additions and 150 deletions

View file

@ -1,14 +1,12 @@
use std::{sync::Arc, error::Error, format};
use std::{sync::Arc, format};
use codemp::prelude::*;
use codemp::{prelude::*};
use codemp::errors::Error as CodempError;
use tokio::sync::Mutex;
use pyo3::{
prelude::*,
exceptions::{PyConnectionError, PyRuntimeError},
types::{PyBool, PyString}
exceptions::{PyConnectionError, PyRuntimeError, PyBaseException},
types::PyString
};
struct PyCodempError(CodempError);
@ -18,7 +16,7 @@ impl From::<CodempError> for PyCodempError {
}
}
impl std::convert::From<PyCodempError> for PyErr {
impl From<PyCodempError> for PyErr {
fn from(err: PyCodempError) -> PyErr {
match err.0 {
CodempError::Transport { status, message } => {
@ -29,227 +27,269 @@ impl std::convert::From<PyCodempError> for PyErr {
},
CodempError::InvalidState { msg } => {
PyRuntimeError::new_err(format!("Invalid state: {}", msg))
},
CodempError::Filler { message } => {
PyBaseException::new_err(format!("Generic error: {}", message))
}
}
}
}
#[pyfunction]
fn connect<'a>(py: Python<'a>, dest: String) -> PyResult<&'a PyAny> {
// construct a python coroutine
pyo3_asyncio::tokio::future_into_py(py, async move {
match CodempClient::new(dest.as_str()).await {
Ok(c) => {
fn codemp_init<'a>(py: Python<'a>) -> PyResult<Py<PyClientHandle>> {
let py_instance: PyClientHandle = CodempInstance::default().into();
Python::with_gil(|py| {
let cc: PyClientHandle = c.into();
let handle = Py::new(py, cc)?;
Ok(handle)
})
},
Err(e) => { Err(PyConnectionError::new_err(e.source().unwrap().to_string())) }
}
Ok(Py::new(py, py_instance)?)
})
}
#[pyclass]
#[derive(Clone)]
struct PyClientHandle(Arc<Mutex<CodempClient>>);
struct PyClientHandle(Arc<CodempInstance>);
impl From::<CodempClient> for PyClientHandle {
fn from(value: CodempClient) -> Self {
PyClientHandle(Arc::new(Mutex::new(value)))
impl From::<CodempInstance> for PyClientHandle {
fn from(value: CodempInstance) -> Self {
PyClientHandle(Arc::new(value))
}
}
#[pymethods]
impl PyClientHandle {
fn get_id<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
let binding = rc.lock().await;
Python::with_gil(|py| {
let id: Py<PyString> = PyString::new(py, binding.id()).into();
Ok(id)
})
})
}
fn create<'a>(&self, py: Python<'a>, path: String, content: Option<String>) -> PyResult<&'a PyAny> {
fn connect<'a>(&'a self, py: Python<'a>, addr: String) ->PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
match rc.lock().await.create(path, content).await {
Ok(accepted) => {
Python::with_gil(|py| {
let accepted: Py<PyBool> = PyBool::new(py, accepted).into();
Ok(accepted)
})
},
Err(e) => { Err(PyConnectionError::new_err(e.source().unwrap().to_string())) }
}
rc.connect(addr.as_str()).await.map_err(PyCodempError::from)?;
Ok(())
})
}
fn listen<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> {
fn create<'a>(&'a self, py: Python<'a>, path: String, content: Option<String>) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
match rc.lock().await.listen().await {
Ok(controller) => {
Python::with_gil(|py| {
let cc: PyControllerHandle = controller.into();
let contr = Py::new(py, cc)?;
Ok(contr)
})
},
Err(e) => {Err(PyConnectionError::new_err(e.source().unwrap().to_string()))}
}
rc.create(path.as_str(), content.as_deref())
.await
.map_err(PyCodempError::from)?;
Ok(())
})
}
fn attach<'a>(&self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
fn attach<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
let uri = path.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
match rc.lock().await.attach(uri).await {
Ok(factory) => {
let buffctrl = rc.attach(path.as_str())
.await
.map_err(PyCodempError::from)?;
Python::with_gil(|py| {
let ff: PyOperationsHandle = factory.into();
let fact = Py::new(py, ff)?;
Ok(fact)
Ok(Py::new(py, PyBufferController(buffctrl))?)
})
})
},
Err(e) => {Err(PyConnectionError::new_err(e.source().unwrap().to_string()))}
}
fn join<'a>(&'a self, py: Python<'a>, session: String) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
let curctrl = rc.join(session.as_str())
.await
.map_err(PyCodempError::from)?;
Python::with_gil(|py| {
Ok(Py::new(py, PyCursorController(curctrl))?)
})
})
}
fn get_cursor<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
let curctrl = rc.get_cursor()
.await
.map_err(PyCodempError::from)?;
Python::with_gil(|py| {
Ok(Py::new(py, PyCursorController(curctrl))?)
})
})
}
fn get_buffer<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
let buffctrl = rc.get_buffer(path.as_str())
.await
.map_err(PyCodempError::from)?;
Python::with_gil(|py| {
Ok(Py::new(py, PyBufferController(buffctrl))?)
})
})
}
fn leave_workspace<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
rc.leave_workspace()
.await
.map_err(PyCodempError::from)?;
Ok(())
})
}
fn disconnect_buffer<'a>(&'a self, py: Python<'a>, path: String) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
rc.disconnect_buffer(path.as_str())
.await
.map_err(PyCodempError::from)?;
Ok(())
})
}
}
impl From::<CursorControllerHandle> for PyControllerHandle {
fn from(value: CursorControllerHandle) -> Self {
PyControllerHandle(value)
impl From::<CodempCursorController> for PyCursorController {
fn from(value: CodempCursorController) -> Self {
PyCursorController(Arc::new(value))
}
}
impl From::<OperationControllerHandle> for PyOperationsHandle {
fn from(value: OperationControllerHandle) -> Self {
PyOperationsHandle(value)
impl From::<CodempBufferController> for PyBufferController {
fn from(value: CodempBufferController) -> Self {
PyBufferController(Arc::new(value))
}
}
#[pyclass]
struct PyControllerHandle(CursorControllerHandle);
struct PyCursorController(Arc<CodempCursorController>);
#[pymethods]
impl PyControllerHandle {
fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
let mut rc = self.0.clone();
let cb = coro_py.clone();
// We want to start polling the ControlHandle and call the callback every time
// we have something.
impl PyCursorController {
// fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
// let mut rc = self.0.clone();
// let cb = coro_py.clone();
// // We want to start polling the ControlHandle and call the callback every time
// // we have something.
// pyo3_asyncio::tokio::future_into_py(py, async move {
// while let Some(op) = rc.poll().await {
// let start = op.start.unwrap_or(Position { row: 0, col: 0});
// let end = op.end.unwrap_or(Position { row: 0, col: 0});
// let cb_fut = Python::with_gil(|py| -> PyResult<_> {
// let args = (op.user, caller_id.clone(), op.buffer, (start.row, start.col), (end.row, end.col));
// let coro = cb.call1(py, args)?;
// pyo3_asyncio::tokio::into_future(coro.into_ref(py))
// })?;
// cb_fut.await?;
// }
// Ok(())
// })
// }
fn send<'a>(&'a self, py: Python<'a>, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
let pos = CodempCursorPosition {
buffer: path,
start: Some(CodempRowCol { row: start.0, col: start.1 }),
end: Some(CodempRowCol { row: end.0, col: end.1 })
};
pyo3_asyncio::tokio::future_into_py(py, async move {
while let Some(op) = rc.poll().await {
let start = op.start.unwrap_or(Position { row: 0, col: 0});
let end = op.end.unwrap_or(Position { row: 0, col: 0});
let cb_fut = Python::with_gil(|py| -> PyResult<_> {
let args = (op.user, caller_id.clone(), op.buffer, (start.row, start.col), (end.row, end.col));
let coro = cb.call1(py, args)?;
pyo3_asyncio::tokio::into_future(coro.into_ref(py))
})?;
cb_fut.await?;
}
rc.send(pos)
.map_err(PyCodempError::from)?;
Ok(())
})
} // to call after polling cursor movements.
fn send<'a>(&self, py: Python<'a>, path: String, start: (i32, i32), end: (i32, i32)) -> PyResult<&'a PyAny> {
}
fn recv<'a>(&'a self, py: Python<'a>) -> PyResult<&'a PyAny> {
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
let startpos = Position { row: start.0, col: start.1 };
let endpos = Position { row: end.0, col: end.1 };
rc.send(path.as_str(), startpos, endpos).await;
Ok(Python::with_gil(|py| py.None()))
})
} // when we change the cursor ourselves.
}
#[pyclass]
struct PyOperationsHandle(OperationControllerHandle);
#[pymethods]
impl PyOperationsHandle {
fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
let mut rc = self.0.clone();
let cb = coro_py.clone();
// We want to start polling the ControlHandle and call the callback every time
// we have something.
pyo3_asyncio::tokio::future_into_py(py, async move {
while let Some(edit) = rc.poll().await {
let start = edit.span.start;
let end = edit.span.end;
let text = edit.content;
let cb_fut = Python::with_gil(|py| -> PyResult<_> {
let args = (caller_id.clone(), start, end, text);
let coro = cb.call1(py, args)?;
pyo3_asyncio::tokio::into_future(coro.into_ref(py))
})?;
cb_fut.await?;
}
let cur_event = rc.recv()
.await
.map_err(PyCodempError::from)?;
Ok(())
})
} //to call after polling text changes
}
}
#[pyclass]
struct PyBufferController(Arc<CodempBufferController>);
#[pymethods]
impl PyBufferController {
// fn callback<'a>(&'a self, py: Python<'a>, coro_py: Py<PyAny>, caller_id: Py<PyString>) -> PyResult<&'a PyAny> {
// let mut rc = self.0.clone();
// let cb = coro_py.clone();
// // We want to start polling the ControlHandle and call the callback every time
// // we have something.
// pyo3_asyncio::tokio::future_into_py(py, async move {
// while let Some(edit) = rc.poll().await {
// let start = edit.span.start;
// let end = edit.span.end;
// let text = edit.content;
// let cb_fut = Python::with_gil(|py| -> PyResult<_> {
// let args = (caller_id.clone(), start, end, text);
// let coro = cb.call1(py, args)?;
// pyo3_asyncio::tokio::into_future(coro.into_ref(py))
// })?;
// cb_fut.await?;
// }
// Ok(())
// })
// }
fn content(&self, py: Python<'_>) -> PyResult<Py<PyString>> {
let cont: Py<PyString> = PyString::new(py, self.0.content().as_str()).into();
Ok(cont)
}
fn apply<'a>(&self, py: Python<'a>, skip: usize, text: String, tail: usize) -> PyResult<&'a PyAny>{
fn send<'a>(&self, py: Python<'a>, skip: usize, text: String, tail: usize) -> PyResult<&'a PyAny>{
let rc = self.0.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
match rc.delta(skip, text.as_str(), tail) {
Some(op) => { rc.apply(op).await; Ok(()) },
None => Err(PyConnectionError::new_err("delta failed"))
}
// if let Some(op) = rc.delta(skip, text.as_str(), tail) {
// rc.apply(op).await;
// Python::with_gil(|py| {
// let accepted: Py<PyBool> = PyBool::new(py, true).into();
// Ok(accepted)
// })
// } else {
// Python::with_gil(|py| {
// let accepted: Py<PyBool> = PyBool::new(py, false).into();
// Ok(accepted)
// })
// }
Ok(())
})
} //after making text mofications.
}
}
// #[pyclass]
// struct PyCursorEvent {
// user: String,
// buffer: Some(String),
// start: (i32, i32),
// end: (i32, i32)
// }
// impl From<CodempCursorEvent> for PyCursorEvent {
// fn from(value: CodempCursorEvent) -> Self {
// PyCursorEvent {
// user: value.user,
// buffer: value.position.buffer,
// start: (value.position.start.row, value.position.start.col),
// end: (value.position.end.row, value.position.end.col)
// }
// }
// }
// Python module
#[pymodule]
fn codemp_client(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(connect, m)?)?;
m.add_function(wrap_pyfunction!(codemp_init, m)?)?;
m.add_class::<PyClientHandle>()?;
m.add_class::<PyControllerHandle>()?;
m.add_class::<PyOperationsHandle>()?;
m.add_class::<PyCursorController>()?;
m.add_class::<PyBufferController>()?;
Ok(())
}