This commit is contained in:
Mohd Mohsin 2025-07-07 10:21:48 +02:00 committed by GitHub
commit 585a2299db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 633 additions and 44 deletions

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ profile.json
flamegraph.svg
.idea/
.direnv
hierarchical_message_system_tree.txt

4
Cargo.lock generated
View file

@ -4766,9 +4766,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.94"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]

View file

@ -15,3 +15,4 @@ pub mod node_graph_executor;
#[cfg(test)]
pub mod test_utils;
pub mod utility_traits;
pub mod utility_types;

View file

@ -24,7 +24,7 @@ enum AnimationState {
},
}
#[derive(Default, Debug, Clone, PartialEq)]
#[derive(Default, Debug, Clone, PartialEq, ExtractField)]
pub struct AnimationMessageHandler {
/// Used to re-send the UI on the next frame after playback starts
live_preview_recently_zero: bool,
@ -57,6 +57,7 @@ impl AnimationMessageHandler {
}
}
#[message_handler_data]
impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -1,10 +1,11 @@
use crate::messages::prelude::*;
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct BroadcastMessageHandler {
listeners: HashMap<BroadcastEvent, Vec<Message>>,
}
#[message_handler_data]
impl MessageHandler<BroadcastMessage, ()> for BroadcastMessageHandler {
fn process_message(&mut self, message: BroadcastMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -1,11 +1,12 @@
use super::utility_types::MessageLoggingVerbosity;
use crate::messages::prelude::*;
#[derive(Debug, Default)]
#[derive(Debug, Default, ExtractField)]
pub struct DebugMessageHandler {
pub message_logging_verbosity: MessageLoggingVerbosity,
}
#[message_handler_data]
impl MessageHandler<DebugMessage, ()> for DebugMessageHandler {
fn process_message(&mut self, message: DebugMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -2,19 +2,21 @@ use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArt
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
#[derive(ExtractField)]
pub struct DialogMessageData<'a> {
pub portfolio: &'a PortfolioMessageHandler,
pub preferences: &'a PreferencesMessageHandler,
}
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
#[derive(Debug, Default, Clone)]
#[derive(Debug, Default, Clone, ExtractField)]
pub struct DialogMessageHandler {
export_dialog: ExportDialogMessageHandler,
new_document_dialog: NewDocumentDialogMessageHandler,
preferences_dialog: PreferencesDialogMessageHandler,
}
#[message_handler_data]
impl MessageHandler<DialogMessage, DialogMessageData<'_>> for DialogMessageHandler {
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, data: DialogMessageData) {
let DialogMessageData { portfolio, preferences } = data;

View file

@ -3,12 +3,13 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::prelude::*;
#[derive(ExtractField)]
pub struct ExportDialogMessageData<'a> {
pub portfolio: &'a PortfolioMessageHandler,
}
/// A dialog to allow users to customize their file export.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, ExtractField)]
pub struct ExportDialogMessageHandler {
pub file_type: FileType,
pub scale_factor: f64,
@ -31,6 +32,7 @@ impl Default for ExportDialogMessageHandler {
}
}
#[message_handler_data]
impl MessageHandler<ExportDialogMessage, ExportDialogMessageData<'_>> for ExportDialogMessageHandler {
fn process_message(&mut self, message: ExportDialogMessage, responses: &mut VecDeque<Message>, data: ExportDialogMessageData) {
let ExportDialogMessageData { portfolio } = data;

View file

@ -4,13 +4,14 @@ use glam::{IVec2, UVec2};
use graph_craft::document::NodeId;
/// A dialog to allow users to set some initial options about a new document.
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct NewDocumentDialogMessageHandler {
pub name: String,
pub infinite: bool,
pub dimensions: UVec2,
}
#[message_handler_data]
impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler {
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -4,14 +4,16 @@ use crate::messages::portfolio::document::utility_types::wires::GraphWireStyle;
use crate::messages::preferences::SelectionMode;
use crate::messages::prelude::*;
#[derive(ExtractField)]
pub struct PreferencesDialogMessageData<'a> {
pub preferences: &'a PreferencesMessageHandler,
}
/// A dialog to allow users to customize Graphite editor options
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct PreferencesDialogMessageHandler {}
#[message_handler_data]
impl MessageHandler<PreferencesDialogMessage, PreferencesDialogMessageData<'_>> for PreferencesDialogMessageHandler {
fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, data: PreferencesDialogMessageData) {
let PreferencesDialogMessageData { preferences } = data;

View file

@ -1,8 +1,9 @@
use crate::messages::prelude::*;
#[derive(Debug, Default)]
#[derive(Debug, Default, ExtractField)]
pub struct GlobalsMessageHandler {}
#[message_handler_data]
impl MessageHandler<GlobalsMessage, ()> for GlobalsMessageHandler {
fn process_message(&mut self, message: GlobalsMessage, _responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -6,16 +6,18 @@ use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*;
use std::fmt::Write;
#[derive(ExtractField)]
pub struct InputMapperMessageData<'a> {
pub input: &'a InputPreprocessorMessageHandler,
pub actions: ActionList,
}
#[derive(Debug, Default)]
#[derive(Debug, Default, ExtractField)]
pub struct InputMapperMessageHandler {
mapping: Mapping,
}
#[message_handler_data]
impl MessageHandler<InputMapperMessage, InputMapperMessageData<'_>> for InputMapperMessageHandler {
fn process_message(&mut self, message: InputMapperMessage, responses: &mut VecDeque<Message>, data: InputMapperMessageData) {
let InputMapperMessageData { input, actions } = data;

View file

@ -2,16 +2,18 @@ use crate::messages::input_mapper::input_mapper_message_handler::InputMapperMess
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::prelude::*;
#[derive(ExtractField)]
pub struct KeyMappingMessageData<'a> {
pub input: &'a InputPreprocessorMessageHandler,
pub actions: ActionList,
}
#[derive(Debug, Default)]
#[derive(Debug, Default, ExtractField)]
pub struct KeyMappingMessageHandler {
mapping_handler: InputMapperMessageHandler,
}
#[message_handler_data]
impl MessageHandler<KeyMappingMessage, KeyMappingMessageData<'_>> for KeyMappingMessageHandler {
fn process_message(&mut self, message: KeyMappingMessage, responses: &mut VecDeque<Message>, data: KeyMappingMessageData) {
let KeyMappingMessageData { input, actions } = data;

View file

@ -6,11 +6,12 @@ use crate::messages::prelude::*;
use glam::DVec2;
use std::time::Duration;
#[derive(ExtractField)]
pub struct InputPreprocessorMessageData {
pub keyboard_platform: KeyboardPlatformLayout,
}
#[derive(Debug, Default)]
#[derive(Debug, Default, ExtractField)]
pub struct InputPreprocessorMessageHandler {
pub frame_time: FrameTimeInfo,
pub time: u64,
@ -19,6 +20,7 @@ pub struct InputPreprocessorMessageHandler {
pub viewport_bounds: ViewportBounds,
}
#[message_handler_data]
impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for InputPreprocessorMessageHandler {
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, data: InputPreprocessorMessageData) {
let InputPreprocessorMessageData { keyboard_platform } = data;

View file

@ -6,7 +6,7 @@ use graphene_std::text::Font;
use graphene_std::vector::style::{FillChoice, GradientStops};
use serde_json::Value;
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct LayoutMessageHandler {
layouts: [Layout; LayoutTarget::LayoutTargetLength as usize],
}
@ -342,6 +342,11 @@ impl LayoutMessageHandler {
}
}
pub fn custom_data() -> MessageData {
MessageData::new(String::from("Function"), vec![(String::from("Fn(&MessageDiscriminant) -> Option<KeysGroup>"), 346)], file!())
}
#[message_handler_data(CustomData)]
impl<F: Fn(&MessageDiscriminant) -> Option<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler {
fn process_message(&mut self, message: LayoutMessage, responses: &mut std::collections::VecDeque<Message>, action_input_mapping: F) {
match message {

View file

@ -45,3 +45,86 @@ impl specta::Type for MessageDiscriminant {
specta::DataType::Any
}
}
#[cfg(test)]
mod test {
use super::*;
use std::io::Write;
#[test]
fn generate_message_tree() {
let result = Message::build_message_tree();
let mut file = std::fs::File::create("../hierarchical_message_system_tree.txt").unwrap();
file.write_all(format!("{} `{}`\n", result.name(), result.path()).as_bytes()).unwrap();
if let Some(variants) = result.variants() {
for (i, variant) in variants.iter().enumerate() {
let is_last = i == variants.len() - 1;
print_tree_node(variant, "", is_last, &mut file);
}
}
}
fn print_tree_node(tree: &DebugMessageTree, prefix: &str, is_last: bool, file: &mut std::fs::File) {
// Print the current node
let (branch, child_prefix) = if tree.has_message_handler_data_fields() || tree.has_message_handler_fields() {
("├── ", format!("{}", prefix))
} else {
if is_last {
("└── ", format!("{} ", prefix))
} else {
("├── ", format!("{}", prefix))
}
};
if tree.path().is_empty() {
file.write_all(format!("{}{}{}\n", prefix, branch, tree.name()).as_bytes()).unwrap();
} else {
file.write_all(format!("{}{}{} `{}`\n", prefix, branch, tree.name(), tree.path()).as_bytes()).unwrap();
}
// Print children if any
if let Some(variants) = tree.variants() {
let len = variants.len();
for (i, variant) in variants.iter().enumerate() {
let is_last_child = i == len - 1;
print_tree_node(variant, &child_prefix, is_last_child, file);
}
}
// Print handler field if any
if let Some(data) = tree.message_handler_fields() {
let len = data.fields().len();
let (branch, child_prefix) = if tree.has_message_handler_data_fields() {
("├── ", format!("{}", prefix))
} else {
("└── ", format!("{} ", prefix))
};
if data.path().is_empty() {
file.write_all(format!("{}{}{}\n", prefix, branch, data.name()).as_bytes()).unwrap();
} else {
file.write_all(format!("{}{}{} `{}`\n", prefix, branch, data.name(), data.path()).as_bytes()).unwrap();
}
for (i, field) in data.fields().iter().enumerate() {
let is_last_field = i == len - 1;
let branch = if is_last_field { "└── " } else { "├── " };
file.write_all(format!("{}{}{}\n", child_prefix, branch, field.0).as_bytes()).unwrap();
}
}
// Print data field if any
if let Some(data) = tree.message_handler_data_fields() {
let len = data.fields().len();
if data.path().is_empty() {
file.write_all(format!("{}{}{}\n", prefix, "└── ", data.name()).as_bytes()).unwrap();
} else {
file.write_all(format!("{}{}{} `{}`\n", prefix, "└── ", data.name(), data.path()).as_bytes()).unwrap();
}
for (i, field) in data.fields().iter().enumerate() {
let is_last_field = i == len - 1;
let branch = if is_last_field { "└── " } else { "├── " };
file.write_all(format!("{}{}{}\n", format!("{} ", prefix), branch, field.0).as_bytes()).unwrap();
}
}
}
}

View file

@ -38,6 +38,7 @@ use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::style::ViewMode;
use std::time::Duration;
#[derive(ExtractField)]
pub struct DocumentMessageData<'a> {
pub document_id: DocumentId,
pub ipp: &'a InputPreprocessorMessageHandler,
@ -48,7 +49,7 @@ pub struct DocumentMessageData<'a> {
pub device_pixel_ratio: f64,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ExtractField)]
#[serde(default)]
pub struct DocumentMessageHandler {
// ======================
@ -168,6 +169,7 @@ impl Default for DocumentMessageHandler {
}
}
#[message_handler_data]
impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessageHandler {
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, data: DocumentMessageData) {
let DocumentMessageData {

View file

@ -21,17 +21,19 @@ struct ArtboardInfo {
merge_node: NodeId,
}
#[derive(ExtractField)]
pub struct GraphOperationMessageData<'a> {
pub network_interface: &'a mut NodeNetworkInterface,
pub collapsed: &'a mut CollapsedLayers,
pub node_graph: &'a mut NodeGraphMessageHandler,
}
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize, ExtractField)]
pub struct GraphOperationMessageHandler {}
// GraphOperationMessageHandler always modified the document network. This is so changes to the layers panel will only affect the document network.
// For changes to the selected network, use NodeGraphMessageHandler. No NodeGraphMessage's should be added here, since they will affect the selected nested network.
#[message_handler_data]
impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for GraphOperationMessageHandler {
fn process_message(&mut self, message: GraphOperationMessage, responses: &mut VecDeque<Message>, data: GraphOperationMessageData) {
let network_interface = data.network_interface;

View file

@ -13,6 +13,7 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeId;
#[derive(ExtractField)]
pub struct NavigationMessageData<'a> {
pub network_interface: &'a mut NodeNetworkInterface,
pub breadcrumb_network_path: &'a [NodeId],
@ -23,7 +24,7 @@ pub struct NavigationMessageData<'a> {
pub preferences: &'a PreferencesMessageHandler,
}
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Default, ExtractField)]
pub struct NavigationMessageHandler {
navigation_operation: NavigationOperation,
mouse_position: ViewportPosition,
@ -31,6 +32,7 @@ pub struct NavigationMessageHandler {
abortable_pan_start: Option<f64>,
}
#[message_handler_data]
impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for NavigationMessageHandler {
fn process_message(&mut self, message: NavigationMessage, responses: &mut VecDeque<Message>, data: NavigationMessageData) {
let NavigationMessageData {

View file

@ -27,7 +27,7 @@ use graphene_std::*;
use renderer::Quad;
use std::cmp::Ordering;
#[derive(Debug)]
#[derive(Debug, ExtractField)]
pub struct NodeGraphHandlerData<'a> {
pub network_interface: &'a mut NodeNetworkInterface,
pub selection_network_path: &'a [NodeId],
@ -41,7 +41,7 @@ pub struct NodeGraphHandlerData<'a> {
pub preferences: &'a PreferencesMessageHandler,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, ExtractField)]
pub struct NodeGraphMessageHandler {
// TODO: Remove network and move to NodeNetworkInterface
pub network: Vec<NodeId>,
@ -92,6 +92,7 @@ pub struct NodeGraphMessageHandler {
}
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
#[message_handler_data]
impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGraphMessageHandler {
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, data: NodeGraphHandlerData<'a>) {
let NodeGraphHandlerData {

View file

@ -1,13 +1,14 @@
use super::utility_types::{OverlayProvider, OverlaysVisibilitySettings};
use crate::messages::prelude::*;
#[derive(ExtractField)]
pub struct OverlaysMessageData<'a> {
pub visibility_settings: OverlaysVisibilitySettings,
pub ipp: &'a InputPreprocessorMessageHandler,
pub device_pixel_ratio: f64,
}
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct OverlaysMessageHandler {
pub overlay_providers: HashSet<OverlayProvider>,
#[cfg(target_arch = "wasm32")]
@ -16,6 +17,7 @@ pub struct OverlaysMessageHandler {
context: Option<web_sys::CanvasRenderingContext2d>,
}
#[message_handler_data]
impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessageHandler {
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, data: OverlaysMessageData) {
let OverlaysMessageData { visibility_settings, ipp, .. } = data;

View file

@ -4,9 +4,10 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct PropertiesPanelMessageHandler {}
#[message_handler_data]
impl MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'_>)> for PropertiesPanelMessageHandler {
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) {
let PropertiesPanelMessageHandlerData {

View file

@ -6,7 +6,7 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
use crate::messages::prelude::*;
use graphene_std::path_bool::BooleanOperation;
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct MenuBarMessageHandler {
pub has_active_document: bool,
pub canvas_tilted: bool,
@ -21,6 +21,7 @@ pub struct MenuBarMessageHandler {
pub reset_node_definitions_on_open: bool,
}
#[message_handler_data]
impl MessageHandler<MenuBarMessage, ()> for MenuBarMessageHandler {
fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -25,6 +25,7 @@ use graphene_std::renderer::Quad;
use graphene_std::text::Font;
use std::vec;
#[derive(ExtractField)]
pub struct PortfolioMessageData<'a> {
pub ipp: &'a InputPreprocessorMessageHandler,
pub preferences: &'a PreferencesMessageHandler,
@ -35,7 +36,7 @@ pub struct PortfolioMessageData<'a> {
pub animation: &'a AnimationMessageHandler,
}
#[derive(Debug, Default)]
#[derive(Debug, Default, ExtractField)]
pub struct PortfolioMessageHandler {
menu_bar_message_handler: MenuBarMessageHandler,
pub documents: HashMap<DocumentId, DocumentMessageHandler>,
@ -52,6 +53,7 @@ pub struct PortfolioMessageHandler {
pub reset_node_definitions_on_open: bool,
}
#[message_handler_data]
impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, data: PortfolioMessageData) {
let PortfolioMessageData {

View file

@ -15,7 +15,7 @@ use std::any::Any;
use std::sync::Arc;
/// The spreadsheet UI allows for instance data to be previewed.
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, ExtractField)]
pub struct SpreadsheetMessageHandler {
/// Sets whether or not the spreadsheet is drawn.
pub spreadsheet_view_open: bool,
@ -25,6 +25,7 @@ pub struct SpreadsheetMessageHandler {
viewing_vector_data_domain: VectorDataDomain,
}
#[message_handler_data]
impl MessageHandler<SpreadsheetMessage, ()> for SpreadsheetMessageHandler {
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -5,7 +5,7 @@ use crate::messages::preferences::SelectionMode;
use crate::messages::prelude::*;
use graph_craft::wasm_application_io::EditorPreferences;
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, specta::Type, ExtractField)]
pub struct PreferencesMessageHandler {
pub selection_mode: SelectionMode,
pub zoom_with_scroll: bool,
@ -44,6 +44,7 @@ impl Default for PreferencesMessageHandler {
}
}
#[message_handler_data]
impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
fn process_message(&mut self, message: PreferencesMessage, responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -1,6 +1,6 @@
// Root
pub use crate::utility_traits::{ActionList, AsMessage, MessageHandler, ToDiscriminant, TransitiveChild};
pub use crate::utility_traits::{ActionList, AsMessage, HierarchicalTree, MessageHandler, ToDiscriminant, TransitiveChild};
pub use crate::utility_types::{DebugMessageTree, MessageData};
// Message, MessageData, MessageDiscriminant, MessageHandler
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};

View file

@ -12,6 +12,7 @@ use graphene_std::raster::color::Color;
const ARTBOARD_OVERLAY_PROVIDER: OverlayProvider = |context| DocumentMessage::DrawArtboardOverlays(context).into();
#[derive(ExtractField)]
pub struct ToolMessageData<'a> {
pub document_id: DocumentId,
pub document: &'a mut DocumentMessageHandler,
@ -21,7 +22,7 @@ pub struct ToolMessageData<'a> {
pub preferences: &'a PreferencesMessageHandler,
}
#[derive(Debug, Default)]
#[derive(Debug, Default, ExtractField)]
pub struct ToolMessageHandler {
pub tool_state: ToolFsmState,
pub transform_layer_handler: TransformLayerMessageHandler,
@ -29,6 +30,7 @@ pub struct ToolMessageHandler {
pub tool_is_active: bool,
}
#[message_handler_data]
impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, data: ToolMessageData) {
let ToolMessageData {

View file

@ -13,7 +13,7 @@ use crate::messages::tool::common_functionality::transformation_cage::*;
use graph_craft::document::NodeId;
use graphene_std::renderer::Quad;
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct ArtboardTool {
fsm_state: ArtboardToolFsmState,
data: ArtboardToolData,
@ -48,6 +48,7 @@ impl ToolMetadata for ArtboardTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ArtboardTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
self.fsm_state.process_event(message, &mut self.data, tool_data, &(), responses, false);

View file

@ -20,7 +20,7 @@ pub enum DrawMode {
Restore,
}
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct BrushTool {
fsm_state: BrushToolFsmState,
data: BrushToolData,
@ -185,6 +185,7 @@ impl LayoutHolder for BrushTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for BrushTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
let ToolMessage::Brush(BrushToolMessage::UpdateOptions(action)) = message else {

View file

@ -1,7 +1,7 @@
use super::tool_prelude::*;
use crate::messages::tool::utility_types::DocumentToolData;
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct EyedropperTool {
fsm_state: EyedropperToolFsmState,
data: EyedropperToolData,
@ -39,6 +39,7 @@ impl LayoutHolder for EyedropperTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for EyedropperTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
self.fsm_state.process_event(message, &mut self.data, tool_data, &(), responses, true);

View file

@ -3,7 +3,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
use graphene_std::vector::style::Fill;
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct FillTool {
fsm_state: FillToolFsmState,
}
@ -41,6 +41,7 @@ impl LayoutHolder for FillTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FillTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
self.fsm_state.process_event(message, &mut (), tool_data, &(), responses, true);

View file

@ -13,7 +13,7 @@ use graphene_std::Color;
use graphene_std::vector::VectorModificationType;
use graphene_std::vector::{PointId, SegmentId};
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct FreehandTool {
fsm_state: FreehandToolFsmState,
data: FreehandToolData,
@ -116,6 +116,7 @@ impl LayoutHolder for FreehandTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FreehandTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message else {

View file

@ -7,7 +7,7 @@ use crate::messages::tool::common_functionality::graph_modification_utils::{Node
use crate::messages::tool::common_functionality::snapping::SnapManager;
use graphene_std::vector::style::{Fill, Gradient, GradientType};
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct GradientTool {
fsm_state: GradientToolFsmState,
data: GradientToolData,
@ -53,6 +53,7 @@ impl ToolMetadata for GradientTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for GradientTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
let ToolMessage::Gradient(GradientToolMessage::UpdateOptions(action)) = message else {

View file

@ -1,6 +1,6 @@
use super::tool_prelude::*;
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct NavigateTool {
fsm_state: NavigateToolFsmState,
tool_data: NavigateToolData,
@ -38,6 +38,7 @@ impl LayoutHolder for NavigateTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for NavigateTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);

View file

@ -22,7 +22,7 @@ use graphene_std::vector::{HandleExt, HandleId, NoHashBuilder, SegmentId, Vector
use graphene_std::vector::{ManipulatorPointId, PointId, VectorModificationType};
use std::vec;
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct PathTool {
fsm_state: PathToolFsmState,
tool_data: PathToolData,
@ -275,6 +275,7 @@ impl LayoutHolder for PathTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
let updating_point = message == ToolMessage::Path(PathToolMessage::SelectedPointUpdated);

View file

@ -17,7 +17,7 @@ use graphene_std::Color;
use graphene_std::vector::{HandleId, ManipulatorPointId, NoHashBuilder, SegmentId, StrokeId, VectorData};
use graphene_std::vector::{PointId, VectorModificationType};
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct PenTool {
fsm_state: PenToolFsmState,
tool_data: PenToolData,
@ -186,6 +186,7 @@ impl LayoutHolder for PenTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
let ToolMessage::Pen(PenToolMessage::UpdateOptions(action)) = message else {

View file

@ -28,7 +28,7 @@ use graphene_std::renderer::Rect;
use graphene_std::transform::ReferencePoint;
use std::fmt;
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct SelectTool {
fsm_state: SelectToolFsmState,
tool_data: SelectToolData,
@ -242,6 +242,7 @@ impl LayoutHolder for SelectTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SelectTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
if let ToolMessage::Select(SelectToolMessage::SelectOptions(SelectOptionsUpdate::NestedSelectionBehavior(nested_selection_behavior))) = message {

View file

@ -14,7 +14,7 @@ use graph_craft::document::{NodeId, NodeInput};
use graphene_std::Color;
use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct SplineTool {
fsm_state: SplineToolFsmState,
tool_data: SplineToolData,
@ -123,6 +123,7 @@ impl LayoutHolder for SplineTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SplineTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message else {

View file

@ -21,7 +21,7 @@ use graphene_std::renderer::Quad;
use graphene_std::text::{Font, FontCache, TypesettingConfig, lines_clipping, load_font};
use graphene_std::vector::style::Fill;
#[derive(Default)]
#[derive(Default, ExtractField)]
pub struct TextTool {
fsm_state: TextToolFsmState,
tool_data: TextToolData,
@ -171,6 +171,7 @@ impl LayoutHolder for TextTool {
}
}
#[message_handler_data]
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for TextTool {
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
let ToolMessage::Text(TextToolMessage::UpdateOptions(action)) = message else {

View file

@ -20,7 +20,7 @@ const TRANSFORM_GRS_OVERLAY_PROVIDER: OverlayProvider = |context| TransformLayer
const SLOW_KEY: Key = Key::Shift;
const INCREMENTS_KEY: Key = Key::Control;
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct TransformLayerMessageHandler {
pub transform_operation: TransformOperation,
@ -134,6 +134,21 @@ fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &D
}
type TransformData<'a> = (&'a DocumentMessageHandler, &'a InputPreprocessorMessageHandler, &'a ToolData, &'a mut ShapeState);
pub fn custom_data() -> MessageData {
MessageData::new(
String::from("TransformData<'a>"),
vec![
(String::from("&'a DocumentMessageHandler"), 136),
(String::from("&'a InputPreprocessorMessageHandler"), 136),
(String::from("&'a ToolData"), 136),
(String::from("&'a mut ShapeState"), 136),
],
file!(),
)
}
#[message_handler_data(CustomData)]
impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayerMessageHandler {
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (document, input, tool_data, shape_editor): TransformData) {
let using_path_tool = tool_data.active_tool_type == ToolType::Path;

View file

@ -18,6 +18,7 @@ use graphene_std::text::FontCache;
use std::borrow::Cow;
use std::fmt::{self, Debug};
#[derive(ExtractField)]
pub struct ToolActionHandlerData<'a> {
pub document: &'a mut DocumentMessageHandler,
pub document_id: DocumentId,

View file

@ -1,10 +1,11 @@
use crate::messages::prelude::*;
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractField)]
pub struct WorkspaceMessageHandler {
node_graph_visible: bool,
}
#[message_handler_data]
impl MessageHandler<WorkspaceMessage, ()> for WorkspaceMessageHandler {
fn process_message(&mut self, message: WorkspaceMessage, _responses: &mut VecDeque<Message>, _data: ()) {
match message {

View file

@ -45,3 +45,19 @@ pub trait TransitiveChild: Into<Self::Parent> + Into<Self::TopParent> {
pub trait Hint {
fn hints(&self) -> HashMap<String, String>;
}
pub trait HierarchicalTree {
fn build_message_tree() -> DebugMessageTree;
fn message_handler_data_str() -> MessageData {
MessageData::new(String::new(), Vec::new(), "")
}
fn message_handler_str() -> MessageData {
MessageData::new(String::new(), Vec::new(), "")
}
fn path() -> &'static str {
""
}
}

View file

@ -0,0 +1,99 @@
#[derive(Debug)]
pub struct MessageData {
name: String,
fields: Vec<(String, usize)>,
path: &'static str,
}
impl MessageData {
pub fn new(name: String, fields: Vec<(String, usize)>, path: &'static str) -> MessageData {
MessageData { name, fields, path }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn fields(&self) -> &Vec<(String, usize)> {
&self.fields
}
pub fn path(&self) -> &'static str {
self.path
}
}
#[derive(Debug)]
pub struct DebugMessageTree {
name: String,
variants: Option<Vec<DebugMessageTree>>,
message_handler: Option<MessageData>,
message_handler_data: Option<MessageData>,
path: &'static str,
}
impl DebugMessageTree {
pub fn new(name: &str) -> DebugMessageTree {
DebugMessageTree {
name: name.to_string(),
variants: None,
message_handler: None,
message_handler_data: None,
path: "",
}
}
pub fn set_path(&mut self, path: &'static str) {
self.path = path;
}
pub fn add_variant(&mut self, variant: DebugMessageTree) {
if let Some(variants) = &mut self.variants {
variants.push(variant);
} else {
self.variants = Some(vec![variant]);
}
}
pub fn add_message_handler_data_field(&mut self, message_handler_data: MessageData) {
self.message_handler_data = Some(message_handler_data);
}
pub fn add_message_handler_field(&mut self, message_handler: MessageData) {
self.message_handler = Some(message_handler);
}
pub fn name(&self) -> &str {
&self.name
}
pub fn path(&self) -> &'static str {
self.path
}
pub fn variants(&self) -> Option<&Vec<DebugMessageTree>> {
self.variants.as_ref()
}
pub fn message_handler_data_fields(&self) -> Option<&MessageData> {
self.message_handler_data.as_ref()
}
pub fn message_handler_fields(&self) -> Option<&MessageData> {
self.message_handler.as_ref()
}
pub fn has_message_handler_data_fields(&self) -> bool {
match self.message_handler_data_fields() {
Some(_) => true,
None => false,
}
}
pub fn has_message_handler_fields(&self) -> bool {
match self.message_handler_fields() {
Some(_) => true,
None => false,
}
}
}

View file

@ -61,7 +61,7 @@ pub fn combined_message_attrs_impl(attr: TokenStream, input_item: TokenStream) -
<#parent as ToDiscriminant>::Discriminant
};
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, TransitiveChild)] });
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, TransitiveChild, HierarchicalTree)] });
input.attrs.push(syn::parse_quote! { #[parent(#parent, #parent::#variant)] });
if parent_is_top {
input.attrs.push(syn::parse_quote! { #[parent_is_top] });
@ -97,7 +97,7 @@ pub fn combined_message_attrs_impl(attr: TokenStream, input_item: TokenStream) -
fn top_level_impl(input_item: TokenStream) -> syn::Result<TokenStream> {
let mut input = syn::parse2::<ItemEnum>(input_item)?;
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant)] });
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, HierarchicalTree)] });
input.attrs.push(syn::parse_quote! { #[discriminant_attr(derive(Debug, Copy, Clone, PartialEq, Eq, Hash, AsMessage))] });
for var in &mut input.variants {

View file

@ -0,0 +1,57 @@
use crate::helpers::clean_rust_type_syntax;
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, format_ident, quote};
use syn::{Data, DeriveInput, Fields, Type, parse2};
pub fn derive_extract_field_impl(input: TokenStream) -> syn::Result<TokenStream> {
let input = parse2::<DeriveInput>(input)?;
let struct_name = &input.ident;
let generics = &input.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let fields = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => &fields.named,
_ => return Err(syn::Error::new(Span::call_site(), "ExtractField only works on structs with named fields")),
},
_ => return Err(syn::Error::new(Span::call_site(), "ExtractField only works on structs")),
};
let mut field_line = Vec::new();
// Extract field names and types as strings at compile time
let field_info = fields
.iter()
.map(|field| {
let ident = field.ident.as_ref().unwrap();
let name = ident.to_string();
let ty = clean_rust_type_syntax(field.ty.to_token_stream().to_string());
let line = ident.span().start().line;
field_line.push(line);
(name, ty)
})
.collect::<Vec<_>>();
let field_str = field_info.into_iter().map(|(name, ty)| (format!("{}: {}", name, ty)));
let res = quote! {
impl #impl_generics #struct_name #ty_generics #where_clause {
pub fn field_types() -> Vec<(String, usize)> {
vec![
#((String::from(#field_str), #field_line)),*
]
}
pub fn print_field_types() {
for (field, line) in Self::field_types() {
println!("{} at line {}", field, line);
}
}
pub fn path() -> &'static str {
file!()
}
}
};
Ok(res)
}

View file

@ -42,6 +42,58 @@ pub fn two_segment_path(left_ident: Ident, right_ident: Ident) -> Path {
Path { leading_colon: None, segments }
}
pub fn clean_rust_type_syntax(input: String) -> String {
let mut result = String::new();
let mut chars = input.chars().peekable();
while let Some(c) = chars.next() {
match c {
'&' => {
result.push('&');
while let Some(' ') = chars.peek() {
chars.next();
}
}
'<' => {
while let Some(' ') = result.chars().rev().next() {
result.pop();
}
result.push('<');
while let Some(' ') = chars.peek() {
chars.next();
}
}
'>' => {
while let Some(' ') = result.chars().rev().next() {
result.pop();
}
result.push('>');
while let Some(' ') = chars.peek() {
chars.next();
}
}
':' => {
if let Some(':') = chars.peek() {
while let Some(' ') = result.chars().rev().next() {
result.pop();
}
}
result.push(':');
chars.next();
result.push(':');
while let Some(' ') = chars.peek() {
chars.next();
}
}
_ => {
result.push(c);
}
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -0,0 +1,73 @@
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, quote};
use syn::{Data, DeriveInput, Fields, Type, parse2};
pub fn generate_hierarchical_tree(input: TokenStream) -> syn::Result<TokenStream> {
let input = parse2::<DeriveInput>(input)?;
let input_type = &input.ident;
let data = match &input.data {
Data::Enum(data) => data,
_ => return Err(syn::Error::new(Span::call_site(), "Tried to derive HierarchicalTree for non-enum")),
};
let build_message_tree = data.variants.iter().map(|variant| {
let variant_type = &variant.ident;
let has_child = variant
.attrs
.iter()
.any(|attr| attr.path().get_ident().is_some_and(|ident| ident == "sub_discriminant" || ident == "child"));
if has_child {
if let Fields::Unnamed(fields) = &variant.fields {
let field_type = &fields.unnamed.first().unwrap().ty;
quote! {
{
let mut variant_tree = DebugMessageTree::new(stringify!(#variant_type));
let field_name = stringify!(#field_type);
const message_string: &str = "Message";
if message_string == &field_name[field_name.len().saturating_sub(message_string.len())..] {
// The field is a Message type, recursively build its tree
let sub_tree = #field_type::build_message_tree();
variant_tree.add_variant(sub_tree);
}
message_tree.add_variant(variant_tree);
}
}
} else {
quote! {
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
}
}
} else {
quote! {
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
}
}
});
let res = quote! {
impl HierarchicalTree for #input_type {
fn build_message_tree() -> DebugMessageTree {
let mut message_tree = DebugMessageTree::new(stringify!(#input_type));
#(#build_message_tree)*
let message_handler_str = #input_type::message_handler_str();
if message_handler_str.fields().len() > 0 {
message_tree.add_message_handler_field(message_handler_str);
}
let message_handler_data_str = #input_type::message_handler_data_str();
if message_handler_data_str.fields().len() > 0 {
message_tree.add_message_handler_data_field(message_handler_data_str);
}
message_tree.set_path(file!());
message_tree
}
}
};
Ok(res)
}

View file

@ -3,17 +3,23 @@
mod as_message;
mod combined_message_attrs;
mod discriminant;
mod extract_fields;
mod helper_structs;
mod helpers;
mod hierarchical_tree;
mod hint;
mod message_handler_data_attr;
mod transitive_child;
mod widget_builder;
use crate::as_message::derive_as_message_impl;
use crate::combined_message_attrs::combined_message_attrs_impl;
use crate::discriminant::derive_discriminant_impl;
use crate::extract_fields::derive_extract_field_impl;
use crate::helper_structs::AttrInnerSingleString;
use crate::hierarchical_tree::generate_hierarchical_tree;
use crate::hint::derive_hint_impl;
use crate::message_handler_data_attr::message_handler_data_attr_impl;
use crate::transitive_child::derive_transitive_child_impl;
use crate::widget_builder::derive_widget_builder_impl;
use proc_macro::TokenStream;
@ -281,6 +287,21 @@ pub fn derive_widget_builder(input_item: TokenStream) -> TokenStream {
TokenStream::from(derive_widget_builder_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
}
#[proc_macro_derive(HierarchicalTree)]
pub fn derive_hierarchical_tree(input_item: TokenStream) -> TokenStream {
TokenStream::from(generate_hierarchical_tree(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
}
#[proc_macro_derive(ExtractField)]
pub fn derive_extract_field(input_item: TokenStream) -> TokenStream {
TokenStream::from(derive_extract_field_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
}
#[proc_macro_attribute]
pub fn message_handler_data(attr: TokenStream, input_item: TokenStream) -> TokenStream {
TokenStream::from(message_handler_data_attr_impl(attr.into(), input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -0,0 +1,118 @@
use crate::helpers::{call_site_ident, clean_rust_type_syntax};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, quote};
use syn::{ItemImpl, Type, parse2, spanned::Spanned};
pub fn message_handler_data_attr_impl(attr: TokenStream, input_item: TokenStream) -> syn::Result<TokenStream> {
// Parse the input as an impl block
let impl_block = parse2::<ItemImpl>(input_item.clone())?;
let self_ty = &impl_block.self_ty;
let path = match &**self_ty {
Type::Path(path) => &path.path,
_ => return Err(syn::Error::new(Span::call_site(), "Expected impl implementation")),
};
let input_type = path.segments.last().map(|s| &s.ident).unwrap();
// Extract the message type from the trait path
let trait_path = match &impl_block.trait_ {
Some((_, path, _)) => path,
None => return Err(syn::Error::new(Span::call_site(), "Expected trait implementation")),
};
// Get the trait generics (should be MessageHandler<M, D>)
if let Some(segment) = trait_path.segments.last() {
if segment.ident != "MessageHandler" {
return Err(syn::Error::new(segment.ident.span(), "Expected MessageHandler trait"));
}
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
if args.args.len() >= 2 {
// Extract the message type (M) and data type (D) from the trait params
let message_type = &args.args[0];
let data_type = &args.args[1];
// Check if the attribute is "CustomData"
let is_custom_data = attr.to_string().contains("CustomData");
let impl_item = match data_type {
syn::GenericArgument::Type(t) => {
match t {
syn::Type::Path(type_path) if !type_path.path.segments.is_empty() => {
// Get just the base identifier (ToolMessageData) without generics
let type_name = &type_path.path.segments.first().unwrap().ident;
if is_custom_data {
quote! {
#input_item
impl #message_type {
pub fn message_handler_data_str() -> MessageData {
custom_data()
}
pub fn message_handler_str() -> MessageData {
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
}
}
}
} else {
quote! {
#input_item
impl #message_type {
pub fn message_handler_data_str() -> MessageData
{
MessageData::new(format!("{}",stringify!(#type_name)), #type_name::field_types(), #type_name::path())
}
pub fn message_handler_str() -> MessageData {
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
}
}
}
}
}
syn::Type::Tuple(_) => quote! {
#input_item
impl #message_type {
pub fn message_handler_str() -> MessageData {
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
}
}
},
syn::Type::Reference(type_reference) => {
let message_type = call_site_ident(format!("{input_type}Message"));
let type_ident = match &*type_reference.elem {
syn::Type::Path(type_path) => &type_path.path.segments.first().unwrap().ident,
_ => return Err(syn::Error::new(type_reference.elem.span(), "Expected type path")),
};
let tr = clean_rust_type_syntax(type_reference.to_token_stream().to_string());
quote! {
#input_item
impl #message_type {
pub fn message_handler_data_str() -> MessageData {
MessageData::new(format!("{}", #tr),#type_ident::field_types(), #type_ident::path())
}
pub fn message_handler_str() -> MessageData {
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
}
}
}
}
_ => return Err(syn::Error::new(t.span(), "Unsupported type format")),
}
}
_ => quote! {
#input_item
},
};
return Ok(impl_item);
}
}
}
Ok(input_item)
}