Project architecture advice

Hi all,

I am struggling with generics in a project of mine, and I would like some help setting things up cleanly.

It is a binary using clap, and I want the user to be able to access the functionality of an ever-growing list of modules whose behavior depends on user input. I am currently using the lazy-static crate to achieve this in a static context. For example:

pub mod m1;
...
pub mod mN;

lazy_static! {
   pub static ref MODULES: 
        HashMap<&'static str, fn(Settings) -> dyn CommonTrait> = {
       let mut map = HashMap::new();
       map.insert(m1::NAME, m1::Struct::initialize);
       ...
       map.insert(mN:NAME, mN::Struct::initialize);
       map
   }
}

...allowing a command like binary --target m1_name --settings 258292 to be parsed by matching m1_name against the keys in the map, and passing the settings 258292 into the initialization function it maps to.

This is my first question -- is this the best way to map user input to specific module behavior? Keep in mind that it is very important to minimize the development effort to add an m(N+1) module in my case, which is why these aren't just hard coded into some match statement.

Now, the real reason I am asking for help is because I want to add another layer of complexity/freedom. Namely, making another trait (which all of the Structs referenced above implement) generic over an integer:

AnotherCommonTrait<const N: usize>

Here, all Structs could have multiple implementations for different arbitrary N values. This is important because I am using the nalgebra crate, and making Structs generic over integers actually makes a lot of sense in my use case. However, this kind of makes the complexity of the previous approach blow up, where I would need something like the following to make it work out:

lazy_static! {
   pub static ref MODULES: 
       HashMap<&'static str, 
           HashMap<usize, fn(Settings) -> dyn CommonTrait>> = {
       ...
       /// { X => Y} just being another map:
       map.insert(m1::NAME, 
           { 1 => <m1::Struct as AnotherCommonTrait<1>>::initialize,
             ...
           }
       );
       ...
   }

Which is pretty awful, and I don't even know if it would work. I feel like I am suffering some serious tunnel vision here, so I would welcome any ideas.

Thank you!

I feel like the first part should be doable with just clap. I'll write a detailed answer later.

Can you give a small compilable example on the playground?

I totally agree now that you mention it. I was trying to map the names of the modules as strings, when I could have just put them into an enum as a non-optional argument to a command. Thanks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.