From d9d3241c91539ee996e2eb44b2e681bf6bb9f1b1 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Sat, 13 Sep 2025 04:16:38 +0200 Subject: [PATCH] feat: WIP --- Cargo.lock | 43 +++ Cargo.toml | 2 +- config.lua | 6 +- definitions/automation:devices.lua | 40 +++ definitions/automation:secrets.lua | 6 + definitions/automation:utils.lua | 27 ++ definitions/automation:variables.lua | 6 + serde_luacats/Cargo.toml | 8 + serde_luacats/src/.ser.rs.pending-snap | 4 + serde_luacats/src/error.rs | 26 ++ serde_luacats/src/lib.rs | 5 + serde_luacats/src/ser.rs | 394 +++++++++++++++++++++++++ 12 files changed, 565 insertions(+), 2 deletions(-) create mode 100644 definitions/automation:devices.lua create mode 100644 definitions/automation:secrets.lua create mode 100644 definitions/automation:utils.lua create mode 100644 definitions/automation:variables.lua create mode 100644 serde_luacats/Cargo.toml create mode 100644 serde_luacats/src/.ser.rs.pending-snap create mode 100644 serde_luacats/src/error.rs create mode 100644 serde_luacats/src/lib.rs create mode 100644 serde_luacats/src/ser.rs diff --git a/Cargo.lock b/Cargo.lock index accc16c..2b25af7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -345,6 +345,18 @@ dependencies = [ "winnow", ] +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -455,6 +467,12 @@ dependencies = [ "embedded-hal", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "env_home" version = "0.1.0" @@ -969,6 +987,17 @@ dependencies = [ "serde", ] +[[package]] +name = "insta" +version = "1.43.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0" +dependencies = [ + "console", + "once_cell", + "similar", +] + [[package]] name = "inventory" version = "0.3.21" @@ -1816,6 +1845,14 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_luacats" +version = "0.1.0" +dependencies = [ + "insta", + "serde", +] + [[package]] name = "serde_path_to_error" version = "0.1.17" @@ -1873,6 +1910,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "slab" version = "0.4.11" diff --git a/Cargo.toml b/Cargo.toml index 952c0c4..a88cf5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ "automation_lib", "automation_macro", "google_home/google_home", - "google_home/google_home_macro", + "google_home/google_home_macro", "serde_luacats", ] [workspace.dependencies] diff --git a/config.lua b/config.lua index f3cdc5b..cafbcf7 100644 --- a/config.lua +++ b/config.lua @@ -2,17 +2,21 @@ local devices = require("automation:devices") local device_manager = require("automation:device_manager") local utils = require("automation:utils") local secrets = require("automation:secrets") -local debug = require("automation:variables").debug or false +local debug = require("automation:variables").debug and true or false print(_VERSION) local host = utils.get_hostname() print("Running @" .. host) +--- @param topic string +--- @return string local function mqtt_z2m(topic) return "zigbee2mqtt/" .. topic end +--- @param topic string +--- @return string local function mqtt_automation(topic) return "automation/" .. topic end diff --git a/definitions/automation:devices.lua b/definitions/automation:devices.lua new file mode 100644 index 0000000..ef54c5b --- /dev/null +++ b/definitions/automation:devices.lua @@ -0,0 +1,40 @@ +---@meta + +local devices + +---@class Action +---@field action "broadcast" +---@field extras table | nil +---@field label string | nil +---@clear clear bool|nil + +---@alias Priority +---| "min" +---| "low" +---| "default" +---| "high" +---| "max" + +---@class Notification +---@field title string +---@field message string | nil +-- NOTE: It might be possible to specify this down to the actual possible values +---@field tags string[] | nil +---@field priority Priority | nil +---@field actions Action[] | nil + +---@class Ntfy +local Ntfy +---@async +---@param notification Notification +function Ntfy:send_notification(notification) end + +---@class NtfyConfig +---@field topic string + +devices.Ntfy = {} +---@param config NtfyConfig +---@return Ntfy +function devices.Ntfy.new(config) end + +return devices diff --git a/definitions/automation:secrets.lua b/definitions/automation:secrets.lua new file mode 100644 index 0000000..6494943 --- /dev/null +++ b/definitions/automation:secrets.lua @@ -0,0 +1,6 @@ +---@meta + +---@type table +local secrets + +return secrets diff --git a/definitions/automation:utils.lua b/definitions/automation:utils.lua new file mode 100644 index 0000000..d8b380c --- /dev/null +++ b/definitions/automation:utils.lua @@ -0,0 +1,27 @@ +---@meta + +local utils + +---@class Timeout +local Timeout +---@async +---@param timeout number +---@param callback fun() +function Timeout:start(timeout, callback) end +---@async +function Timeout:cancel() end +---@async +---@return boolean +function Timeout:is_waiting() end + +utils.Timeout = {} +---@return Timeout +function utils.Timeout.new() end + +--- @return string hostname +function utils.get_hostname() end + +--- @return number epoch +function utils.get_epoch() end + +return utils diff --git a/definitions/automation:variables.lua b/definitions/automation:variables.lua new file mode 100644 index 0000000..6f09c6d --- /dev/null +++ b/definitions/automation:variables.lua @@ -0,0 +1,6 @@ +---@meta + +---@type table +local variables + +return variables diff --git a/serde_luacats/Cargo.toml b/serde_luacats/Cargo.toml new file mode 100644 index 0000000..57af995 --- /dev/null +++ b/serde_luacats/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "serde_luacats" +version = "0.1.0" +edition = "2024" + +[dependencies] +insta = "1.43.2" +serde = { workspace = true } diff --git a/serde_luacats/src/.ser.rs.pending-snap b/serde_luacats/src/.ser.rs.pending-snap new file mode 100644 index 0000000..f0f997e --- /dev/null +++ b/serde_luacats/src/.ser.rs.pending-snap @@ -0,0 +1,4 @@ +{"run_id":"1757770490-872092575","line":386,"new":{"module_name":"serde_luacats__ser","snapshot_name":"test","metadata":{"source":"serde_luacats/src/ser.rs","assertion_line":386,"expression":"to_string(&test).unwrap()"},"snapshot":"---@class Test\n---@field int integer\n---@field text string\n---@field other ---@class Other\n---@field cool bool"},"old":{"module_name":"serde_luacats__ser","metadata":{},"snapshot":"---@class Test\n---@field int integer\n---@field text string"}} +{"run_id":"1757770536-381047902","line":386,"new":{"module_name":"serde_luacats__ser","snapshot_name":"test","metadata":{"source":"serde_luacats/src/ser.rs","assertion_line":386,"expression":"to_string(&test).unwrap()"},"snapshot":"---@class Test\n---@field int integer\n---@field text string\n---@field test nil"},"old":{"module_name":"serde_luacats__ser","metadata":{},"snapshot":"---@class Test\n---@field int integer\n---@field text string"}} +{"run_id":"1757770547-700188086","line":386,"new":{"module_name":"serde_luacats__ser","snapshot_name":"test","metadata":{"source":"serde_luacats/src/ser.rs","assertion_line":386,"expression":"to_string(&test).unwrap()"},"snapshot":"---@class Test\n---@field int integer\n---@field text string\n---@field test bool"},"old":{"module_name":"serde_luacats__ser","metadata":{},"snapshot":"---@class Test\n---@field int integer\n---@field text string"}} +{"run_id":"1757770652-863230562","line":389,"new":{"module_name":"serde_luacats__ser","snapshot_name":"test","metadata":{"source":"serde_luacats/src/ser.rs","assertion_line":389,"expression":"to_string(&test).unwrap()"},"snapshot":"---@class Test\n---@field int integer\n---@field text string\n---@field test bool|nil"},"old":{"module_name":"serde_luacats__ser","metadata":{},"snapshot":"---@class Test\n---@field int integer\n---@field text string"}} diff --git a/serde_luacats/src/error.rs b/serde_luacats/src/error.rs new file mode 100644 index 0000000..604c5b9 --- /dev/null +++ b/serde_luacats/src/error.rs @@ -0,0 +1,26 @@ +use std::fmt::{self, Display}; + +use serde::ser; + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub enum Error { + Message(String), +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Self::Message(msg.to_string()) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Message(msg) => f.write_str(msg), + } + } +} + +impl std::error::Error for Error {} diff --git a/serde_luacats/src/lib.rs b/serde_luacats/src/lib.rs new file mode 100644 index 0000000..82343b7 --- /dev/null +++ b/serde_luacats/src/lib.rs @@ -0,0 +1,5 @@ +mod error; +mod ser; + +pub use error::{Error, Result}; +// pub use ser::{Serializer, to_string}; diff --git a/serde_luacats/src/ser.rs b/serde_luacats/src/ser.rs new file mode 100644 index 0000000..672995a --- /dev/null +++ b/serde_luacats/src/ser.rs @@ -0,0 +1,394 @@ +use serde::{Serialize, ser}; + +use crate::error::{Error, Result}; + +pub struct Serializer { + output: String, +} + +pub fn to_string(value: &T) -> Result { + let mut serializer = Serializer { + output: String::new(), + }; + + value.serialize(&mut serializer)?; + + Ok(serializer.output) +} + +impl<'a> ser::Serializer for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + type SerializeSeq = Self; + type SerializeTuple = Self; + type SerializeTupleStruct = Self; + type SerializeTupleVariant = Self; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Self; + + fn serialize_bool(self, v: bool) -> std::result::Result { + self.output += "bool"; + + Ok(()) + } + + fn serialize_i8(self, v: i8) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_i16(self, v: i16) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_i32(self, v: i32) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_i64(self, v: i64) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_u8(self, v: u8) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_u16(self, v: u16) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_u32(self, v: u32) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_u64(self, v: u64) -> std::result::Result { + self.output += "integer"; + + Ok(()) + } + + fn serialize_f32(self, v: f32) -> std::result::Result { + self.output += "number"; + + Ok(()) + } + + fn serialize_f64(self, v: f64) -> std::result::Result { + self.output += "number"; + + Ok(()) + } + + fn serialize_char(self, v: char) -> std::result::Result { + todo!() + } + + fn serialize_str(self, v: &str) -> std::result::Result { + self.output += "string"; + + Ok(()) + } + + fn serialize_bytes(self, v: &[u8]) -> std::result::Result { + todo!() + } + + fn serialize_none(self) -> std::result::Result { + self.output += "nil"; + + Ok(()) + } + + fn serialize_some(self, value: &T) -> std::result::Result + where + T: ?Sized + Serialize, + { + value.serialize(&mut *self)?; + self.output += "|nil"; + + Ok(()) + } + + fn serialize_unit(self) -> std::result::Result { + self.output += "nil"; + + Ok(()) + } + + fn serialize_unit_struct( + self, + name: &'static str, + ) -> std::result::Result { + self.serialize_unit() + } + + fn serialize_unit_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + ) -> std::result::Result { + todo!() + } + + fn serialize_newtype_struct( + self, + name: &'static str, + value: &T, + ) -> std::result::Result + where + T: ?Sized + Serialize, + { + todo!() + } + + fn serialize_newtype_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + value: &T, + ) -> std::result::Result + where + T: ?Sized + Serialize, + { + todo!() + } + + fn serialize_seq( + self, + len: Option, + ) -> std::result::Result { + todo!() + } + + fn serialize_tuple(self, len: usize) -> std::result::Result { + todo!() + } + + fn serialize_tuple_struct( + self, + name: &'static str, + len: usize, + ) -> std::result::Result { + todo!() + } + + fn serialize_tuple_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> std::result::Result { + todo!() + } + + fn serialize_map( + self, + len: Option, + ) -> std::result::Result { + todo!() + } + + fn serialize_struct( + self, + name: &'static str, + len: usize, + ) -> std::result::Result { + self.output += &format!("---@class {name}\n"); + + Ok(self) + } + + fn serialize_struct_variant( + self, + name: &'static str, + variant_index: u32, + variant: &'static str, + len: usize, + ) -> std::result::Result { + todo!() + } +} + +impl<'a> ser::SerializeSeq for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + todo!() + } + + fn end(self) -> std::result::Result { + todo!() + } +} + +impl<'a> ser::SerializeTuple for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + todo!() + } + + fn end(self) -> std::result::Result { + todo!() + } +} + +impl<'a> ser::SerializeTupleStruct for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + todo!() + } + + fn end(self) -> std::result::Result { + todo!() + } +} + +impl<'a> ser::SerializeTupleVariant for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + todo!() + } + + fn end(self) -> std::result::Result { + todo!() + } +} + +impl<'a> ser::SerializeMap for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + fn serialize_key(&mut self, key: &T) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + todo!() + } + + fn serialize_value(&mut self, value: &T) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + todo!() + } + + fn end(self) -> std::result::Result { + todo!() + } +} + +impl<'a> ser::SerializeStruct for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + self.output += &format!("---@field {key} "); + value.serialize(&mut **self)?; + self.output += "\n"; + + Ok(()) + } + + fn end(self) -> std::result::Result { + Ok(()) + } +} + +impl<'a> ser::SerializeStructVariant for &'a mut Serializer { + type Ok = (); + + type Error = Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> std::result::Result<(), Self::Error> + where + T: ?Sized + Serialize, + { + todo!() + } + + fn end(self) -> std::result::Result { + todo!() + } +} + +#[test] +fn test() { + #[derive(Serialize)] + struct Other { + cool: bool, + } + + #[derive(Serialize)] + struct Test { + int: u32, + text: String, + test: Option, // other: Other, + } + + let test = Test { + int: 1, + text: "Hello, World".into(), + test: Some(true), // other: Other { cool: true }, + }; + + insta::assert_snapshot!(to_string(&test).unwrap(), @r" + ---@class Test + ---@field int integer + ---@field text string + "); +}