feat: implement StringLiteral macro

This commit is contained in:
J / Jacob Babich
2026-07-09 22:20:24 -04:00
parent d6ad9972c7
commit 0565624599
10 changed files with 209 additions and 121 deletions

View File

@@ -26,6 +26,7 @@ pyo3 = { workspace = true }
pyo3-async-runtimes = { workspace = true, features = ["tokio-runtime"] }
python-utils = { path = "../python-utils", features = ["macros"] }
snafu = { workspace = true }
string-literal = { path = "../string-literal", features = ["macros"] }
strum = { workspace = true, features = ["derive"] }
tokio = { workspace = true }
tracing = { optional = true, workspace = true }

View File

@@ -4,39 +4,14 @@ use std::str::FromStr;
use pyo3::FromPyObject;
use python_utils::{FromPyFromStr, ToStrToPy};
use snafu::Snafu;
use string_literal::StringLiteral;
use crate::{entity_id::EntityId, state_object::StateObject};
// 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)]
#[derive(Debug, Clone, StringLiteral, FromPyFromStr, ToStrToPy)]
#[string_literal(value = "state_changed")]
pub struct Type;
/// expected a string of value "state_changed", but got {actual}
#[derive(Debug, Snafu)]
pub struct ParseTypeError {
actual: 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(),
})
}
}
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "state_changed")
}
}
#[derive(Debug, FromPyObject)]
#[pyo3(from_item_all)]
pub struct Data<

View File

@@ -1,12 +1,10 @@
use std::{future::Future, sync::Arc};
use emitter_and_signal::{Signal, SignalExt};
use pyo3::{
exceptions::{PyException, PyValueError},
prelude::*,
};
use python_utils::FromPyObjectViaParse;
use snafu::{ensure, ResultExt, Snafu};
use pyo3::{FromPyObject, Py, PyAny, PyErr, Python};
use python_utils::{FromPyFromStr, FromPyObjectViaParse, ToStrToPy};
use snafu::{ResultExt, Snafu};
use string_literal::StringLiteral;
use super::super::state_classes::measurement::Measurement;
use crate::{
@@ -18,45 +16,10 @@ use crate::{
unit_of_measurement::power::UnitOfMeasurement,
};
#[derive(Debug)]
#[derive(Debug, Clone, Copy, StringLiteral, FromPyFromStr, ToStrToPy)]
#[string_literal(value = "power")]
struct Power;
#[derive(Debug, Snafu)]
pub enum ExtractPowerError {
/// couldn't extract the object as a string
ExtractStringError { source: PyErr },
/// the string {actual:?} is not "power" like it's supposed to be
NotPower { actual: String },
}
impl From<ExtractPowerError> for PyErr {
fn from(error: ExtractPowerError) -> Self {
match &error {
ExtractPowerError::ExtractStringError { .. } => PyException::new_err(error.to_string()),
ExtractPowerError::NotPower { .. } => PyValueError::new_err(error.to_string()),
}
}
}
// 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 Power {
type Error = ExtractPowerError;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
let string: &str = obj.extract().context(ExtractStringSnafu)?;
ensure!(
string == "power",
NotPowerSnafu {
actual: string.to_owned()
}
);
Ok(Self)
}
}
#[derive(Debug, FromPyObject)]
#[pyo3(from_item_all)]
pub struct PowerSensorAttributes {

View File

@@ -1,48 +1,6 @@
use pyo3::{
exceptions::{PyException, PyValueError},
prelude::*,
};
use snafu::{ensure, ResultExt, Snafu};
use python_utils::{FromPyFromStr, ToStrToPy};
use string_literal::StringLiteral;
#[derive(Debug)]
#[derive(Debug, Clone, Copy, StringLiteral, FromPyFromStr, ToStrToPy)]
#[string_literal(value = "measurement")]
pub struct Measurement;
#[derive(Debug, Snafu)]
pub enum ExtractMeasurementError {
/// couldn't extract the object as a string
ExtractStringError { source: PyErr },
/// the string {actual:?} is not "measurement" like it's supposed to be
NotMeasurement { actual: String },
}
impl From<ExtractMeasurementError> for PyErr {
fn from(error: ExtractMeasurementError) -> Self {
match &error {
ExtractMeasurementError::ExtractStringError { .. } => {
PyException::new_err(error.to_string())
}
ExtractMeasurementError::NotMeasurement { .. } => {
PyValueError::new_err(error.to_string())
}
}
}
}
// 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 Measurement {
type Error = ExtractMeasurementError;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
let string: &str = obj.extract().context(ExtractStringSnafu)?;
ensure!(
string == "measurement",
NotMeasurementSnafu {
actual: string.to_owned()
}
);
Ok(Self)
}
}