I've got an open source app written in Rust. I'm in the process of adding an enterprise version of it. 99% of the functionality is in the open source version, and the enterprise version just needs to replace a few of the underlying functions. I'd like to keep the enterprise code closed-source.
What's the best project layout?
It would have to be two separate git repos (I think) if I want one to be open and the other closed.
I could have the enterprise version depend on the open source version, but I'm having a hard time replacing the underlying functions in the open source version with enterprise functions. Rust won't let me swap out code that exists in a dependency, even with a feature flag, because we'd have a circular dependency: enterprise depends on the open source, but the open source must access enterprise code.
I've tried tricks with the #[path] directive to have the open source pull a file from the enterprise project, but that has led to dependency hell.
Surely this is a solved problem. How do other people do it?
Answer
Traits can do this.
Say you have a function in your open source version that does something:
pub fn run() {
println!("this is open source");
}
You can make a trait that has items for all the things you want to change. Then make your run
function generic.
pub trait Version {
fn f();
}
pub fn run<V: Version>() {
V::f();
}
Then you can implement the trait with your open source behavior and use it in your app.
pub struct OpenSource;
impl Version for OpenSource {
fn f() {
println!("this is open source");
}
}
fn main() {
run::<OpenSource>();
}
Then the enterprise version can include the open source library as a dependency, implement the trait, and use that implementation in its app.
pub struct Enterprise;
impl your_app::Version for Enterprise {
fn f() {
println!("this is enterprise");
}
}
fn main() {
your_app::run::<Enterprise>();
}
You will most likely have many functions and possibly some associated types in your trait, and need to pass the generic through many functions.