Ticket ids

Let's think again about our ticket management system.
Our ticket model right now looks like this:

#![allow(unused)]
fn main() {
pub struct Ticket {
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status
}
}

One thing is missing here: an identifier to uniquely identify a ticket.
That identifier should be unique for each ticket. That can be guaranteed by generating it automatically when a new ticket is created.

Refining the model

Where should the id be stored?
We could add a new field to the Ticket struct:

#![allow(unused)]
fn main() {
pub struct Ticket {
    pub id: TicketId,
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status
}
}

But we don't know the id before creating the ticket. So it can't be there from the get-go.
It'd have to be optional:

#![allow(unused)]
fn main() {
pub struct Ticket {
    pub id: Option<TicketId>,
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status
}
}

That's also not ideal—we'd have to handle the None case every single time we retrieve a ticket from the store, even though we know that the id should always be there once the ticket has been created.

The best solution is to have two different ticket states, represented by two separate types: a TicketDraft and a Ticket:

#![allow(unused)]
fn main() {
pub struct TicketDraft {
    pub title: TicketTitle,
    pub description: TicketDescription
}

pub struct Ticket {
    pub id: TicketId,
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status
}
}

A TicketDraft is a ticket that hasn't been created yet. It doesn't have an id, and it doesn't have a status.
A Ticket is a ticket that has been created. It has an id and a status.
Since each field in TicketDraft and Ticket embeds its own constraints, we don't have to duplicate logic across the two types.

Exercise

The exercise for this section is located in 06_ticket_management/12_two_states