The UDP and TCP code is almost entirely duplicate - no need to repeat the same logic, introducing bugs.
there is a trait ServerMocker - that defines common functions, but does not have any value because noone would write code like this - as there is almost never any need to use it via the trait, only via specific type (tcp vs udp vs ...)
let server = TcpServerMocker::new();
run_with_server(server);
fn run_with_server(server: dyn ServerMocker) {
// use server without knowing if it is UDP or TCP
}
Proposal
I think it would be better to consolidate API, while keeping the "backend" implementation different (udp or tcp-specific). There are two way to do it:
Simpler API
Introduce one struct ServerMocker that supports tcp, udp, and eventually other protocols. Note that the actual "backend" implementation could be done by different structs internally, but that's hidden from the user. The actual usage of the server is described in #15
let tcp_server = ServerMocker::tcp(); // or tcp_with_port(n) or with_opts(TcpOptions { .... })
let udp_server = ServerMocker::udp(); // or udp_with_port(n) or with_opts(UdpOptions { .... })
// use servers
Slightly more powerful API
I am not sure this is needed just yet, and it can be introduced later. This is based on the type-state pattern described in this excellent video:
let tcp_server = ServerMocker::<TcpOptions>::new(); // or with_opts(TcpOptions { .... })
let udp_server = ServerMocker::<UdpOptions>::new(); // or with_opts(UdpOptions { .... })
// this allows different functions to be available on different types:
impl ServerMocker<TcpOptions> {
pub fn tcp_specific_command(...) { ... }
}
Currently there is a bit of a confusing API:
trait ServerMocker
- that defines common functions, but does not have any value because noone would write code like this - as there is almost never any need to use it via the trait, only via specific type (tcp vs udp vs ...)Proposal
I think it would be better to consolidate API, while keeping the "backend" implementation different (udp or tcp-specific). There are two way to do it:
Simpler API
Introduce one
struct ServerMocker
that supports tcp, udp, and eventually other protocols. Note that the actual "backend" implementation could be done by different structs internally, but that's hidden from the user. The actual usage of theserver
is described in #15Slightly more powerful API
I am not sure this is needed just yet, and it can be introduced later. This is based on the type-state pattern described in this excellent video: