Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attributes validation #3

Merged
merged 9 commits into from
Oct 10, 2023
22 changes: 0 additions & 22 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions-rs/cargo@v1
with:
command: check
Expand All @@ -29,11 +24,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- run: sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev
- uses: actions-rs/cargo@v1
with:
Expand All @@ -45,12 +35,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
Expand All @@ -61,12 +45,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: clippy
- uses: actions-rs/cargo@v1
with:
command: clippy
Expand Down
1 change: 1 addition & 0 deletions pubsubman/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod messages_view;
mod modal;
mod publish_view;
mod topic_name;
mod validity_frame;

pub use messages_view::MessagesView;
pub use modal::Modal;
Expand Down
96 changes: 96 additions & 0 deletions pubsubman/src/ui/publish_view/attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use std::collections::HashMap;

use crate::ui::validity_frame::ValidityFrame;

#[derive(Default, Hash)]
pub struct Attributes(Vec<(String, String)>);

impl Attributes {
fn validator(&self) -> AttributesValidator {
let mut key_count_map = HashMap::new();

for (key, _) in self.0.iter() {
*key_count_map.entry(key.clone()).or_insert_with(|| 0) += 1;
}

AttributesValidator(key_count_map)
}

pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn push(&mut self, attr: (String, String)) {
self.0.push(attr);
}

pub fn show(&mut self, ui: &mut egui::Ui, is_key_valid: impl Fn(&str) -> bool) {
let mut attr_idx_to_delete = None;

for (idx, (key, val)) in self.0.iter_mut().enumerate() {
let is_valid = is_key_valid(key);

ui.validity_frame(is_valid).show(ui, |ui| {
ui.add(
egui::TextEdit::singleline(key)
.desired_width(100.0)
.code_editor()
.hint_text("Key"),
);
});

ui.add(
egui::TextEdit::singleline(val)
.desired_width(100.0)
.code_editor()
.hint_text("Value"),
);

if ui.button("🗑").clicked() {
attr_idx_to_delete = Some(idx);
}

ui.end_row();
}

if let Some(i) = attr_idx_to_delete {
self.0.remove(i);
}
}
}

impl From<&Attributes> for HashMap<String, String> {
fn from(value: &Attributes) -> Self {
HashMap::from_iter(value.0.clone())
}
}

#[derive(Default, Clone)]
pub struct AttributesValidator(HashMap<String, usize>);

impl AttributesValidator {
pub fn is_valid(&self) -> bool {
self.0.iter().all(|(_, count)| *count < 2)
}

pub fn is_key_valid(&self, key: &str) -> bool {
self.0.get(key).is_some_and(|count| *count < 2)
}
}

pub fn attributes_validator(ctx: &egui::Context, attributes: &Attributes) -> AttributesValidator {
impl egui::util::cache::ComputerMut<&Attributes, AttributesValidator> for AttributesValidator {
fn compute(&mut self, attributes: &Attributes) -> AttributesValidator {
attributes.validator()
}
}

type AttributesKeyCounterCache =
egui::util::cache::FrameCache<AttributesValidator, AttributesValidator>;

ctx.memory_mut(|mem| {
mem.caches
.cache::<AttributesKeyCounterCache>()
.get(attributes)
})
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::collections::HashMap;

use pubsubman_backend::{
message::FrontendMessage,
model::{PubsubMessageToPublish, TopicName},
Expand All @@ -8,10 +6,14 @@ use tokio::sync::mpsc::Sender;

use crate::actions::publish_message;

use self::attributes::{attributes_validator, Attributes};

mod attributes;

#[derive(Default)]
pub struct PublishView {
data: String,
attributes: Vec<(String, String)>,
attributes: Attributes,
}

impl PublishView {
Expand All @@ -35,46 +37,29 @@ impl PublishView {
);
});

egui::CollapsingHeader::new("Attributes")
let mut header_text = egui::RichText::new("Attributes");
let attributes_validator = attributes_validator(ui.ctx(), &self.attributes);
let all_attributes_valid = attributes_validator.is_valid();

if !all_attributes_valid {
header_text = header_text.color(ui.visuals().error_fg_color);
};

egui::CollapsingHeader::new(header_text)
.id_source(format!("{}-attributes", selected_topic.0))
.default_open(false)
.show(ui, |ui| {
let mut attr_idx_to_delete = None;

if !self.attributes.is_empty() {
egui::Grid::new(format!("{}-attributes-form", selected_topic.0))
.min_col_width(100.0)
.num_columns(3)
.spacing((0.0, 4.0))
.show(ui, |ui| {
for (idx, (id, val)) in self.attributes.iter_mut().enumerate() {
ui.add(
egui::TextEdit::singleline(id)
.desired_width(100.0)
.code_editor()
.hint_text("Key"),
);

ui.add(
egui::TextEdit::singleline(val)
.desired_width(100.0)
.code_editor()
.hint_text("Value"),
);

if ui.button("🗑").clicked() {
attr_idx_to_delete = Some(idx);
}

ui.end_row();
}
self.attributes
.show(ui, |key| attributes_validator.is_key_valid(key));
});
}

if let Some(i) = attr_idx_to_delete {
self.attributes.remove(i);
}

if !self.attributes.is_empty() {
ui.add_space(4.0);
}
Expand All @@ -86,14 +71,17 @@ impl PublishView {

ui.add_space(8.0);

if ui.button("Publish").clicked() {
if ui
.add_enabled(all_attributes_valid, egui::Button::new("Publish"))
.clicked()
{
publish_message(front_tx, selected_topic, self.into())
}
}
}

impl From<&mut PublishView> for PubsubMessageToPublish {
fn from(val: &mut PublishView) -> Self {
Self::new(val.data.clone(), HashMap::from_iter(val.attributes.clone()))
Self::new(val.data.clone(), (&val.attributes).into())
}
}
24 changes: 24 additions & 0 deletions pubsubman/src/ui/validity_frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pub trait ValidityFrame {
fn validity_frame(&self, is_valid: bool) -> egui::Frame;
}

impl ValidityFrame for &mut egui::Ui {
fn validity_frame(&self, is_valid: bool) -> egui::Frame {
let (stroke, rounding) = if is_valid {
(egui::Stroke::NONE, egui::Rounding::ZERO)
} else {
(
egui::Stroke {
width: 1.0,
color: self.visuals().error_fg_color,
},
self.visuals().widgets.hovered.rounding,
)
};

egui::Frame::none()
.stroke(stroke)
.inner_margin(2.0)
.rounding(rounding)
}
}
Loading