feat: FromPyFromStr and ToStrToPy macros YAY
This commit is contained in:
@@ -1,18 +1,13 @@
|
||||
use std::{convert::Infallible, fmt::Display, str::FromStr};
|
||||
|
||||
use pyo3::{
|
||||
exceptions::{PyException, PyValueError},
|
||||
prelude::*,
|
||||
types::PyString,
|
||||
};
|
||||
use python_utils::{FromPyFromStr, ToStrToPy};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use super::{
|
||||
domain::Domain,
|
||||
object_id::{ObjectId, ObjectIdParsingError},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, FromPyFromStr, ToStrToPy)]
|
||||
pub struct EntityId(pub Domain, pub ObjectId);
|
||||
|
||||
#[derive(Debug, Clone, Snafu)]
|
||||
@@ -47,47 +42,3 @@ impl Display for EntityId {
|
||||
write!(f, "{domain}.{object_id}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ExtractEntityIdError {
|
||||
/// couldn't extract the object as a string
|
||||
ExtractStringError { source: PyErr },
|
||||
|
||||
/// couldn't parse the string as an [`EntityId`]
|
||||
ParseError { source: EntityIdParsingError },
|
||||
}
|
||||
|
||||
impl From<ExtractEntityIdError> for PyErr {
|
||||
fn from(error: ExtractEntityIdError) -> Self {
|
||||
match &error {
|
||||
ExtractEntityIdError::ExtractStringError { .. } => {
|
||||
PyException::new_err(error.to_string())
|
||||
}
|
||||
ExtractEntityIdError::ParseError { .. } => PyValueError::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 EntityId {
|
||||
type Error = ExtractEntityIdError;
|
||||
|
||||
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
||||
let s = ob.extract().context(ExtractStringSnafu)?;
|
||||
let entity_id = EntityId::from_str(s).context(ParseSnafu)?;
|
||||
|
||||
Ok(entity_id)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
||||
impl<'py> IntoPyObject<'py> for EntityId {
|
||||
type Target = PyString;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
let s = self.to_string();
|
||||
s.into_pyobject(py)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use std::{convert::Infallible, fmt::Display, str::FromStr, sync::Arc};
|
||||
|
||||
use pyo3::{exceptions::PyTypeError, prelude::*, types::PyString};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use python_utils::{FromPyFromStr, ToStrToPy};
|
||||
use ulid::Ulid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, FromPyFromStr, ToStrToPy)]
|
||||
pub enum Id {
|
||||
Ulid(Ulid),
|
||||
Other(Arc<str>),
|
||||
}
|
||||
|
||||
#[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()),
|
||||
impl From<String> for Id {
|
||||
fn from(s: String) -> Self {
|
||||
if let Ok(ulid) = s.parse() {
|
||||
Id::Ulid(ulid)
|
||||
} else {
|
||||
Id::Other(s.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)?;
|
||||
impl FromStr for Id {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(ulid) = s.parse() {
|
||||
Ok(Id::Ulid(ulid))
|
||||
} else {
|
||||
@@ -39,18 +31,11 @@ impl<'a, 'py> FromPyObject<'a, '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;
|
||||
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
impl Display for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Id::Ulid(ulid) => ulid.to_string().into_pyobject(py),
|
||||
Id::Other(id) => id.into_pyobject(py),
|
||||
Id::Ulid(ulid) => write!(f, "{ulid}"),
|
||||
Id::Other(other) => write!(f, "{other}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,39 @@
|
||||
use pyo3::exceptions::{PyTypeError, PyValueError};
|
||||
use pyo3::prelude::*;
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::FromPyObject;
|
||||
use python_utils::{FromPyFromStr, ToStrToPy};
|
||||
use snafu::Snafu;
|
||||
|
||||
use crate::{entity_id::EntityId, state_object::StateObject};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
// TODO: replace with a derive(PyFromStrLiteral) / #[literal = "state_changed"] once I learn how to make something like that and see about serde or strum integration or inspiration
|
||||
#[derive(Debug, Clone, FromPyFromStr, ToStrToPy)]
|
||||
pub struct Type;
|
||||
|
||||
/// expected a string of value "state_changed", but got {actual}
|
||||
#[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 },
|
||||
pub struct ParseTypeError {
|
||||
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()),
|
||||
impl FromStr for Type {
|
||||
type Err = ParseTypeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s == "state_changed" {
|
||||
Ok(Self)
|
||||
} else {
|
||||
Err(ParseTypeError {
|
||||
actual: s.to_owned(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(PyFromStrLiteral) / #[literal = "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(ExtractTypeError::UnexpectedValue { actual: s.into() })
|
||||
}
|
||||
impl Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "state_changed")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::{convert::Infallible, str::FromStr};
|
||||
|
||||
use pyo3::{types::{PyDict, PyString, PyDictMethods}, Bound, IntoPyObject, Python, PyErr};
|
||||
use python_utils::IntoPyObjectViaDisplay;
|
||||
use pyo3::{
|
||||
types::{PyDict, PyDictMethods, PyString},
|
||||
Bound, IntoPyObject, PyErr, Python,
|
||||
};
|
||||
use python_utils::{FromPyFromStr, IntoPyObjectViaDisplay, ToStrToPy};
|
||||
use snafu::Snafu;
|
||||
use strum::EnumString;
|
||||
use url::Url;
|
||||
@@ -84,7 +87,7 @@ impl TryFrom<String> for NonSpecialMessage {
|
||||
}
|
||||
|
||||
/// How much of a notification is visible on the lock screen
|
||||
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
|
||||
#[derive(Debug, Clone, Default, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum Visibility {
|
||||
/// always show all notification content
|
||||
@@ -98,38 +101,14 @@ pub enum Visibility {
|
||||
Secret,
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
||||
impl<'py> IntoPyObject<'py> for Visibility {
|
||||
type Target = PyString;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
let s = self.to_string();
|
||||
s.into_pyobject(py)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, EnumString, strum::Display)]
|
||||
#[derive(Debug, Clone, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
|
||||
pub enum Behavior {
|
||||
/// prompt for text to return with the event
|
||||
#[strum(serialize = "textInput")]
|
||||
TextInput,
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
||||
impl<'py> IntoPyObject<'py> for Behavior {
|
||||
type Target = PyString;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
let s = self.to_string();
|
||||
s.into_pyobject(py)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
|
||||
#[derive(Debug, Clone, Default, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
|
||||
#[strum(serialize_all = "camelCase")]
|
||||
pub enum ActivationMode {
|
||||
/// launch the app when tapped
|
||||
@@ -139,18 +118,6 @@ pub enum ActivationMode {
|
||||
Background,
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
||||
impl<'py> IntoPyObject<'py> for ActivationMode {
|
||||
type Target = PyString;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
let s = self.to_string();
|
||||
s.into_pyobject(py)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: better typed versions like `CallNumber` or `OpenWebpage` where Action: From<CallNumber> and Action: From<OpenWebPage>
|
||||
#[derive(Debug, Clone, IntoPyObject, typed_builder::TypedBuilder)]
|
||||
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
|
||||
@@ -190,7 +157,7 @@ pub struct Action {
|
||||
pub icon: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
|
||||
#[derive(Debug, Clone, Default, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
|
||||
#[strum(serialize_all = "snake_case", suffix = "_stream")]
|
||||
pub enum MediaStream {
|
||||
Alarm,
|
||||
@@ -203,18 +170,6 @@ pub enum MediaStream {
|
||||
System,
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
|
||||
impl<'py> IntoPyObject<'py> for MediaStream {
|
||||
type Target = PyString;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = Infallible;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
let s = self.to_string();
|
||||
s.into_pyobject(py)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, typed_builder::TypedBuilder)]
|
||||
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
|
||||
pub struct NotifyMobileAppServiceDataData {
|
||||
@@ -232,7 +187,13 @@ impl<'py> IntoPyObject<'py> for NotifyMobileAppServiceDataData {
|
||||
type Error = PyErr;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
let NotifyMobileAppServiceDataData { actions, command, media_stream, tts_text, visibility } = self;
|
||||
let NotifyMobileAppServiceDataData {
|
||||
actions,
|
||||
command,
|
||||
media_stream,
|
||||
tts_text,
|
||||
visibility,
|
||||
} = self;
|
||||
|
||||
let dict = PyDict::new(py);
|
||||
|
||||
@@ -279,7 +240,11 @@ impl<'py> IntoPyObject<'py> for NotifyMobileAppServiceData {
|
||||
type Error = PyErr;
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
let NotifyMobileAppServiceData { message, title, data } = self;
|
||||
let NotifyMobileAppServiceData {
|
||||
message,
|
||||
title,
|
||||
data,
|
||||
} = self;
|
||||
|
||||
let dict = PyDict::new(py);
|
||||
|
||||
@@ -293,4 +258,4 @@ impl<'py> IntoPyObject<'py> for NotifyMobileAppServiceData {
|
||||
|
||||
Ok(dict)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use mitsein::vec1::Vec1;
|
||||
use pyo3::{types::PyAnyMethods, IntoPyObject, Python};
|
||||
|
||||
use crate::{
|
||||
object_id::ObjectId,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{future::Future, str::FromStr, sync::Arc};
|
||||
use std::{future::Future, sync::Arc};
|
||||
|
||||
use emitter_and_signal::{Signal, SignalExt};
|
||||
use pyo3::{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
use pyo3::{exceptions::PyValueError, PyErr};
|
||||
use python_utils::{FromPyFromStr, ToStrToPy};
|
||||
use snafu::Snafu;
|
||||
|
||||
// TODO: derive(PyFromStr) (analogous to serde_with::DeserializeFromStr) once I make one
|
||||
#[derive(Debug, Clone, derive_more::Display)]
|
||||
#[derive(Debug, Clone, derive_more::Display, FromPyFromStr, ToStrToPy)]
|
||||
pub struct Slug(Arc<str>);
|
||||
|
||||
#[derive(Debug, Clone, Snafu)]
|
||||
|
||||
@@ -1,55 +1,12 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::{
|
||||
exceptions::{PyException, PyValueError},
|
||||
prelude::*,
|
||||
};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use python_utils::{FromPyFromStr, ToStrToPy};
|
||||
use strum::EnumString;
|
||||
|
||||
/// A state in Home Assistant that is known to represent an error of some kind:
|
||||
/// * `unavailable` (the device is likely offline or unreachable from the Home Assistant instance)
|
||||
/// * `unknown` (I don't know how to explain this one)
|
||||
#[derive(Debug, Clone, EnumString, strum::Display)]
|
||||
#[derive(Debug, Clone, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum ErrorState {
|
||||
Unavailable,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ExtractErrorStateError {
|
||||
/// couldn't extract the object as a string
|
||||
ExtractStringError { source: PyErr },
|
||||
|
||||
/// the string had an unexpected value
|
||||
UnexpectedValue {
|
||||
source: <ErrorState as FromStr>::Err,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ExtractErrorStateError> for PyErr {
|
||||
fn from(error: ExtractErrorStateError) -> Self {
|
||||
match &error {
|
||||
ExtractErrorStateError::ExtractStringError { .. } => {
|
||||
PyException::new_err(error.to_string())
|
||||
}
|
||||
ExtractErrorStateError::UnexpectedValue { .. } => {
|
||||
PyValueError::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 ErrorState {
|
||||
type Error = ExtractErrorStateError;
|
||||
|
||||
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
||||
let s = ob.extract::<String>().context(ExtractStringSnafu)?;
|
||||
|
||||
let state = ErrorState::from_str(&s).context(UnexpectedValueSnafu)?;
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::{convert::Infallible, str::FromStr};
|
||||
|
||||
use pyo3::{exceptions::PyException, prelude::*};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use python_utils::{FromPyFromStr, ToStrToPy};
|
||||
|
||||
pub mod error_state;
|
||||
pub mod unexpected_state;
|
||||
@@ -9,13 +8,30 @@ pub mod unexpected_state;
|
||||
pub use error_state::ErrorState;
|
||||
pub use unexpected_state::UnexpectedState;
|
||||
|
||||
#[derive(Debug, Clone, derive_more::Display)]
|
||||
#[derive(Debug, Clone, derive_more::Display, FromPyFromStr, ToStrToPy)]
|
||||
pub enum HomeAssistantState<State> {
|
||||
Ok(State),
|
||||
Err(ErrorState),
|
||||
UnexpectedErr(UnexpectedState),
|
||||
}
|
||||
|
||||
impl<State> From<String> for HomeAssistantState<State>
|
||||
where
|
||||
State: FromStr,
|
||||
{
|
||||
fn from(s: String) -> Self {
|
||||
if let Ok(ok) = State::from_str(&s) {
|
||||
return HomeAssistantState::Ok(ok);
|
||||
}
|
||||
|
||||
if let Ok(error) = ErrorState::from_str(&s) {
|
||||
return HomeAssistantState::Err(error);
|
||||
}
|
||||
|
||||
HomeAssistantState::UnexpectedErr(UnexpectedState(s.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<State: FromStr> FromStr for HomeAssistantState<State> {
|
||||
type Err = Infallible;
|
||||
|
||||
@@ -31,34 +47,3 @@ impl<State: FromStr> FromStr for HomeAssistantState<State> {
|
||||
Ok(HomeAssistantState::UnexpectedErr(UnexpectedState(s.into())))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ExtractHomeAssistantStateError {
|
||||
/// couldn't extract the object as a string
|
||||
ExtractStringError { source: PyErr },
|
||||
}
|
||||
|
||||
impl From<ExtractHomeAssistantStateError> for PyErr {
|
||||
fn from(error: ExtractHomeAssistantStateError) -> Self {
|
||||
match &error {
|
||||
ExtractHomeAssistantStateError::ExtractStringError { .. } => {
|
||||
PyException::new_err(error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with a derive(PyFromStr) (analogous to serde_with::DeserializeFromStr) once I make one
|
||||
impl<'a, 'py, State: FromStr + FromPyObject<'a, 'py>> FromPyObject<'a, 'py>
|
||||
for HomeAssistantState<State>
|
||||
{
|
||||
type Error = ExtractHomeAssistantStateError;
|
||||
|
||||
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
||||
let s = ob.extract::<&str>().context(ExtractStringSnafu)?;
|
||||
|
||||
let Ok(state) = s.parse();
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use pyo3::{
|
||||
exceptions::{PyException, PyValueError},
|
||||
prelude::*,
|
||||
};
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use python_utils::{FromPyFromStr, ToStrToPy};
|
||||
use strum::EnumString;
|
||||
use uom::{
|
||||
si::{
|
||||
@@ -18,7 +14,7 @@ use uom::{
|
||||
};
|
||||
|
||||
/// Power units
|
||||
#[derive(Debug, Clone, Copy, EnumString, strum::Display)]
|
||||
#[derive(Debug, Clone, Copy, EnumString, strum::Display, FromPyFromStr, ToStrToPy)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum UnitOfMeasurement {
|
||||
#[strum(serialize = "mW")]
|
||||
@@ -37,42 +33,6 @@ pub enum UnitOfMeasurement {
|
||||
BtuPerhour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ExtractUnitOfMeasurementError {
|
||||
/// couldn't extract the object as a string
|
||||
ExtractStringError { source: PyErr },
|
||||
|
||||
/// couldn't parse the string as a [`UnitOfMeasurement`]
|
||||
ParseError {
|
||||
source: <UnitOfMeasurement as FromStr>::Err,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ExtractUnitOfMeasurementError> for PyErr {
|
||||
fn from(error: ExtractUnitOfMeasurementError) -> Self {
|
||||
match &error {
|
||||
ExtractUnitOfMeasurementError::ExtractStringError { .. } => {
|
||||
PyException::new_err(error.to_string())
|
||||
}
|
||||
ExtractUnitOfMeasurementError::ParseError { .. } => {
|
||||
PyValueError::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 UnitOfMeasurement {
|
||||
type Error = ExtractUnitOfMeasurementError;
|
||||
|
||||
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
||||
let s = obj.extract().context(ExtractStringSnafu)?;
|
||||
let unit_of_measurement = UnitOfMeasurement::from_str(s).context(ParseSnafu)?;
|
||||
|
||||
Ok(unit_of_measurement)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnitOfMeasurement {
|
||||
pub fn into_uom<V>(&self, amount: V) -> Power<V>
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user