From f834a8e39461f5af5baecd3b42f3f687b9847ed3 Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Sun, 31 Oct 2021 16:39:56 -0700 Subject: [PATCH 1/6] Move nightsketch sketches to their own directory in crate --- sketch/src/lib.rs | 5 ++--- sketch/src/{ => sketches}/blossom.rs | 1 + sketch/src/sketches/mod.rs | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) rename sketch/src/{ => sketches}/blossom.rs (99%) create mode 100644 sketch/src/sketches/mod.rs diff --git a/sketch/src/lib.rs b/sketch/src/lib.rs index db6306b..5125289 100644 --- a/sketch/src/lib.rs +++ b/sketch/src/lib.rs @@ -1,13 +1,12 @@ pub(crate) use nightgraphics::prelude::*; -pub(crate) use nightsketch_derive::sketch; mod sketch; pub use sketch::*; mod metadata; pub use metadata::*; -mod blossom; -use blossom::*; +mod sketches; +use sketches::*; #[cfg_attr(feature = "cli", derive(clap::Parser))] #[cfg_attr( diff --git a/sketch/src/blossom.rs b/sketch/src/sketches/blossom.rs similarity index 99% rename from sketch/src/blossom.rs rename to sketch/src/sketches/blossom.rs index 13f3665..9227b2f 100644 --- a/sketch/src/blossom.rs +++ b/sketch/src/sketches/blossom.rs @@ -1,4 +1,5 @@ use super::*; +use crate::*; use std::f64::consts::{PI, TAU}; /// A series of lightly complex sine modulated rings around the center of the diff --git a/sketch/src/sketches/mod.rs b/sketch/src/sketches/mod.rs new file mode 100644 index 0000000..924676f --- /dev/null +++ b/sketch/src/sketches/mod.rs @@ -0,0 +1,4 @@ +mod blossom; + +pub use blossom::Blossom; +pub(self) use nightsketch_derive::sketch; From 6230ced4c71bc8a3071052fd71da55d0c24a6358 Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Mon, 1 Nov 2021 13:28:18 -0700 Subject: [PATCH 2/6] Split CLI sketchlist from GUI one in prep for macros --- cli/src/main.rs | 4 +-- sketch/src/lib.rs | 50 +----------------------------------- sketch/src/sketches/mod.rs | 30 ++++++++++++++++++++++ ui/src/app/drawing.rs | 2 +- ui/src/app/sketch_control.rs | 9 ++++--- 5 files changed, 39 insertions(+), 56 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 3bb2dd8..d85f592 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,13 +1,13 @@ use clap::{crate_authors, crate_description, crate_version, Parser}; use nightgraphics::render::SvgRenderer; -use nightsketch::SketchList; +use nightsketch::SketchSubcommand; use serde::{Deserialize, Serialize}; #[derive(clap::Parser, Serialize, Deserialize)] #[clap(about= crate_description!(), version = crate_version!(), author = crate_authors!())] struct Opts { #[clap(subcommand)] - sketch: SketchList, + sketch: SketchSubcommand, } fn main() { diff --git a/sketch/src/lib.rs b/sketch/src/lib.rs index 5125289..28f798e 100644 --- a/sketch/src/lib.rs +++ b/sketch/src/lib.rs @@ -4,53 +4,5 @@ mod sketch; pub use sketch::*; mod metadata; pub use metadata::*; - mod sketches; -use sketches::*; - -#[cfg_attr(feature = "cli", derive(clap::Parser))] -#[cfg_attr( - feature = "serde_support", - derive(serde::Deserialize, serde::Serialize) -)] -pub enum SketchList { - Blossom(Blossom), -} - -impl Default for SketchList { - fn default() -> Self { - Self::Blossom(Blossom::default()) - } -} - -impl SketchList { - fn inner_sketch(&self) -> &dyn Sketch { - match self { - Self::Blossom(s) => s as &dyn Sketch, - } - } - fn inner_sketch_mut(&mut self) -> &mut dyn Sketch { - match self { - Self::Blossom(s) => s as &mut dyn Sketch, - } - } - pub fn exec(&self) -> SketchResult { - self.inner_sketch().exec() - } - pub fn param_metadata(&self) -> Vec { - self.inner_sketch().param_metadata() - } - pub fn mut_float_by_id(&mut self, id: u64) -> SketchResult<&mut f64> { - self.inner_sketch_mut().mut_float_by_id(id) - } - pub fn mut_int_by_id(&mut self, id: u64) -> SketchResult<&mut i64> { - self.inner_sketch_mut().mut_int_by_id(id) - } - pub fn mut_uint_by_id(&mut self, id: u64) -> SketchResult<&mut u64> { - self.inner_sketch_mut().mut_uint_by_id(id) - } - - pub fn mut_bool_by_id(&mut self, id: u64) -> SketchResult<&mut bool> { - self.inner_sketch_mut().mut_bool_by_id(id) - } -} +pub use sketches::*; diff --git a/sketch/src/sketches/mod.rs b/sketch/src/sketches/mod.rs index 924676f..0404ccc 100644 --- a/sketch/src/sketches/mod.rs +++ b/sketch/src/sketches/mod.rs @@ -1,4 +1,34 @@ mod blossom; +use super::*; pub use blossom::Blossom; pub(self) use nightsketch_derive::sketch; + +#[cfg_attr(feature = "cli", derive(clap::Parser))] +#[cfg_attr( + feature = "serde_support", + derive(serde::Deserialize, serde::Serialize) +)] +pub enum SketchSubcommand { + Blossom(Blossom), +} + +impl Default for SketchSubcommand { + fn default() -> Self { + Self::Blossom(Blossom::default()) + } +} + +pub struct SketchList {} + +impl SketchList { + pub fn default_sketch() -> Box { + Box::new(Blossom::default()) + } + pub fn sketch_by_name(name: &str) -> SketchResult> { + match name { + "blossom" => Ok(Box::new(Blossom::default())), + _ => Err(SketchError::Todo("sdfs".to_string())), + } + } +} diff --git a/ui/src/app/drawing.rs b/ui/src/app/drawing.rs index cf1b0b9..215fd4c 100644 --- a/ui/src/app/drawing.rs +++ b/ui/src/app/drawing.rs @@ -30,7 +30,7 @@ pub struct SketchData { impl Default for Drawing { fn default() -> Self { - let (sketch_size, shapes) = SketchList::default().exec().unwrap().render_egui(); + let (sketch_size, shapes) = SketchList::default_sketch().exec().unwrap().render_egui(); let sketch_rect = egui::Rect::from_min_size(Pos2::ZERO, sketch_size); Self { shapes, diff --git a/ui/src/app/sketch_control.rs b/ui/src/app/sketch_control.rs index d0aaaa9..a7b5ad5 100644 --- a/ui/src/app/sketch_control.rs +++ b/ui/src/app/sketch_control.rs @@ -3,17 +3,18 @@ use nightgraphics::render::EguiRenderer; use nightsketch::*; pub struct SketchControl { - sketch: SketchList, + sketch: Box, params: Vec, pub needs_render: bool, } impl Default for SketchControl { fn default() -> Self { - let sketch = SketchList::default(); + let sketch = SketchList::default_sketch(); + let params = sketch.param_metadata(); SketchControl { - sketch: SketchList::default(), - params: sketch.param_metadata(), + sketch, + params, needs_render: true, } } From de964f2d2002a214d606f979087a1c49c67f37ed Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Tue, 2 Nov 2021 10:01:53 -0700 Subject: [PATCH 3/6] Add clap/cli compatible `sketchlist!` sketch loading macro --- sketch/src/sketches/blossom.rs | 1 - sketch/src/sketches/mod.rs | 28 ++----- sketch_derive/src/codegen/mod.rs | 4 + .../src/{codegen.rs => codegen/sketch.rs} | 0 sketch_derive/src/codegen/sketchlist.rs | 75 +++++++++++++++++++ sketch_derive/src/lib.rs | 8 ++ sketch_derive/src/parse/mod.rs | 3 + sketch_derive/src/parse/sketchlist.rs | 37 +++++++++ 8 files changed, 134 insertions(+), 22 deletions(-) create mode 100644 sketch_derive/src/codegen/mod.rs rename sketch_derive/src/{codegen.rs => codegen/sketch.rs} (100%) create mode 100644 sketch_derive/src/codegen/sketchlist.rs create mode 100644 sketch_derive/src/parse/sketchlist.rs diff --git a/sketch/src/sketches/blossom.rs b/sketch/src/sketches/blossom.rs index 9227b2f..13f3665 100644 --- a/sketch/src/sketches/blossom.rs +++ b/sketch/src/sketches/blossom.rs @@ -1,5 +1,4 @@ use super::*; -use crate::*; use std::f64::consts::{PI, TAU}; /// A series of lightly complex sine modulated rings around the center of the diff --git a/sketch/src/sketches/mod.rs b/sketch/src/sketches/mod.rs index 0404ccc..c813594 100644 --- a/sketch/src/sketches/mod.rs +++ b/sketch/src/sketches/mod.rs @@ -1,34 +1,20 @@ -mod blossom; - use super::*; -pub use blossom::Blossom; -pub(self) use nightsketch_derive::sketch; - -#[cfg_attr(feature = "cli", derive(clap::Parser))] -#[cfg_attr( - feature = "serde_support", - derive(serde::Deserialize, serde::Serialize) -)] -pub enum SketchSubcommand { - Blossom(Blossom), -} - -impl Default for SketchSubcommand { - fn default() -> Self { - Self::Blossom(Blossom::default()) - } -} +pub(self) use nightsketch_derive::{sketch, sketchlist}; pub struct SketchList {} impl SketchList { pub fn default_sketch() -> Box { - Box::new(Blossom::default()) + Box::new(blossom::Blossom::default()) } pub fn sketch_by_name(name: &str) -> SketchResult> { match name { - "blossom" => Ok(Box::new(Blossom::default())), + "blossom" => Ok(Box::new(blossom::Blossom::default())), _ => Err(SketchError::Todo("sdfs".to_string())), } } } + +sketchlist! { + blossom::Blossom, +} diff --git a/sketch_derive/src/codegen/mod.rs b/sketch_derive/src/codegen/mod.rs new file mode 100644 index 0000000..b7e96b2 --- /dev/null +++ b/sketch_derive/src/codegen/mod.rs @@ -0,0 +1,4 @@ +mod sketch; +mod sketchlist; +pub use sketch::*; +pub use sketchlist::*; diff --git a/sketch_derive/src/codegen.rs b/sketch_derive/src/codegen/sketch.rs similarity index 100% rename from sketch_derive/src/codegen.rs rename to sketch_derive/src/codegen/sketch.rs diff --git a/sketch_derive/src/codegen/sketchlist.rs b/sketch_derive/src/codegen/sketchlist.rs new file mode 100644 index 0000000..74c279b --- /dev/null +++ b/sketch_derive/src/codegen/sketchlist.rs @@ -0,0 +1,75 @@ +use crate::parse::SketchListEntry; +use quote::quote; + +pub fn sketch_subcommand_enum_tokens(sketches: &[SketchListEntry]) -> proc_macro2::TokenStream { + // For each sketch, create an enum variant with its name and the struct + // associated with it. For example, if module `doop` has sketch `Doop`, + // a variant `Doop(Doop)` is created. + let sketch_enum_entries: Vec = sketches + .iter() + .map(|sketch| { + let s = &sketch.sketch; + quote!(#s(#s)) + }) + .collect(); + + let exec_match_arms: Vec = sketches + .iter() + .map(|sketch| { + let s = &sketch.sketch; + quote!(Self::#s(s) => s.exec()) + }) + .collect(); + + let serde_derive_attr = if cfg!(feature = "serde_support") { + Some(quote!(#[derive(serde::Deserialize, serde::Serialize)])) + } else { + None + }; + + let sketch_subcommand_tokens = if cfg!(feature = "cli") { + Some(quote! { + #serde_derive_attr + #[derive(clap::Parser)] + pub enum SketchSubcommand { + #(#sketch_enum_entries),* + } + + impl SketchSubcommand { + pub fn exec(&self) -> SketchResult { + match self { + #(#exec_match_arms),*, + _ => Err(SketchError::Todo("TODO".to_string())), + } + } + } + }) + } else { + None + }; + + quote!( #sketch_subcommand_tokens ) +} + +pub fn sketch_mod_use_tokens(sketches: &[SketchListEntry]) -> proc_macro2::TokenStream { + let mod_stmts: Vec = sketches + .iter() + .map(|sketch| { + let m = &sketch.module; + quote!(mod #m) + }) + .collect(); + let use_stmts: Vec = sketches + .iter() + .map(|sketch| { + let m = &sketch.module; + let s = &sketch.sketch; + quote!(pub use #m::#s) + }) + .collect(); + + quote!( + #(#mod_stmts);*; + #(#use_stmts);*; + ) +} diff --git a/sketch_derive/src/lib.rs b/sketch_derive/src/lib.rs index e02aff9..bffa3bc 100644 --- a/sketch_derive/src/lib.rs +++ b/sketch_derive/src/lib.rs @@ -26,3 +26,11 @@ pub fn sketch(_attr: TokenStream, input: TokenStream) -> TokenStream { ) .into() } + +#[proc_macro] +pub fn sketchlist(input: TokenStream) -> TokenStream { + let sketches: SketchList = parse_macro_input!(input); + let sketch_mod_use_stmts = sketch_mod_use_tokens(&sketches.sketches); + let sketch_subcommand_enum = sketch_subcommand_enum_tokens(&sketches.sketches); + quote!(#sketch_mod_use_stmts #sketch_subcommand_enum).into() +} diff --git a/sketch_derive/src/parse/mod.rs b/sketch_derive/src/parse/mod.rs index 926c25d..8c3dd9b 100644 --- a/sketch_derive/src/parse/mod.rs +++ b/sketch_derive/src/parse/mod.rs @@ -8,6 +8,9 @@ pub use param::SketchParam; mod param_attr; pub use param_attr::{ParamAttr, ParamAttrs}; +mod sketchlist; +pub use sketchlist::{SketchList, SketchListEntry}; + mod utils; /* /// Doc string diff --git a/sketch_derive/src/parse/sketchlist.rs b/sketch_derive/src/parse/sketchlist.rs new file mode 100644 index 0000000..830c339 --- /dev/null +++ b/sketch_derive/src/parse/sketchlist.rs @@ -0,0 +1,37 @@ +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::{Ident, Path, Result, Token}; + +#[derive(Debug)] +pub struct SketchListEntry { + pub module: Punctuated, + pub sketch: Ident, +} + +impl Parse for SketchListEntry { + fn parse(input: ParseStream) -> Result { + let path: syn::Path = input.call(Path::parse_mod_style)?; + let mut seg_vec: Vec = path.segments.into_iter().collect(); + let sketch = seg_vec.pop().unwrap().ident; + let mut module = Punctuated::new(); + for seg in seg_vec { + module.push(seg); + } + Ok(SketchListEntry { module, sketch }) + } +} + +#[derive(Debug)] +pub struct SketchList { + pub sketches: Vec, +} + +impl Parse for SketchList { + fn parse(input: ParseStream) -> Result { + let sketches: Punctuated = + input.parse_terminated(SketchListEntry::parse)?; + let sketches: Vec = sketches.into_iter().collect(); + + Ok(SketchList { sketches }) + } +} From 11291b12c91c5939fddf11a81229087e579c96d5 Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Tue, 2 Nov 2021 16:42:19 -0700 Subject: [PATCH 4/6] Add GUI/arbitrary sketch listing/accessing macro --- sketch/src/sketches/mod.rs | 14 --------- sketch_derive/src/codegen/sketchlist.rs | 38 +++++++++++++++++++------ sketch_derive/src/lib.rs | 21 ++++++++++---- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/sketch/src/sketches/mod.rs b/sketch/src/sketches/mod.rs index c813594..bf4faaa 100644 --- a/sketch/src/sketches/mod.rs +++ b/sketch/src/sketches/mod.rs @@ -1,20 +1,6 @@ use super::*; pub(self) use nightsketch_derive::{sketch, sketchlist}; -pub struct SketchList {} - -impl SketchList { - pub fn default_sketch() -> Box { - Box::new(blossom::Blossom::default()) - } - pub fn sketch_by_name(name: &str) -> SketchResult> { - match name { - "blossom" => Ok(Box::new(blossom::Blossom::default())), - _ => Err(SketchError::Todo("sdfs".to_string())), - } - } -} - sketchlist! { blossom::Blossom, } diff --git a/sketch_derive/src/codegen/sketchlist.rs b/sketch_derive/src/codegen/sketchlist.rs index 74c279b..d581aeb 100644 --- a/sketch_derive/src/codegen/sketchlist.rs +++ b/sketch_derive/src/codegen/sketchlist.rs @@ -9,7 +9,8 @@ pub fn sketch_subcommand_enum_tokens(sketches: &[SketchListEntry]) -> proc_macro .iter() .map(|sketch| { let s = &sketch.sketch; - quote!(#s(#s)) + let m = &sketch.module; + quote!(#s(#m::#s)) }) .collect(); @@ -51,7 +52,7 @@ pub fn sketch_subcommand_enum_tokens(sketches: &[SketchListEntry]) -> proc_macro quote!( #sketch_subcommand_tokens ) } -pub fn sketch_mod_use_tokens(sketches: &[SketchListEntry]) -> proc_macro2::TokenStream { +pub fn sketch_mod_stmts_tokens(sketches: &[SketchListEntry]) -> proc_macro2::TokenStream { let mod_stmts: Vec = sketches .iter() .map(|sketch| { @@ -59,17 +60,38 @@ pub fn sketch_mod_use_tokens(sketches: &[SketchListEntry]) -> proc_macro2::Token quote!(mod #m) }) .collect(); - let use_stmts: Vec = sketches + + quote! { + #(#mod_stmts);*; + } +} + +pub fn sketchlist_struct_tokens(sketches: &[SketchListEntry]) -> proc_macro2::TokenStream { + let sketch_by_name_match_arms: Vec = sketches .iter() .map(|sketch| { let m = &sketch.module; let s = &sketch.sketch; - quote!(pub use #m::#s) + let s_name = s.to_string(); + quote! { + #s_name => Ok(Box::new(#m::#s::default())) + } }) .collect(); - quote!( - #(#mod_stmts);*; - #(#use_stmts);*; - ) + quote! { + pub struct SketchList {} + + impl SketchList { + pub fn default_sketch() -> Box { + Box::new(blossom::Blossom::default()) + } + pub fn sketch_by_name(name: &str) -> SketchResult> { + match name { + #(#sketch_by_name_match_arms),*, + _ => Err(SketchError::Todo("sdfs".to_string())), + } + } + } + } } diff --git a/sketch_derive/src/lib.rs b/sketch_derive/src/lib.rs index bffa3bc..aff2c2b 100644 --- a/sketch_derive/src/lib.rs +++ b/sketch_derive/src/lib.rs @@ -19,18 +19,27 @@ pub fn sketch(_attr: TokenStream, input: TokenStream) -> TokenStream { let impl_sketchaccess = impl_sketchaccess_tokens(name, params); let impl_default = impl_default_tokens(name, params); - quote! ( + quote! { #struct_tokens #impl_default #impl_sketchaccess - ) + } .into() } #[proc_macro] pub fn sketchlist(input: TokenStream) -> TokenStream { - let sketches: SketchList = parse_macro_input!(input); - let sketch_mod_use_stmts = sketch_mod_use_tokens(&sketches.sketches); - let sketch_subcommand_enum = sketch_subcommand_enum_tokens(&sketches.sketches); - quote!(#sketch_mod_use_stmts #sketch_subcommand_enum).into() + let sketchlist: SketchList = parse_macro_input!(input); + let sketches = &sketchlist.sketches; + + let sketch_mod_stmts = sketch_mod_stmts_tokens(sketches); + let sketch_subcommand_enum = sketch_subcommand_enum_tokens(sketches); + let sketchlist_struct = sketchlist_struct_tokens(sketches); + + quote! { + #sketch_mod_stmts + #sketch_subcommand_enum + #sketchlist_struct + } + .into() } From 3d842057186a0993638e8337a27f3daeb5f50be2 Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Tue, 2 Nov 2021 19:30:57 -0700 Subject: [PATCH 5/6] Derive list of sketches, and allow sketch selection in UI --- sketch_derive/src/codegen/sketchlist.rs | 13 +++++++++++++ ui/src/app/sketch_control.rs | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/sketch_derive/src/codegen/sketchlist.rs b/sketch_derive/src/codegen/sketchlist.rs index d581aeb..03ddaf9 100644 --- a/sketch_derive/src/codegen/sketchlist.rs +++ b/sketch_derive/src/codegen/sketchlist.rs @@ -79,6 +79,14 @@ pub fn sketchlist_struct_tokens(sketches: &[SketchListEntry]) -> proc_macro2::To }) .collect(); + let sketch_names: Vec = sketches + .iter() + .map(|sketch| { + let s_name = sketch.sketch.to_string(); + quote! { #s_name.to_string() } + }) + .collect(); + quote! { pub struct SketchList {} @@ -92,6 +100,11 @@ pub fn sketchlist_struct_tokens(sketches: &[SketchListEntry]) -> proc_macro2::To _ => Err(SketchError::Todo("sdfs".to_string())), } } + pub fn sketch_names() -> Vec { + vec![ + #(#sketch_names),* + ] + } } } } diff --git a/ui/src/app/sketch_control.rs b/ui/src/app/sketch_control.rs index a7b5ad5..9495db9 100644 --- a/ui/src/app/sketch_control.rs +++ b/ui/src/app/sketch_control.rs @@ -4,17 +4,23 @@ use nightsketch::*; pub struct SketchControl { sketch: Box, + sketch_name: String, params: Vec, + sketch_names: Vec, pub needs_render: bool, } impl Default for SketchControl { fn default() -> Self { let sketch = SketchList::default_sketch(); + let sketch_name = "Blossom".to_string(); let params = sketch.param_metadata(); + let sketch_names = SketchList::sketch_names(); SketchControl { sketch, + sketch_name, params, + sketch_names, needs_render: true, } } @@ -22,6 +28,24 @@ impl Default for SketchControl { impl SketchControl { fn param_grid_contents(&mut self, ui: &mut egui::Ui) { + ui.label("Sketch"); + let val = self.sketch_name.to_string(); + egui::ComboBox::from_label("") + .selected_text(self.sketch_name.to_string()) + .show_ui(ui, |ui| { + for n in &self.sketch_names { + ui.selectable_value(&mut self.sketch_name, n.to_string(), n); + } + }); + if val != self.sketch_name { + self.sketch = SketchList::sketch_by_name(&self.sketch_name).unwrap(); + self.needs_render = true; + self.params = self.sketch.param_metadata(); + } + ui.end_row(); + // Leave some visual space without a separator + ui.end_row(); + for param in &self.params { let sketch = &mut self.sketch; let id = param.id; From c70943f86a8fd2053371c7155bc6433d4cb7174d Mon Sep 17 00:00:00 2001 From: Kyle Kneitinger Date: Tue, 2 Nov 2021 19:46:25 -0700 Subject: [PATCH 6/6] Correct rust stable quote! macro braces --- sketch_derive/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sketch_derive/src/lib.rs b/sketch_derive/src/lib.rs index aff2c2b..de7cde0 100644 --- a/sketch_derive/src/lib.rs +++ b/sketch_derive/src/lib.rs @@ -19,11 +19,11 @@ pub fn sketch(_attr: TokenStream, input: TokenStream) -> TokenStream { let impl_sketchaccess = impl_sketchaccess_tokens(name, params); let impl_default = impl_default_tokens(name, params); - quote! { + quote!( #struct_tokens #impl_default #impl_sketchaccess - } + ) .into() } @@ -36,10 +36,10 @@ pub fn sketchlist(input: TokenStream) -> TokenStream { let sketch_subcommand_enum = sketch_subcommand_enum_tokens(sketches); let sketchlist_struct = sketchlist_struct_tokens(sketches); - quote! { + quote!( #sketch_mod_stmts #sketch_subcommand_enum #sketchlist_struct - } + ) .into() }