xdot/
xdot_parse.rs
1use nom::error::Error as NomError;
4
5pub mod draw;
6mod op_parser;
7mod ops;
8pub mod shapes;
9
10pub use self::draw::Pen;
11use self::shapes::Shape;
12
13#[cfg(feature = "pyo3")]
14fn try_into_shape(shape: &pyo3::Bound<'_, pyo3::PyAny>) -> pyo3::PyResult<Shape> {
15 use pyo3::prelude::*;
16
17 if let Ok(ell) = shape.extract::<shapes::Ellipse>() {
18 Ok(ell.into())
19 } else if let Ok(points) = shape.extract::<shapes::Points>() {
20 Ok(points.into())
21 } else if let Ok(text) = shape.extract::<shapes::Text>() {
22 Ok(text.into())
23 } else {
24 Err(pyo3::exceptions::PyTypeError::new_err(format!(
25 "Cannot convert object of type {} to Shape",
26 shape.get_type().name()?
27 )))
28 }
29}
30
31#[derive(Debug, Clone, PartialEq)]
33#[cfg_attr(feature = "pyo3", pyo3::pyclass(eq, module = "xdot_rs"))]
34pub struct ShapeDraw {
35 pub pen: Pen,
37 pub shape: Shape,
38}
39#[cfg(feature = "pyo3")]
40#[pyo3::pymethods]
41impl ShapeDraw {
42 #[new]
43 fn new(shape: &pyo3::Bound<'_, pyo3::PyAny>, pen: Pen) -> pyo3::PyResult<Self> {
44 let shape = try_into_shape(shape)?;
45 Ok(ShapeDraw { shape, pen })
46 }
47 #[getter]
48 fn get_pen(&self) -> Pen {
49 self.pen.clone()
50 }
51 #[setter]
52 fn set_pen(&mut self, pen: Pen) {
53 self.pen = pen;
54 }
55 #[getter]
56 fn get_shape<'py>(
57 &self,
58 py: pyo3::Python<'py>,
59 ) -> pyo3::PyResult<pyo3::Bound<'py, pyo3::PyAny>> {
60 use pyo3::IntoPyObjectExt;
61 match &self.shape {
62 Shape::Ellipse(e) => e.clone().into_bound_py_any(py),
63 Shape::Points(p) => p.clone().into_bound_py_any(py),
64 Shape::Text(t) => t.clone().into_bound_py_any(py),
65 }
66 }
67 #[setter]
68 fn set_shape(&mut self, shape: &pyo3::Bound<'_, pyo3::PyAny>) -> pyo3::PyResult<()> {
69 self.shape = try_into_shape(shape)?;
70 Ok(())
71 }
72}
73
74#[cfg(feature = "pyo3")]
75#[test]
76fn cmp_equal() {
77 use super::*;
78 use pyo3::{IntoPyObjectExt, prelude::*};
79
80 pyo3::prepare_freethreaded_python();
81
82 let ellip = shapes::Ellipse {
83 x: 0.,
84 y: 0.,
85 w: 0.,
86 h: 0.,
87 filled: true,
88 };
89 Python::with_gil(|py| {
90 let a = ShapeDraw::new(&ellip.clone().into_bound_py_any(py)?, Pen::default())?;
91 let b = ShapeDraw::new(&ellip.clone().into_bound_py_any(py)?, Pen::default())?;
92 assert!(
93 a.into_bound_py_any(py)?
94 .getattr("__eq__")?
95 .call1((b,))?
96 .extract::<bool>()?
97 );
98 Ok::<(), PyErr>(())
99 })
100 .unwrap();
101}
102
103pub fn parse(input: &str) -> Result<Vec<ShapeDraw>, NomError<&str>> {
106 use ops::Op::*;
107 let mut pen = Pen::default();
108 let mut shape_draws = vec![];
109 for op in op_parser::parse(input)? {
110 match op {
111 DrawShape(shape) => shape_draws.push(ShapeDraw {
112 pen: pen.clone(),
113 shape,
114 }),
115 SetFontCharacteristics(fc) => pen.font_characteristics = fc,
116 SetFillColor(color) => pen.fill_color = color,
117 SetPenColor(color) => pen.color = color,
118 SetFont { size, name } => {
119 pen.font_size = size;
120 pen.font_name = name;
121 }
122 SetStyle(style) => pen.line_style = style,
123 ExternalImage(_) => todo!("conversion of external image op"),
124 }
125 }
126 Ok(shape_draws)
127}
128
129#[cfg(feature = "pyo3")]
130#[pyo3::pyfunction]
131#[pyo3(name = "parse")]
132pub fn parse_py(input: &str) -> pyo3::PyResult<Vec<ShapeDraw>> {
133 use pyo3::{PyErr, exceptions::PyValueError};
134
135 parse(input).map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))
136}