Skip to content

Commit 988501d

Browse files
authored
Merge pull request #51 from llMBQll/protobuf-none-type
Allow `None` values in proto messages
2 parents 3384c28 + a660e8b commit 988501d

10 files changed

Lines changed: 505 additions & 102 deletions

File tree

config/scripts.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
local function volume()
22
local function display_device(widgets, offset, device, device_type)
3-
if device and device.Connected then
3+
if device then
44
table.insert(widgets, Widget.Text {
55
text = device.Name,
66
scrolling = true,

omni-led-api/proto/plugin.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ message EventResponse {}
1616

1717
message Field {
1818
oneof field {
19+
None f_none = 8;
1920
bool f_bool = 1;
2021
int64 f_integer = 2;
2122
double f_float = 3;
@@ -87,3 +88,5 @@ enum LogLevelFilter {
8788
LOG_LEVEL_FILTER_DEBUG = 5;
8889
LOG_LEVEL_FILTER_TRACE = 6;
8990
}
91+
92+
message None {}

omni-led-api/src/types.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ macro_rules! into_field {
8080
};
8181
}
8282

83+
// None
84+
into_field!(None, field::Field::FNone);
85+
8386
// Boolean values
8487
into_field!(bool, field::Field::FBool);
8588

omni-led-applications/audio/README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,18 @@ Audio application sends `AUDIO` events in two forms:
1616

1717
1. Full update for both devices on startup and on main input/output device change
1818
`AUDIO`: table
19-
- `Input`: table
20-
- `Connected`: bool
19+
- `Input`: table | none
2120
- `IsMuted`: bool
2221
- `Volume`: integer
2322
- `Name`: string
24-
- `Output`: table
25-
- `Connected`: bool
23+
- `Output`: table | none
2624
- `IsMuted`: bool
2725
- `Volume`: integer
2826
- `Name`: string
2927

30-
> `Connected` field tells if the device is actually connected or not. If `Connected` is `false` then rest of the data
31-
> for the relevant device type is filled with dummy values.
28+
> `Input` and `Output` fields are only sent if the devices are found. If the device is disconnected during the
29+
lifetime of the application the fields will be set with value `none` so that they are cleaned up in the scripting
30+
environment.
3231

3332
2. Partial update on main input/output device's volume change
3433
`AUDIO`: table

omni-led-applications/audio/src/main.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use audio::Audio;
22
use clap::Parser;
33
use log::debug;
44
use omni_led_api::plugin::Plugin;
5+
use omni_led_api::types::Table;
56
use omni_led_derive::IntoProto;
6-
use std::error::Error;
77
use tokio::runtime::Handle;
88
use tokio::sync::mpsc;
99
use tokio::sync::mpsc::{Receiver, Sender};
@@ -13,9 +13,9 @@ mod audio;
1313
const NAME: &str = "AUDIO";
1414

1515
#[tokio::main]
16-
async fn main() -> Result<(), Box<dyn Error>> {
16+
async fn main() {
1717
let options = Options::parse();
18-
let mut plugin = Plugin::new(NAME, &options.address).await?;
18+
let mut plugin = Plugin::new(NAME, &options.address).await.unwrap();
1919

2020
let (tx, mut rx): (
2121
Sender<(DeviceData, DeviceType)>,
@@ -33,21 +33,23 @@ async fn main() -> Result<(), Box<dyn Error>> {
3333
);
3434
}
3535

36-
let event = match device_type {
37-
DeviceType::Input => AudioEvent {
38-
input: Some(data),
39-
output: None,
40-
},
41-
DeviceType::Output => AudioEvent {
42-
input: None,
43-
output: Some(data),
44-
},
36+
let event_data = if data.connected {
37+
Some(EventData {
38+
is_muted: data.is_muted,
39+
volume: data.volume,
40+
name: data.name,
41+
})
42+
} else {
43+
None
44+
};
45+
46+
let event: Table = match device_type {
47+
DeviceType::Input => InputAudioEvent { input: event_data }.into(),
48+
DeviceType::Output => OutputAudioEvent { output: event_data }.into(),
4549
};
4650

4751
plugin.update(event.into()).await.unwrap();
4852
}
49-
50-
Ok(())
5153
}
5254

5355
#[derive(Copy, Clone, Debug)]
@@ -58,13 +60,26 @@ pub enum DeviceType {
5860

5961
#[derive(IntoProto)]
6062
#[proto(rename_all = PascalCase)]
61-
struct AudioEvent {
62-
input: Option<DeviceData>,
63-
output: Option<DeviceData>,
63+
struct InputAudioEvent {
64+
#[proto(strong_none)]
65+
input: Option<EventData>,
66+
}
67+
68+
#[derive(IntoProto)]
69+
#[proto(rename_all = PascalCase)]
70+
struct OutputAudioEvent {
71+
#[proto(strong_none)]
72+
output: Option<EventData>,
6473
}
6574

6675
#[derive(IntoProto)]
6776
#[proto(rename_all = PascalCase)]
77+
struct EventData {
78+
is_muted: bool,
79+
volume: i32,
80+
name: Option<String>,
81+
}
82+
6883
struct DeviceData {
6984
connected: bool,
7085
is_muted: bool,

omni-led-derive/src/into_proto.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use proc_macro2::TokenStream;
33
use quote::quote;
44
use syn::{Attribute, Data, DeriveInput};
55

6-
use crate::common::{get_attribute, is_option, parse_attributes};
6+
use crate::common::{get_attribute, get_attribute_with_default_value, is_option, parse_attributes};
77

88
pub fn expand_into_proto_derive(input: DeriveInput) -> proc_macro::TokenStream {
99
let name = input.ident;
@@ -47,10 +47,11 @@ fn generate_assignments(data: &Data, struct_attrs: &StructAttributes) -> TokenSt
4747
None => field_name,
4848
};
4949

50-
let is_option = is_option(&field.ty);
51-
5250
let attrs = get_field_attributes(&field.attrs);
5351

52+
let is_option = is_option(&field.ty);
53+
let propagate_none = attrs.strong_none.is_some();
54+
5455
let value_accessor = if is_option {
5556
quote! { value }
5657
} else {
@@ -65,17 +66,30 @@ fn generate_assignments(data: &Data, struct_attrs: &StructAttributes) -> TokenSt
6566
};
6667

6768
let insertion = quote! {
68-
table.items.insert(#renamed.to_string(), #transformed.into());
69+
table.items.insert(#renamed.to_string(), #transformed.into())
70+
};
71+
72+
let none_insertion = quote! {
73+
table.items.insert(#renamed.to_string(), omni_led_api::types::None{}.into())
6974
};
7075

7176
if is_option {
72-
quote! {
73-
if let Some(value) = self.#field_identifier {
74-
#insertion
77+
if propagate_none {
78+
quote! {
79+
match self.#field_identifier {
80+
Some(value) => #insertion,
81+
None => #none_insertion,
82+
};
83+
}
84+
} else {
85+
quote! {
86+
if let Some(value) = self.#field_identifier {
87+
#insertion;
88+
}
7589
}
7690
}
7791
} else {
78-
insertion
92+
quote! { #insertion; }
7993
}
8094
});
8195
quote! { #(#assignments)* }
@@ -99,13 +113,15 @@ fn get_struct_attributes(attributes: &Vec<Attribute>) -> StructAttributes {
99113
}
100114

101115
struct FieldAttributes {
116+
strong_none: Option<TokenStream>,
102117
transform: Option<TokenStream>,
103118
}
104119

105120
fn get_field_attributes(attributes: &Vec<Attribute>) -> FieldAttributes {
106121
let mut attributes = parse_attributes("proto", attributes);
107122

108123
FieldAttributes {
124+
strong_none: get_attribute_with_default_value(&mut attributes, "strong_none", quote! {}),
109125
transform: get_attribute(&mut attributes, "transform"),
110126
}
111127
}

omni-led-lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ libc = "0.2"
4444
windres = "0.2"
4545

4646
[dev-dependencies]
47+
omni-led-derive = { path = "../omni-led-derive", features = ["into-proto"] }
4748
test-case = "3.3.1"
4849

4950
[features]

omni-led-lib/src/common/common.rs

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
use mlua::{Lua, Table, Value, chunk};
2-
use omni_led_api::types::Field;
3-
use omni_led_api::types::field::Field as FieldEntry;
4-
use std::hash::{DefaultHasher, Hash, Hasher};
5-
6-
use crate::script_handler::script_data_types::ImageData;
72

83
#[macro_export]
94
macro_rules! create_table {
@@ -51,8 +46,6 @@ macro_rules! create_table_with_defaults {
5146
}};
5247
}
5348

54-
pub const KEY_VAL_TABLE: &str = "key-val-table";
55-
5649
pub fn load_internal_functions(lua: &Lua) {
5750
let dump = lua
5851
.create_function(|_, value: Value| {
@@ -90,53 +83,3 @@ pub fn load_internal_functions(lua: &Lua) {
9083
)
9184
.unwrap();
9285
}
93-
94-
pub fn proto_to_lua_value(lua: &Lua, field: Field) -> mlua::Result<Value> {
95-
match field.field {
96-
None => Ok(mlua::Nil),
97-
Some(FieldEntry::FBool(bool)) => Ok(Value::Boolean(bool)),
98-
Some(FieldEntry::FInteger(integer)) => Ok(Value::Integer(integer)),
99-
Some(FieldEntry::FFloat(float)) => Ok(Value::Number(float)),
100-
Some(FieldEntry::FString(string)) => {
101-
let string = lua.create_string(string)?;
102-
Ok(Value::String(string))
103-
}
104-
Some(FieldEntry::FArray(array)) => {
105-
let size = array.items.len();
106-
let table = lua.create_table_with_capacity(size, 0)?;
107-
for value in array.items {
108-
table.push(proto_to_lua_value(lua, value)?)?;
109-
}
110-
Ok(Value::Table(table))
111-
}
112-
Some(FieldEntry::FTable(map)) => {
113-
let size = map.items.len();
114-
let table = lua.create_table_with_capacity(0, size)?;
115-
for (key, value) in map.items {
116-
table.set(key, proto_to_lua_value(lua, value)?)?;
117-
}
118-
119-
let meta = lua.create_table_with_capacity(0, 1)?;
120-
meta.set(KEY_VAL_TABLE, true)?;
121-
_ = table.set_metatable(Some(meta));
122-
123-
Ok(Value::Table(table))
124-
}
125-
Some(FieldEntry::FImageData(image)) => {
126-
let hash = hash(&image.data);
127-
let image_data = ImageData {
128-
format: image.format().try_into().map_err(mlua::Error::external)?,
129-
bytes: image.data,
130-
hash: Some(hash),
131-
};
132-
let user_data = lua.create_any_userdata(image_data)?;
133-
Ok(Value::UserData(user_data))
134-
}
135-
}
136-
}
137-
138-
pub fn hash<T: Hash>(t: &T) -> u64 {
139-
let mut s = DefaultHasher::new();
140-
t.hash(&mut s);
141-
s.finish()
142-
}

0 commit comments

Comments
 (0)