Skip to content

Commit

Permalink
support conversion of ints and strings directly into hash
Browse files Browse the repository at this point in the history
  • Loading branch information
benhall-7 committed Jan 5, 2025
1 parent cb9e11f commit ea73c76
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 29 deletions.
58 changes: 58 additions & 0 deletions src/duplicate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::{Hash, ParamList2, ParamStruct2, ParamType};

/// A fake clone implementation. PyO3 uses blanket implementations of FromPyObject for pyclasses
/// when they implement Clone. I need to have custom implementations for some types
pub(crate) trait Duplicate: Sized {
fn duplicate(&self) -> Self;
}

impl<T> Duplicate for T
where
T: Clone,
{
fn duplicate(&self) -> Self {
self.clone()
}
}

impl Duplicate for ParamType {
fn duplicate(&self) -> Self {
match self {
ParamType::Bool(v) => ParamType::Bool(*v),
ParamType::I8(v) => ParamType::I8(*v),
ParamType::U8(v) => ParamType::U8(*v),
ParamType::I16(v) => ParamType::I16(*v),
ParamType::U16(v) => ParamType::U16(*v),
ParamType::I32(v) => ParamType::I32(*v),
ParamType::U32(v) => ParamType::U32(*v),
ParamType::Float(v) => ParamType::Float(*v),
ParamType::Hash(v) => ParamType::Hash(v.duplicate()),
ParamType::Str(v) => ParamType::Str(v.clone()),
ParamType::List(v) => ParamType::List(v.duplicate()),
ParamType::Struct(v) => ParamType::Struct(v.duplicate()),
}
}
}

impl Duplicate for Hash {
fn duplicate(&self) -> Self {
Hash { inner: self.inner }
}
}

impl Duplicate for ParamList2 {
fn duplicate(&self) -> Self {
ParamList2(self.0.duplicate())
}
}

impl Duplicate for ParamStruct2 {
fn duplicate(&self) -> Self {
ParamStruct2(
self.0
.iter()
.map(|(h, p)| (h.duplicate(), p.duplicate()))
.collect(),
)
}
}
49 changes: 21 additions & 28 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use duplicate::Duplicate;
use prc::hash40::*;
use prc::*;
use pyo3::class::basic::CompareOp;
Expand All @@ -7,13 +8,15 @@ use pyo3::prelude::*;
use std::sync::{Arc, Mutex};
use std::vec::IntoIter;

mod duplicate;

#[pyclass(name = "param")]
#[derive(Debug)]
struct Param {
inner: Arc<Mutex<ParamType>>,
}

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, PartialEq)]
/// an enum designed to store sub-params as reference counted structs to allow python interaction
enum ParamType {
Bool(bool),
Expand All @@ -30,14 +33,14 @@ enum ParamType {
Struct(ParamStruct2),
}

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, PartialEq)]
struct ParamList2(Vec<Param>);

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, PartialEq)]
struct ParamStruct2(Vec<(Hash, Param)>);

#[pyclass(name = "hash")]
#[derive(Debug, Copy, Clone, Hash, PartialEq)]
#[derive(Debug, Hash, PartialEq)]
struct Hash {
inner: Hash40,
}
Expand Down Expand Up @@ -94,7 +97,7 @@ impl From<&Param> for ParamKind {
fn from(f: &Param) -> Self {
let mutex = &*f.inner;
let param_type = &*mutex.lock().unwrap();
ParamKind::from(&param_type.clone())
ParamKind::from(&(param_type.duplicate()))
}
}

Expand Down Expand Up @@ -173,7 +176,7 @@ impl Param {
impl Clone for Param {
fn clone(&self) -> Self {
Param {
inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
inner: Arc::new(Mutex::new(self.inner.lock().unwrap().duplicate())),
}
}
}
Expand Down Expand Up @@ -223,8 +226,8 @@ impl Param {
Param::from(ParamKind::from(value))
}
#[staticmethod]
fn hash(value: IntoHash) -> Self {
Param::from(ParamKind::from(Into::<Hash>::into(value)))
fn hash(value: Hash) -> Self {
Param::from(ParamKind::from(value))
}

#[staticmethod]
Expand Down Expand Up @@ -331,7 +334,7 @@ impl Param {
ParamType::I32(v) => v.into_py_any(py),
ParamType::U32(v) => v.into_py_any(py),
ParamType::Float(v) => v.into_py_any(py),
ParamType::Hash(v) => v.into_py_any(py),
ParamType::Hash(v) => v.duplicate().into_py_any(py),
ParamType::Str(v) => v.into_py_any(py),
ParamType::List(_) => Err(PyTypeError::new_err(
"Cannot access value on a list-type param",
Expand All @@ -353,7 +356,7 @@ impl Param {
ParamType::I32(v) => *v = value.extract(py)?,
ParamType::U32(v) => *v = value.extract(py)?,
ParamType::Float(v) => *v = value.extract(py)?,
ParamType::Hash(v) => *v = value.extract::<IntoHash>(py)?.into(),
ParamType::Hash(v) => *v = value.extract(py)?,
ParamType::Str(v) => *v = value.extract(py)?,
ParamType::List(_) => {
return Err(PyTypeError::new_err(
Expand Down Expand Up @@ -390,7 +393,7 @@ impl Param {
}
}
ParamType::Struct(v) => {
let index: Hash = key.extract::<IntoHash>(py)?.into();
let index: Hash = key.extract(py)?;
let mut col: Vec<Param> =
v.0.iter()
.filter(|(hash, _)| hash.inner == index.inner)
Expand Down Expand Up @@ -423,7 +426,7 @@ impl Param {
}
}
ParamType::Struct(v) => {
let index: Hash = key.extract::<IntoHash>(py)?.into();
let index: Hash = key.extract(py)?;
let mut col: Vec<&mut Param> =
v.0.iter_mut()
.filter(|(hash, _)| *hash == index)
Expand Down Expand Up @@ -492,7 +495,7 @@ impl Param {
ParamType::Struct(v) => {
let refs: IntoIter<PyObject> =
v.0.iter()
.map(|(h, p)| (*h, p.clone_ref()).into_py_any(py))
.map(|(h, p)| (h.duplicate(), p.clone_ref()).into_py_any(py))
.collect::<Result<Vec<_>, _>>()?
.into_iter();
Py::new(py, ParamIter { inner: refs })
Expand All @@ -509,7 +512,7 @@ impl Hash {
#[new]
fn new(py: Python, value: PyObject) -> PyResult<Hash> {
value
.extract::<IntoHash>(py)
.extract::<Hash>(py)
.map(|wrapper| Into::<Hash>::into(wrapper))
}

Expand Down Expand Up @@ -562,24 +565,20 @@ impl Hash {
}
}

/// A wrapper struct that allows automatic conversion of strings, numbers, or other hash classes
/// into the hash class
struct IntoHash(Hash);

impl<'py> FromPyObject<'py> for IntoHash {
impl<'py> FromPyObject<'py> for Hash {
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
if let Ok(v) = ob.downcast::<Hash>() {
Ok(IntoHash(v.try_borrow()?.clone()))
Ok(v.try_borrow()?.duplicate())
} else if let Ok(v) = ob.extract::<String>() {
let labels = Hash40::label_map();
let lock = labels.lock().unwrap();
lock.hash_of(&v).map(|hash| IntoHash(hash.into())).ok_or_else(|| {
lock.hash_of(&v).map(|hash| hash.into()).ok_or_else(|| {
PyLookupError::new_err(
"Could not convert this string into a hash. The label map does not contain the string, and is using strict conversion"
)
})
} else if let Ok(v) = ob.extract::<u64>() {
Ok(IntoHash(Hash { inner: Hash40(v) }))
Ok(Hash { inner: Hash40(v) })
} else {
Err(PyTypeError::new_err(
"Hash constructor accepts only Hash, string, or unsigned int64",
Expand All @@ -588,12 +587,6 @@ impl<'py> FromPyObject<'py> for IntoHash {
}
}

impl Into<Hash> for IntoHash {
fn into(self) -> Hash {
self.0
}
}

#[pyclass]
struct ParamIter {
inner: IntoIter<PyObject>,
Expand Down
1 change: 0 additions & 1 deletion test_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,3 @@
fighter[key].value = fighter_mods[key]

fighter_param.save("fighter_param_new.prc")

0 comments on commit ea73c76

Please sign in to comment.