use std::cmp::Ordering;
use pyo3::prelude::*;
use pyo3::types::PyType;
#[pyclass(name = "Evr", frozen, eq, ord, hash, from_py_object)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PyEvr(crate::Evr<'static>);
impl<'a> From<crate::Evr<'a>> for PyEvr {
fn from(e: crate::Evr<'a>) -> Self {
let (epoch, version, release) = e.values();
PyEvr(crate::Evr::new(
epoch.to_owned(),
version.to_owned(),
release.to_owned(),
))
}
}
#[pymethods]
impl PyEvr {
#[new]
#[pyo3(signature = (epoch, version, release))]
fn new(epoch: Option<&str>, version: &str, release: &str) -> Self {
PyEvr(crate::Evr::new(
epoch.unwrap_or("").to_owned(),
version.to_owned(),
release.to_owned(),
))
}
#[classmethod]
fn parse(_cls: &Bound<'_, PyType>, evr: &str) -> Self {
let (epoch, version, release) = crate::Evr::parse_values(evr);
PyEvr(crate::Evr::new(
epoch.to_owned(),
version.to_owned(),
release.to_owned(),
))
}
fn __repr__(&self) -> String {
format!(
"Evr(epoch={:?}, version={:?}, release={:?})",
self.0.epoch(),
self.0.version(),
self.0.release(),
)
}
fn __str__(&self) -> String {
self.0.to_string()
}
#[getter]
fn epoch(&self) -> &str {
self.0.epoch()
}
#[getter]
fn version(&self) -> &str {
self.0.version()
}
#[getter]
fn release(&self) -> &str {
self.0.release()
}
fn evr(&self) -> String {
self.0.evr()
}
fn evr_short(&self) -> String {
self.0.to_string()
}
fn sortkey<'py>(&self, py: Python<'py>) -> Bound<'py, pyo3::types::PyBytes> {
pyo3::types::PyBytes::new(py, self.0.sortkey().as_bytes())
}
}
#[pyclass(name = "Nevra", frozen, eq, ord, hash, from_py_object)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PyNevra(crate::Nevra<'static>);
impl<'a> From<crate::Nevra<'a>> for PyNevra {
fn from(n: crate::Nevra<'a>) -> Self {
let (name, epoch, version, release, arch) = n.values();
PyNevra(crate::Nevra::new(
name.to_owned(),
epoch.to_owned(),
version.to_owned(),
release.to_owned(),
arch.to_owned(),
))
}
}
#[pymethods]
impl PyNevra {
#[new]
#[pyo3(signature = (name, epoch, version, release, arch))]
fn new(name: &str, epoch: Option<&str>, version: &str, release: &str, arch: &str) -> Self {
PyNevra(crate::Nevra::new(
name.to_owned(),
epoch.unwrap_or("").to_owned(),
version.to_owned(),
release.to_owned(),
arch.to_owned(),
))
}
#[classmethod]
fn parse(_cls: &Bound<'_, PyType>, nevra: &str) -> Self {
let (name, epoch, version, release, arch) = crate::Nevra::parse_values(nevra);
PyNevra(crate::Nevra::new(
name.to_owned(),
epoch.to_owned(),
version.to_owned(),
release.to_owned(),
arch.to_owned(),
))
}
fn __repr__(&self) -> String {
format!(
"Nevra(name={:?}, epoch={:?}, version={:?}, release={:?}, arch={:?})",
self.0.name(),
self.0.epoch(),
self.0.version(),
self.0.release(),
self.0.arch(),
)
}
fn __str__(&self) -> String {
self.0.to_string()
}
#[getter]
fn name(&self) -> &str {
self.0.name()
}
#[getter]
fn epoch(&self) -> &str {
self.0.epoch()
}
#[getter]
fn version(&self) -> &str {
self.0.version()
}
#[getter]
fn release(&self) -> &str {
self.0.release()
}
#[getter]
fn arch(&self) -> &str {
self.0.arch()
}
fn evr(&self) -> PyEvr {
PyEvr(crate::Evr::new(
self.0.epoch().to_owned(),
self.0.version().to_owned(),
self.0.release().to_owned(),
))
}
fn nevra(&self) -> String {
self.0.nevra()
}
fn nevra_short(&self) -> String {
self.0.nevra_short()
}
fn nvra(&self) -> String {
self.0.nvra()
}
}
#[pyclass(name = "ReqOperator", frozen, eq, eq_int, hash, from_py_object)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum PyReqOperator {
LT,
LE,
EQ,
GE,
GT,
}
impl From<PyReqOperator> for crate::ReqOperator {
fn from(op: PyReqOperator) -> Self {
match op {
PyReqOperator::LT => crate::ReqOperator::LT,
PyReqOperator::LE => crate::ReqOperator::LE,
PyReqOperator::EQ => crate::ReqOperator::EQ,
PyReqOperator::GE => crate::ReqOperator::GE,
PyReqOperator::GT => crate::ReqOperator::GT,
}
}
}
impl From<crate::ReqOperator> for PyReqOperator {
fn from(op: crate::ReqOperator) -> Self {
match op {
crate::ReqOperator::LT => PyReqOperator::LT,
crate::ReqOperator::LE => PyReqOperator::LE,
crate::ReqOperator::EQ => PyReqOperator::EQ,
crate::ReqOperator::GE => PyReqOperator::GE,
crate::ReqOperator::GT => PyReqOperator::GT,
}
}
}
#[derive(FromPyObject)]
enum OpArg {
Enum(PyReqOperator),
Str(String),
}
impl OpArg {
fn into_req_operator(self) -> PyResult<crate::ReqOperator> {
match self {
OpArg::Enum(e) => Ok(e.into()),
OpArg::Str(s) => match s.as_str() {
"<" | "LT" => Ok(crate::ReqOperator::LT),
"<=" | "LE" => Ok(crate::ReqOperator::LE),
"=" | "==" | "EQ" => Ok(crate::ReqOperator::EQ),
">=" | "GE" => Ok(crate::ReqOperator::GE),
">" | "GT" => Ok(crate::ReqOperator::GT),
_ => Err(pyo3::exceptions::PyValueError::new_err(format!(
"invalid operator: {s:?} (expected <, <=, =, >=, or >)"
))),
},
}
}
}
#[pyclass(name = "Requirement", frozen, eq, hash, from_py_object)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct PyRequirement(crate::Requirement<'static>);
#[pymethods]
impl PyRequirement {
#[new]
#[pyo3(signature = (name, op=None, evr=None))]
fn new(name: &str, op: Option<OpArg>, evr: Option<PyEvr>) -> PyResult<Self> {
match (op, evr) {
(None, None) => Ok(PyRequirement(crate::Requirement::new(name.to_owned()))),
(Some(op_arg), Some(py_evr)) => {
let op = op_arg.into_req_operator()?;
let (epoch, version, release) = py_evr.0.values();
let evr = crate::Evr::new(epoch.to_owned(), version.to_owned(), release.to_owned());
Ok(PyRequirement(crate::Requirement::with_constraint(
name.to_owned(),
op,
evr,
)))
}
_ => Err(pyo3::exceptions::PyValueError::new_err(
"op and evr must both be provided, or both omitted",
)),
}
}
#[getter]
fn name(&self) -> &str {
self.0.name()
}
#[getter]
fn constraint(&self) -> Option<(PyReqOperator, PyEvr)> {
self.0
.constraint()
.map(|(op, evr)| (PyReqOperator::from(op), PyEvr::from(evr.clone())))
}
fn satisfies(&self, name: &str, evr: &PyEvr) -> bool {
self.0.satisfies(name, &evr.0)
}
fn __repr__(&self) -> String {
format!("Requirement({})", self.0)
}
fn __str__(&self) -> String {
self.0.to_string()
}
}
#[pyclass(name = "EvrSortKey", frozen)]
pub struct PyEvrSortKey;
#[pymethods]
impl PyEvrSortKey {
#[classmethod]
#[pyo3(signature = (epoch, version, release))]
fn from_values<'py>(
_cls: &Bound<'_, PyType>,
py: Python<'py>,
epoch: Option<&str>,
version: &str,
release: &str,
) -> Bound<'py, pyo3::types::PyBytes> {
let key = crate::EvrSortKey::from_values(epoch.unwrap_or(""), version, release);
pyo3::types::PyBytes::new(py, key.as_bytes())
}
#[classmethod]
fn parse<'py>(
_cls: &Bound<'_, PyType>,
py: Python<'py>,
evr: &str,
) -> Bound<'py, pyo3::types::PyBytes> {
let key = crate::EvrSortKey::parse(evr);
pyo3::types::PyBytes::new(py, key.as_bytes())
}
}
#[pyfunction]
fn evr_compare(evr1: &str, evr2: &str) -> i32 {
match crate::rpm_evr_compare(evr1, evr2) {
Ordering::Less => -1,
Ordering::Equal => 0,
Ordering::Greater => 1,
}
}
#[pyfunction]
fn evr_sort(evrs: Vec<String>) -> Vec<String> {
let mut parsed: Vec<(crate::Evr<'_>, usize)> = evrs
.iter()
.enumerate()
.map(|(i, s)| (crate::Evr::parse(s), i))
.collect();
parsed.sort_unstable();
parsed.into_iter().map(|(_, i)| evrs[i].clone()).collect()
}
#[pyfunction]
fn nevra_sort(nevras: Vec<String>) -> Vec<String> {
let mut parsed: Vec<(crate::Nevra<'_>, usize)> = nevras
.iter()
.enumerate()
.map(|(i, s)| (crate::Nevra::parse(s), i))
.collect();
parsed.sort_unstable();
parsed.into_iter().map(|(_, i)| nevras[i].clone()).collect()
}
#[pyfunction]
#[pyo3(name = "version_sortkey")]
fn py_version_sortkey<'py>(py: Python<'py>, version: &str) -> Bound<'py, pyo3::types::PyBytes> {
let key = crate::version_sortkey(version);
pyo3::types::PyBytes::new(py, &key)
}
#[pymodule]
pub fn rpm_version(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyEvr>()?;
m.add_class::<PyNevra>()?;
m.add_class::<PyReqOperator>()?;
m.add_class::<PyRequirement>()?;
m.add_class::<PyEvrSortKey>()?;
m.add_function(wrap_pyfunction!(evr_compare, m)?)?;
m.add_function(wrap_pyfunction!(evr_sort, m)?)?;
m.add_function(wrap_pyfunction!(nevra_sort, m)?)?;
m.add_function(wrap_pyfunction!(py_version_sortkey, m)?)?;
Ok(())
}