chore+feat(home-assistant): update to pyo3 0.27 and update extraction errors, switch out SmolStr for Arc<str>, tighten up light service calls and implement some for notify, start implementing units of measurement like for power
This commit is contained in:
@@ -28,7 +28,7 @@ impl<'py, Event: IntoPyObject<'py>> IntoPyObject<'py> for Context<Event> {
|
||||
.bind(py);
|
||||
|
||||
let context_class = homeassistant_core.getattr("Context")?;
|
||||
let context_class = context_class.downcast_into::<PyType>()?;
|
||||
let context_class = context_class.cast_into::<PyType>()?;
|
||||
|
||||
let context_instance = context_class.call1((self.user_id, self.parent_id, self.id))?;
|
||||
|
||||
|
||||
@@ -1,18 +1,35 @@
|
||||
use std::convert::Infallible;
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyString};
|
||||
use smol_str::SmolStr;
|
||||
use pyo3::{exceptions::PyTypeError, prelude::*, types::PyString};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use ulid::Ulid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Id {
|
||||
Ulid(Ulid),
|
||||
Other(SmolStr),
|
||||
Other(Arc<str>),
|
||||
}
|
||||
|
||||
impl<'py> FromPyObject<'py> for Id {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let s = ob.extract::<String>()?;
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ExtractIdError {
|
||||
/// couldn't extract the given object as a string
|
||||
ExtractStringError { source: PyErr },
|
||||
}
|
||||
|
||||
impl From<ExtractIdError> for PyErr {
|
||||
fn from(error: ExtractIdError) -> Self {
|
||||
match &error {
|
||||
ExtractIdError::ExtractStringError { .. } => PyTypeError::new_err(error.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(PyFromStr) (analogous to serde_with::DeserializeFromStr) once I make one
|
||||
impl<'a, 'py> FromPyObject<'a, 'py> for Id {
|
||||
type Error = ExtractIdError;
|
||||
|
||||
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
||||
let s = ob.extract::<&str>().context(ExtractStringSnafu)?;
|
||||
|
||||
if let Ok(ulid) = s.parse() {
|
||||
Ok(Id::Ulid(ulid))
|
||||
@@ -22,6 +39,7 @@ impl<'py> FromPyObject<'py> for Id {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
||||
impl<'py> IntoPyObject<'py> for Id {
|
||||
type Target = PyString;
|
||||
|
||||
@@ -32,7 +50,7 @@ impl<'py> IntoPyObject<'py> for Id {
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
match self {
|
||||
Id::Ulid(ulid) => ulid.to_string().into_pyobject(py),
|
||||
Id::Other(id) => id.as_str().into_pyobject(py),
|
||||
Id::Other(id) => id.into_pyobject(py),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::{exceptions::PyValueError, prelude::*};
|
||||
use pyo3::{
|
||||
exceptions::{PyException, PyTypeError, PyValueError},
|
||||
prelude::*,
|
||||
};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
|
||||
#[derive(Debug, Clone, strum::EnumString, strum::Display)]
|
||||
#[strum(serialize_all = "UPPERCASE")]
|
||||
@@ -9,12 +13,41 @@ pub enum EventOrigin {
|
||||
Remote,
|
||||
}
|
||||
|
||||
impl<'py> FromPyObject<'py> for EventOrigin {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let s = ob.str()?;
|
||||
let s = s.extract()?;
|
||||
let event_origin =
|
||||
EventOrigin::from_str(s).map_err(|err| PyValueError::new_err(err.to_string()))?;
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ExtractEventOriginError {
|
||||
/// couldn't turn the object into a string with `str` (Python function)
|
||||
ToStrError { source: PyErr },
|
||||
|
||||
/// after calling `str` on the object, it's somehow not extractable as a string?!
|
||||
NotString { source: PyErr },
|
||||
|
||||
/// this is not an expected value for [`EventOrigin`]
|
||||
UnexpectedValue {
|
||||
source: <EventOrigin as FromStr>::Err,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ExtractEventOriginError> for PyErr {
|
||||
fn from(error: ExtractEventOriginError) -> Self {
|
||||
match &error {
|
||||
ExtractEventOriginError::ToStrError { .. } => PyException::new_err(error.to_string()),
|
||||
ExtractEventOriginError::NotString { .. } => PyTypeError::new_err(error.to_string()),
|
||||
ExtractEventOriginError::UnexpectedValue { .. } => {
|
||||
PyValueError::new_err(error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'py> FromPyObject<'a, 'py> for EventOrigin {
|
||||
type Error = ExtractEventOriginError;
|
||||
|
||||
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
||||
let s = ob.str().context(ToStrSnafu)?;
|
||||
// TODO: could I go straight to trying to extract an &str without calling .str() first? if so then I could
|
||||
// TODO: replace with a derive(PyFromStr) (analogous to serde_with::DeserializeFromStr) once I make one
|
||||
let s = s.extract().context(NotStringSnafu)?;
|
||||
let event_origin = EventOrigin::from_str(s).context(UnexpectedValueSnafu)?;
|
||||
|
||||
Ok(event_origin)
|
||||
}
|
||||
|
||||
@@ -1,21 +1,41 @@
|
||||
use pyo3::exceptions::PyValueError;
|
||||
use pyo3::exceptions::{PyTypeError, PyValueError};
|
||||
use pyo3::prelude::*;
|
||||
use snafu::{ResultExt, Snafu};
|
||||
|
||||
use crate::{entity_id::EntityId, state_object::StateObject};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Type;
|
||||
|
||||
impl<'py> FromPyObject<'py> for Type {
|
||||
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let s = ob.extract::<&str>()?;
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ExtractTypeError {
|
||||
/// couldn't extract this object as a string
|
||||
ExtractStringError { source: PyErr },
|
||||
|
||||
/// expected a string of value "state_changed", but got {actual}
|
||||
UnexpectedValue { actual: String },
|
||||
}
|
||||
|
||||
impl From<ExtractTypeError> for PyErr {
|
||||
fn from(error: ExtractTypeError) -> Self {
|
||||
match &error {
|
||||
ExtractTypeError::ExtractStringError { .. } => PyTypeError::new_err(error.to_string()),
|
||||
ExtractTypeError::UnexpectedValue { .. } => PyValueError::new_err(error.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(PyFromStrLiteral) / #[str = "state_changed"] once I learn how to make something like that and see about serde or strum integration or inspiration
|
||||
impl<'a, 'py> FromPyObject<'a, 'py> for Type {
|
||||
type Error = ExtractTypeError;
|
||||
|
||||
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
||||
let s = ob.extract::<&str>().context(ExtractStringSnafu)?;
|
||||
|
||||
if s == "state_changed" {
|
||||
Ok(Type)
|
||||
} else {
|
||||
Err(PyValueError::new_err(format!(
|
||||
"expected a string of value 'state_changed', but got {s}"
|
||||
)))
|
||||
Err(ExtractTypeError::UnexpectedValue { actual: s.into() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user