Skip to content

Commit

Permalink
feat(python): better struct access
Browse files Browse the repository at this point in the history
Co-authored-by: alemi <[email protected]>
  • Loading branch information
zaaarf and alemidev committed Oct 16, 2024
1 parent e1e09cb commit 3326217
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 29 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ napi = { version = "2.16", features = ["full"], optional = true }
napi-derive = { version="2.16", optional = true}

# glue (python)
pyo3 = { version = "0.22", features = ["extension-module"], optional = true}
pyo3 = { version = "0.22", features = ["extension-module", "multiple-pymethods"], optional = true}

# extra
async-trait = { version = "0.1", optional = true }
Expand Down
1 change: 0 additions & 1 deletion dist/java/src/mp/code/data/TextChange.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class TextChange {
/**
* The starting position of the change.
Expand Down
2 changes: 1 addition & 1 deletion dist/py/src/codemp/codemp.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Config:
port: Optional[int]
tls: Optional[bool]

def __new__(cls, *, username: str, password: str, **kwargs) -> Config: ...
def __new__(cls, username: str, password: str, **kwargs) -> Config: ...

def init() -> Driver: ...
def set_logger(logger_cb: Callable[[str], None], debug: bool) -> bool: ...
Expand Down
2 changes: 2 additions & 0 deletions src/api/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use uuid::Uuid;

/// Represents a service user
#[derive(Debug, Clone)]
#[cfg_attr(any(feature = "py", feature = "py-noabi"), pyo3::pyclass)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct User {
/// User unique identifier, should never change.
pub id: Uuid,
/// User name, can change but should be unique.
#[pyo3(get, set)]
pub name: String,
}

Expand Down
15 changes: 8 additions & 7 deletions src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,9 @@
//! import codemp
//!
//! # connect first, api.code.mp is managed by hexed.technology
//! config = codemp.get_default_config()
//! config.username = "[email protected]"
//! config.password = "dont-use-this-password"
//! client = codemp.connect(config).wait()
//! client = codemp.connect(
//! codemp.Config('[email protected]', 'dont-use-this-password')
//! ).wait()
//!
//! # create and join a workspace
//! client.create_workspace("some-workspace").wait()
Expand All @@ -94,14 +93,16 @@
//! buffer = workspace.attach_buffer("/my/file.txt").wait()
//!
//! # write `hello!` at the beginning of this buffer
//! buffer.send(
//! 0, 0, "hello!"
//! ).wait()
//! buffer.send(codemp.TextChange(
//! start=0, end=0,
//! content="hello!"
//! )).wait()
//!
//! # wait for cursor movements
//! while true:
//! event = workspace.cursor().recv().wait()
//! print(f"user {event.user} moved on buffer {event.buffer}")
//!
//! ```
//!
//! ## Lua
Expand Down
144 changes: 125 additions & 19 deletions src/ffi/python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ pub mod controllers;
pub mod workspace;

use crate::{
api::{Config, Cursor, TextChange},
api::{BufferUpdate, Config, Cursor, Selection, TextChange, User},
buffer::Controller as BufferController,
cursor::Controller as CursorController,
Client, Workspace,
};

use pyo3::prelude::*;
use pyo3::{
prelude::*,
exceptions::{PyConnectionError, PyRuntimeError, PySystemError},
types::PyDict,
};
Expand Down Expand Up @@ -153,19 +153,28 @@ fn init() -> PyResult<Driver> {
Ok(Driver(Some(rt_stop_tx)))
}

#[pyfunction]
fn get_default_config() -> crate::api::Config {
let mut conf = crate::api::Config::new("".to_string(), "".to_string());
conf.host = Some(conf.host().to_string());
conf.port = Some(conf.port());
conf.tls = Some(false);
conf
#[pymethods]
impl User {
#[getter]
fn get_id(&self) -> pyo3::PyResult<String> {
Ok(self.id.to_string())
}

#[setter]
fn set_id(&mut self, value: String) -> pyo3::PyResult<()> {
self.id = value.parse().map_err(|x: <uuid::Uuid as std::str::FromStr>::Err| pyo3::exceptions::PyRuntimeError::new_err(x.to_string()))?;
Ok(())
}

fn __str__(&self) -> String {
format!("{self:?}")
}
}

#[pymethods]
impl Config {
#[new]
#[pyo3(signature = (*, username, password, **kwds))]
#[pyo3(signature = (username, password, **kwds))]
pub fn pynew(
username: String,
password: String,
Expand All @@ -176,17 +185,117 @@ impl Config {
let port = kwgs.get_item("port")?.and_then(|e| e.extract().ok());
let tls = kwgs.get_item("tls")?.and_then(|e| e.extract().ok());

Ok(Config {
Ok(Self {
username,
password,
host,
port,
tls,
})
} else {
Ok(Config::new(username, password))
Ok(Self::new(username, password))
}
}

fn __str__(&self) -> String {
format!("{self:?}")
}
}

#[pymethods]
impl Cursor {
fn __str__(&self) -> String {
format!("{self:?}")
}
}

#[pymethods]
impl Selection {
#[new]
#[pyo3(signature = (**kwds))]
pub fn py_new(kwds: Option<&Bound<'_, PyDict>>) -> PyResult<Self> {
if let Some(kwds) = kwds {
let start_row = if let Some(e) = kwds.get_item("start_row")? {
e.extract()?
} else {
0
};

let start_col = if let Some(e) = kwds.get_item("start_col")? {
e.extract()?
} else {
0
};

let end_row = if let Some(e) = kwds.get_item("end_row")? {
e.extract()?
} else {
0
};

let end_col = if let Some(e) = kwds.get_item("end_col")? {
e.extract()?
} else {
0
};

let buffer = if let Some(e) = kwds.get_item("buffer")? {
e.extract()?
} else {
String::default()
};

Ok(Self { start_row, start_col, end_row, end_col, buffer })
} else {
Ok(Self::default())
}
}

fn __str__(&self) -> String {
format!("{self:?}")
}
}

#[pymethods]
impl BufferUpdate {
fn __str__(&self) -> String {
format!("{self:?}")
}
}

#[pymethods]
impl TextChange {
#[new]
#[pyo3(signature = (**kwds))]
pub fn py_new(kwds: Option<&Bound<'_, PyDict>>) -> PyResult<Self> {
if let Some(kwds) = kwds {
let start = if let Some(e) = kwds.get_item("start")? {
e.extract()?
} else {
0
};

let end = if let Some(e) = kwds.get_item("end")? {
e.extract()?
} else {
0
};

let content = if let Some(e) = kwds.get_item("content")? {
e.extract()?
} else {
String::default()
};

Ok(Self { start, end, content })
} else {
Ok(Self::default())
}
}

fn __str__(&self) -> String {
format!("{self:?}")
}
}

#[pyfunction]
Expand Down Expand Up @@ -254,27 +363,24 @@ impl From<crate::errors::ControllerError> for PyErr {
}
}

impl IntoPy<PyObject> for crate::api::User {
fn into_py(self, py: Python<'_>) -> PyObject {
self.id.to_string().into_py(py)
}
}

#[pymodule]
fn codemp(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(version, m)?)?;
m.add_function(wrap_pyfunction!(init, m)?)?;
m.add_function(wrap_pyfunction!(get_default_config, m)?)?;
m.add_function(wrap_pyfunction!(connect, m)?)?;
m.add_function(wrap_pyfunction!(set_logger, m)?)?;
m.add_class::<Driver>()?;

m.add_class::<BufferUpdate>()?;
m.add_class::<TextChange>()?;
m.add_class::<BufferController>()?;

m.add_class::<Cursor>()?;
m.add_class::<Selection>()?;
m.add_class::<CursorController>()?;

m.add_class::<User>()?;

m.add_class::<Workspace>()?;
m.add_class::<Client>()?;
m.add_class::<Config>()?;
Expand Down

0 comments on commit 3326217

Please sign in to comment.