use pyo3::exceptions::PyIndexError;
use pyo3::prelude::*;
use pyo3::types::{IntoPyDict, PyDict, PySequence, PyString};
use pyo3::wrap_pyfunction;
use crate::py_cached::PyCached;
#[pyclass(subclass, module = "libcst_native.parser_config")]
#[text_signature = "(*, lines, default_newline)"]
pub struct BaseWhitespaceParserConfig {
pub lines: PyCached<Vec<String>>,
pub default_newline: PyCached<String>,
}
#[pymethods]
impl BaseWhitespaceParserConfig {
#[new]
fn new(lines: &PySequence, default_newline: &PyString) -> PyResult<Self> {
Ok(Self {
lines: lines.extract()?,
default_newline: default_newline.extract()?,
})
}
#[getter]
fn get_lines(&self, py: Python) -> Py<PyAny> {
self.lines.to_object(py)
}
#[getter]
fn get_default_newline(&self, py: Python) -> Py<PyAny> {
self.default_newline.to_object(py)
}
}
impl BaseWhitespaceParserConfig {
pub fn get_line(&self, line_number: usize) -> PyResult<&str> {
let err_fn =
|| PyIndexError::new_err(format!("line number of {} is out of range", line_number));
self.lines
.get(line_number.checked_sub(1).ok_or_else(err_fn)?)
.map(|l| &l[..])
.ok_or_else(err_fn)
}
pub fn get_line_after_column(&self, line_number: usize, column_index: usize) -> PyResult<&str> {
self.get_line(line_number)?
.get(column_index..)
.ok_or_else(|| {
PyIndexError::new_err(format!("column index of {} is out of range", column_index))
})
}
}
#[pyclass(extends=BaseWhitespaceParserConfig, module="libcst_native.parser_config")]
#[text_signature = "(*, lines, encoding, default_indent, default_newline, has_trailing_newline, version, future_imports)"]
pub struct ParserConfig {
#[pyo3(get)]
encoding: Py<PyAny>,
#[pyo3(get)]
default_indent: Py<PyAny>,
#[pyo3(get)]
has_trailing_newline: Py<PyAny>,
#[pyo3(get)]
version: Py<PyAny>,
#[pyo3(get)]
future_imports: Py<PyAny>,
}
#[pymethods]
impl ParserConfig {
#[new]
fn new(
lines: &PySequence,
encoding: Py<PyAny>,
default_indent: Py<PyAny>,
default_newline: &PyString,
has_trailing_newline: Py<PyAny>,
version: Py<PyAny>,
future_imports: Py<PyAny>,
) -> PyResult<(Self, BaseWhitespaceParserConfig)> {
Ok((
Self {
encoding,
default_indent,
has_trailing_newline,
version,
future_imports,
},
BaseWhitespaceParserConfig::new(lines, default_newline)?,
))
}
}
#[pyfunction]
fn parser_config_asdict<'py>(py: Python<'py>, config: PyRef<'py, ParserConfig>) -> &'py PyDict {
let super_config: &BaseWhitespaceParserConfig = config.as_ref();
vec![
("lines", super_config.lines.to_object(py)),
("encoding", config.encoding.clone_ref(py)),
("default_indent", config.default_indent.clone_ref(py)),
(
"default_newline",
super_config.default_newline.to_object(py),
),
(
"has_trailing_newline",
config.has_trailing_newline.clone_ref(py),
),
("version", config.version.clone_ref(py)),
("future_imports", config.future_imports.clone_ref(py)),
]
.into_py_dict(py)
.unwrap()
}
pub fn init_module(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<BaseWhitespaceParserConfig>()?;
m.add_class::<ParserConfig>()?;
m.add_function(wrap_pyfunction!(parser_config_asdict, m)?)
.unwrap();
Ok(self)
}