diff --git a/parking_lot/floor.go b/parking_lot/floor.go index 9359561..bc3a46c 100644 --- a/parking_lot/floor.go +++ b/parking_lot/floor.go @@ -36,8 +36,8 @@ func createParkingSpots(count int, vehicleType vehicles.VehicleType) map[int]*Pa return spots } -func (p *ParkingFloor) FindParkingSpot(vehicleType vehicles.VehicleType) *ParkingSpot { - for _, spot := range p.ParkingSpots[vehicleType] { +func (pf *ParkingFloor) FindParkingSpot(vehicleType vehicles.VehicleType) *ParkingSpot { + for _, spot := range pf.ParkingSpots[vehicleType] { if spot.IsParkingSpotFree() { return spot } @@ -45,11 +45,11 @@ func (p *ParkingFloor) FindParkingSpot(vehicleType vehicles.VehicleType) *Parkin return nil } -func (p *ParkingFloor) DisplayFloorStatus(parkingFloor *ParkingFloor) { - fmt.Printf("Floor ID: %d\n", parkingFloor.FloorID) +func (pf *ParkingFloor) DisplayFloorStatus() { + fmt.Printf("Floor ID: %d\n", pf.FloorID) - for vehicleType, spotMap := range parkingFloor.ParkingSpots { - fmt.Printf("\n%s Spots:\n", vehicleType) + for vehicleType, spotMap := range pf.ParkingSpots { + fmt.Printf("%s Spots:\n", vehicleType) count := 0 for _, spot := range spotMap { @@ -58,6 +58,6 @@ func (p *ParkingFloor) DisplayFloorStatus(parkingFloor *ParkingFloor) { } } - fmt.Printf("\n%s Spot: %d Free\n", vehicleType, count) + fmt.Printf("%s Spot: %d Free\n", vehicleType, count) } } diff --git a/parking_lot/go.mod b/parking_lot/go.mod index c82d9d1..7cd63ee 100644 --- a/parking_lot/go.mod +++ b/parking_lot/go.mod @@ -1,3 +1,5 @@ module lld_go_parking_lot go 1.22.5 + +require github.com/google/uuid v1.6.0 // indirect diff --git a/parking_lot/go.sum b/parking_lot/go.sum new file mode 100644 index 0000000..7790d7c --- /dev/null +++ b/parking_lot/go.sum @@ -0,0 +1,2 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/parking_lot/main.go b/parking_lot/main.go index f1b3505..d793507 100644 --- a/parking_lot/main.go +++ b/parking_lot/main.go @@ -31,18 +31,26 @@ func main() { parkingLot.DisplayAvailability() - ticket, _ := parkingLot.ParkVehicle(vehicles.NewTruck("truck-1")) - - time.Sleep(10 * time.Second) - err := parkingLot.UnparkVehicle(ticket) + truck := vehicles.NewTruck("truck-1") + ticket, err := parkingLot.ParkVehicle(truck) if err != nil { + fmt.Printf("Failed to park %s: %v\n", truck.LicenceNumber, err) return } - formattedCharge := fmt.Sprintf("%.2f", ticket.CalculateTotalCharge()) - - fmt.Printf("bill for %s = %s\n", ticket.Vehicle.GetLicenceNumber(), formattedCharge) + time.Sleep(3 * time.Second) + err = parkingLot.UnparkVehicle(ticket.TicketId) + if err != nil { + fmt.Printf("Failed to unpark for ticketId %s: %v\n", ticket.TicketId, err) + return + } + // duplicate ticket unpark attempt + err = parkingLot.UnparkVehicle(ticket.TicketId) + if err != nil { + fmt.Printf("Failed to unpark for ticketId %s: %v\n", ticket.TicketId, err) + return + } } func parkCar(ind int, parkingLot *ParkingLot) { @@ -50,11 +58,9 @@ func parkCar(ind int, parkingLot *ParkingLot) { car := vehicles.NewCar(fmt.Sprintf("car-%d", ind)) - ticket, err := parkingLot.ParkVehicle(car) + _, err := parkingLot.ParkVehicle(car) if err != nil { fmt.Printf("Failed to park %s: %v\n", car.LicenceNumber, err) return } - - fmt.Printf("%s parked successfully. Ticket: %s\n", car.LicenceNumber, ticket.EntryTime) } diff --git a/parking_lot/parking_lot.go b/parking_lot/parking_lot.go index 1b52824..52049ef 100644 --- a/parking_lot/parking_lot.go +++ b/parking_lot/parking_lot.go @@ -13,32 +13,34 @@ var ( ) type ParkingLot struct { - Name string - floors []*ParkingFloor + Name string + floors []*ParkingFloor + tickets map[string]*ParkingTicket } func GetParkingLotInstance() *ParkingLot { once.Do(func() { - parkingLotInstance = &ParkingLot{} + parkingLotInstance = &ParkingLot{ + tickets: make(map[string]*ParkingTicket), + } }) return parkingLotInstance } -func (p *ParkingLot) AddFloor(floorID int) { - p.floors = append(p.floors, NewParkingFloor(floorID)) +func (pl *ParkingLot) AddFloor(floorID int) { + pl.floors = append(pl.floors, NewParkingFloor(floorID)) } -func (p *ParkingLot) DisplayAvailability() { - fmt.Printf("Parking Lot: %s\n", p.Name) - - for _, floor := range p.floors { - floor.DisplayFloorStatus(floor) +func (pl *ParkingLot) DisplayAvailability() { + fmt.Printf("Parking Lot: %s\n", pl.Name) + for _, floor := range pl.floors { + floor.DisplayFloorStatus() } } -func (p *ParkingLot) findParkingSpot(vehicleType vehicles.VehicleType) (*ParkingSpot, error) { - for _, floor := range p.floors { +func (pl *ParkingLot) findParkingSpot(vehicleType vehicles.VehicleType) (*ParkingSpot, error) { + for _, floor := range pl.floors { if spot := floor.FindParkingSpot(vehicleType); spot != nil { return spot, nil } @@ -47,8 +49,8 @@ func (p *ParkingLot) findParkingSpot(vehicleType vehicles.VehicleType) (*Parking return nil, fmt.Errorf("no available parking spot found for %s", vehicleType) } -func (p *ParkingLot) ParkVehicle(vehicle vehicles.VehicleInterface) (*ParkingTicket, error) { - parkingSpot, err := p.findParkingSpot(vehicle.GetVehicleType()) +func (pl *ParkingLot) ParkVehicle(vehicle vehicles.VehicleInterface) (*ParkingTicket, error) { + parkingSpot, err := pl.findParkingSpot(vehicle.GetVehicleType()) if err != nil { return nil, err } @@ -59,21 +61,47 @@ func (p *ParkingLot) ParkVehicle(vehicle vehicles.VehicleInterface) (*ParkingTic } parkingTicket := NewParkingTicket(vehicle, parkingSpot) + pl.tickets[parkingTicket.TicketId] = parkingTicket + + fmt.Printf("%s parked successfully. Ticket: %v\n", + vehicle.GetLicenceNumber(), + parkingTicket, + ) return parkingTicket, nil } -func (p *ParkingLot) UnparkVehicle(parkingTicket *ParkingTicket) error { +func (p *ParkingLot) UnparkVehicle(parkingTicketID string) error { + if len(parkingTicketID) == 0 { + return fmt.Errorf("invalid parking ticket id") + } + + parkingTicket := p.tickets[parkingTicketID] + if parkingTicket == nil { + return fmt.Errorf("invalid parking ticket") + } + + if parkingTicket.Status == Closed { + return fmt.Errorf("ticket is already used for exit") + } + parkingTicket.SetExitTime(time.Now()) - charge := parkingTicket.CalculateTotalCharge() + charge, err := parkingTicket.CalculateTotalCharge() + if err != nil { + return err + } - paymentSystem := NewPaymentSystem(charge, parkingTicket) + formattedCharge := fmt.Sprintf("%.2f", charge) + fmt.Printf("bill for %s = %s\n", parkingTicket.Vehicle.GetLicenceNumber(), formattedCharge) + paymentSystem := NewPaymentSystem(charge, parkingTicket) if err := paymentSystem.ProcessPayment(); err != nil { return fmt.Errorf("payment failed: %v. Vehicle is still parked", err) } parkingTicket.Spot.RemoveVehicle() + parkingTicket.SetTotalCharge(charge) + parkingTicket.SetTicketStatus(Closed) return nil } diff --git a/parking_lot/parking_spot.go b/parking_lot/parking_spot.go index 20a6cb9..a15fae6 100644 --- a/parking_lot/parking_spot.go +++ b/parking_lot/parking_spot.go @@ -17,25 +17,25 @@ func NewParkingSpot(spotID int, vehicleType vehicles.VehicleType) *ParkingSpot { return &ParkingSpot{SpotID: spotID, VehicleType: vehicleType} } -func (p *ParkingSpot) IsParkingSpotFree() bool { - return p.CurrentVehicle == nil +func (ps *ParkingSpot) IsParkingSpotFree() bool { + return ps.CurrentVehicle == nil } -func (p *ParkingSpot) ParkVehicle(vehicle vehicles.VehicleInterface) error { - p.lock.Lock() - defer p.lock.Unlock() +func (ps *ParkingSpot) ParkVehicle(vehicle vehicles.VehicleInterface) error { + ps.lock.Lock() + defer ps.lock.Unlock() - if vehicle.GetVehicleType() != p.VehicleType { - return fmt.Errorf("vehicle type mismatch: expected %s, got %s", p.VehicleType, vehicle.GetVehicleType()) + if vehicle.GetVehicleType() != ps.VehicleType { + return fmt.Errorf("vehicle type mismatch: expected %s, got %s", ps.VehicleType, vehicle.GetVehicleType()) } - if p.CurrentVehicle != nil { + if ps.CurrentVehicle != nil { return fmt.Errorf("parking spot already occupied") } - p.CurrentVehicle = &vehicle + ps.CurrentVehicle = &vehicle return nil } -func (p *ParkingSpot) RemoveVehicle() { - p.CurrentVehicle = nil +func (ps *ParkingSpot) RemoveVehicle() { + ps.CurrentVehicle = nil } diff --git a/parking_lot/parking_ticket.go b/parking_lot/parking_ticket.go index 4a78564..65e43a6 100644 --- a/parking_lot/parking_ticket.go +++ b/parking_lot/parking_ticket.go @@ -1,36 +1,67 @@ package main import ( + "fmt" "lld_go_parking_lot/vehicles" "time" + + "github.com/google/uuid" ) const baseCharge = 100.00 +type TicketStatus string + +const ( + Active TicketStatus = "ACTIVE" + Closed TicketStatus = "CLOSED" +) + type ParkingTicket struct { + TicketId string EntryTime time.Time - ExitTime time.Time + ExitTime *time.Time Vehicle vehicles.VehicleInterface Spot *ParkingSpot TotalCharge float64 + Status TicketStatus } func NewParkingTicket(vehicle vehicles.VehicleInterface, spot *ParkingSpot) *ParkingTicket { - return &ParkingTicket{EntryTime: time.Now(), ExitTime: time.Time{}, Vehicle: vehicle, Spot: spot, TotalCharge: 0.00} + return &ParkingTicket{ + TicketId: generateTicketID(), + EntryTime: time.Now(), + Vehicle: vehicle, + Spot: spot, + Status: Active, + } +} + +func generateTicketID() string { + return "TICKET-" + uuid.New().String() } func (p *ParkingTicket) SetExitTime(exitTime time.Time) { - p.ExitTime = exitTime + p.ExitTime = &exitTime } -func (p *ParkingTicket) CalculateTotalCharge() float64 { - if p.ExitTime == (time.Time{}) { - p.TotalCharge = baseCharge - return p.TotalCharge +func (p *ParkingTicket) SetTotalCharge(totalCharge float64) { + p.TotalCharge = totalCharge +} + +func (p *ParkingTicket) SetTicketStatus(status TicketStatus) { + p.Status = status +} + +func (p *ParkingTicket) CalculateTotalCharge() (float64, error) { + if p.ExitTime == nil { + return 0, fmt.Errorf("exit time not set") } + duration := p.ExitTime.Sub(p.EntryTime) hours := duration.Hours() additionalCharge := hours * p.Vehicle.GetVehicleCost() p.TotalCharge = baseCharge + additionalCharge - return p.TotalCharge + + return p.TotalCharge, nil }