|
1 | | -use djc_html_transformer::set_html_attributes; |
| 1 | +use djc_html_transformer::{ |
| 2 | + set_html_attributes as set_html_attributes_rust, HtmlTransformerConfig, |
| 3 | +}; |
| 4 | +use pyo3::exceptions::{PyValueError}; |
2 | 5 | use pyo3::prelude::*; |
| 6 | +use pyo3::types::{PyDict, PyTuple}; |
3 | 7 |
|
4 | | -/// A Python module implemented in Rust for high-performance transformations. |
| 8 | +/// Singular Python API that brings togther all the other Rust crates. |
5 | 9 | #[pymodule] |
6 | 10 | fn djc_core(m: &Bound<'_, PyModule>) -> PyResult<()> { |
| 11 | + // HTML transformer |
7 | 12 | m.add_function(wrap_pyfunction!(set_html_attributes, m)?)?; |
8 | 13 | Ok(()) |
9 | 14 | } |
| 15 | + |
| 16 | +/// Transform HTML by adding attributes to the elements. |
| 17 | +/// |
| 18 | +/// Args: |
| 19 | +/// html (str): The HTML string to transform. Can be a fragment or full document. |
| 20 | +/// root_attributes (List[str]): List of attribute names to add to root elements only. |
| 21 | +/// all_attributes (List[str]): List of attribute names to add to all elements. |
| 22 | +/// check_end_names (bool, optional): Whether to validate matching of end tags. Defaults to false. |
| 23 | +/// watch_on_attribute (str, optional): If set, captures which attributes were added to elements with this attribute. |
| 24 | +/// |
| 25 | +/// Returns: |
| 26 | +/// Tuple[str, Dict[str, List[str]]]: A tuple containing: |
| 27 | +/// - The transformed HTML string |
| 28 | +/// - A dictionary mapping captured attribute values to lists of attributes that were added |
| 29 | +/// to those elements. Only returned if watch_on_attribute is set, otherwise empty dict. |
| 30 | +/// |
| 31 | +/// Example: |
| 32 | +/// >>> html = '<div data-id="123"><p>Hello</p></div>' |
| 33 | +/// >>> html, captured = set_html_attributes(html, ['data-root-id'], ['data-v-123'], watch_on_attribute='data-id') |
| 34 | +/// >>> print(captured) |
| 35 | +/// {'123': ['data-root-id', 'data-v-123']} |
| 36 | +/// |
| 37 | +/// Raises: |
| 38 | +/// ValueError: If the HTML is malformed or cannot be parsed. |
| 39 | +#[pyfunction] |
| 40 | +#[pyo3(signature = (html, root_attributes, all_attributes, check_end_names=None, watch_on_attribute=None))] |
| 41 | +#[pyo3( |
| 42 | + text_signature = "(html, root_attributes, all_attributes, *, check_end_names=False, watch_on_attribute=None)" |
| 43 | +)] |
| 44 | +pub fn set_html_attributes( |
| 45 | + py: Python, |
| 46 | + html: &str, |
| 47 | + root_attributes: Vec<String>, |
| 48 | + all_attributes: Vec<String>, |
| 49 | + check_end_names: Option<bool>, |
| 50 | + watch_on_attribute: Option<String>, |
| 51 | +) -> PyResult<Py<PyAny>> { |
| 52 | + let config = HtmlTransformerConfig::new( |
| 53 | + root_attributes, |
| 54 | + all_attributes, |
| 55 | + check_end_names.unwrap_or(false), |
| 56 | + watch_on_attribute, |
| 57 | + ); |
| 58 | + |
| 59 | + match set_html_attributes_rust(html, &config) { |
| 60 | + Ok((html, captured)) => { |
| 61 | + // Convert captured attributes to a Python dictionary |
| 62 | + let captured_dict = PyDict::new(py); |
| 63 | + for (id, attrs) in captured { |
| 64 | + captured_dict.set_item(id, attrs)?; |
| 65 | + } |
| 66 | + |
| 67 | + // Convert items to Bound<PyAny> for the tuple |
| 68 | + use pyo3::types::PyString; |
| 69 | + let html_obj = PyString::new(py, &html).as_any().clone(); |
| 70 | + let dict_obj = captured_dict.as_any().clone(); |
| 71 | + let result = PyTuple::new(py, vec![html_obj, dict_obj])?; |
| 72 | + Ok(result.into_any().unbind()) |
| 73 | + } |
| 74 | + Err(e) => Err(PyValueError::new_err(e.to_string())), |
| 75 | + } |
| 76 | +} |
0 commit comments