I've read recently about the typestate pattern and I think that it can be very useful for writing the foundation of my application.
As I've included in the example, it's very interesting for writing builders that avoid errors at compile time.
But my problem arises when I try to use it in dynamic environment, where I can't control the order of the operations. What can I do in these cases?
struct Product {
product_id: u64,
}
struct Invoice {
product_id: u64,
invoice_address: String,
}
struct Shipping {
product_id: u64,
invoice_address: String,
shipping_address: String,
}
trait Step {}
impl Step for Product {}
impl Step for Invoice {}
impl Step for Shipping {}
struct Process<T: Step + ?Sized> {
step: Box<T>,
}
impl Process<Product> {
pub fn select_product(product_id: u64) -> Process<Product> {
Process {
step: Box::new(Product { product_id }),
}
}
pub fn set_invoice_address(&self, invoice_address: String) -> Process<Invoice> {
Process {
step: Box::new(Invoice {
product_id: self.step.product_id,
invoice_address,
}),
}
}
}
impl Process<Invoice> {
pub fn set_shipping_address(&self, shipping_address: String) -> Process<Shipping> {
Process {
step: Box::new(Shipping {
product_id: self.step.product_id,
invoice_address: self.step.invoice_address.clone(),
shipping_address,
}),
}
}
}
impl Process<Shipping> {
pub fn pay(&self) {
println!("PAY");
}
}
// Chaining works perfectly
fn chaining(product_id: u64, invoice_address: String, shipping_address: String) {
Process::<Product>::select_product(product_id)
.set_invoice_address(invoice_address)
.set_shipping_address(shipping_address)
.pay();
}
// State receives messages/events to process each step and store in memory the current state of the process
struct State {}
impl State {
pub fn fill_product(&mut self, product_id: u64) {
unimplemented!()
}
pub fn fill_invoice(&mut self, invoice_address: String) {
unimplemented!()
}
pub fn reset_invoice(&mut self) {
unimplemented!()
}
pub fn fill_shipping(&mut self, shipping_address: String) {
unimplemented!()
}
pub fn reset_shipping(&mut self) {
unimplemented!()
}
pub fn pay(&mut self) {
unimplemented!()
}
}
fn main() {
let product_id = 10;
let invoice_address = "foo".to_string();
let shipping_address = "bar".to_string();
chaining(product_id, invoice_address, shipping_address);
}