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 db6306b..28f798e 100644 --- a/sketch/src/lib.rs +++ b/sketch/src/lib.rs @@ -1,57 +1,8 @@ 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::*; - -#[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) - } -} +mod sketches; +pub use sketches::*; diff --git a/sketch/src/blossom.rs b/sketch/src/sketches/blossom.rs similarity index 100% rename from sketch/src/blossom.rs rename to sketch/src/sketches/blossom.rs diff --git a/sketch/src/sketches/mod.rs b/sketch/src/sketches/mod.rs new file mode 100644 index 0000000..bf4faaa --- /dev/null +++ b/sketch/src/sketches/mod.rs @@ -0,0 +1,6 @@ +use super::*; +pub(self) use nightsketch_derive::{sketch, sketchlist}; + +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..03ddaf9 --- /dev/null +++ b/sketch_derive/src/codegen/sketchlist.rs @@ -0,0 +1,110 @@ +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; + let m = &sketch.module; + quote!(#s(#m::#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_stmts_tokens(sketches: &[SketchListEntry]) -> proc_macro2::TokenStream { + let mod_stmts: Vec = sketches + .iter() + .map(|sketch| { + let m = &sketch.module; + quote!(mod #m) + }) + .collect(); + + 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; + let s_name = s.to_string(); + quote! { + #s_name => Ok(Box::new(#m::#s::default())) + } + }) + .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 {} + + 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())), + } + } + pub fn sketch_names() -> Vec { + vec![ + #(#sketch_names),* + ] + } + } + } +} diff --git a/sketch_derive/src/lib.rs b/sketch_derive/src/lib.rs index e02aff9..de7cde0 100644 --- a/sketch_derive/src/lib.rs +++ b/sketch_derive/src/lib.rs @@ -19,10 +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 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() +} 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 }) + } +} 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..9495db9 100644 --- a/ui/src/app/sketch_control.rs +++ b/ui/src/app/sketch_control.rs @@ -3,17 +3,24 @@ use nightgraphics::render::EguiRenderer; use nightsketch::*; pub struct SketchControl { - sketch: SketchList, + 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(); + let sketch = SketchList::default_sketch(); + let sketch_name = "Blossom".to_string(); + let params = sketch.param_metadata(); + let sketch_names = SketchList::sketch_names(); SketchControl { - sketch: SketchList::default(), - params: sketch.param_metadata(), + sketch, + sketch_name, + params, + sketch_names, needs_render: true, } } @@ -21,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;