package main
import (
"context"
"errors"
"github.com/go-mizu/mizu"
"github.com/go-mizu/mizu/contract"
"github.com/go-mizu/mizu/contract/rest"
"github.com/go-mizu/mizu/contract/openapi"
)
// Models
type Task struct {
ID string `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
type CreateTaskInput struct {
Title string `json:"title"`
}
type UpdateTaskInput struct {
Title *string `json:"title,omitempty"`
Completed *bool `json:"completed,omitempty"`
}
// Service
type TaskService struct {
tasks map[string]*Task
}
func (s *TaskService) ListTasks(ctx context.Context) ([]*Task, error) {
result := make([]*Task, 0, len(s.tasks))
for _, t := range s.tasks {
result = append(result, t)
}
return result, nil
}
func (s *TaskService) GetTask(ctx context.Context, id string) (*Task, error) {
task, ok := s.tasks[id]
if !ok {
return nil, contract.NotFound("task not found")
}
return task, nil
}
func (s *TaskService) CreateTask(ctx context.Context, input CreateTaskInput) (*Task, error) {
if input.Title == "" {
return nil, contract.BadRequest("title is required")
}
task := &Task{
ID: generateID(),
Title: input.Title,
}
s.tasks[task.ID] = task
return task, nil
}
func (s *TaskService) UpdateTask(ctx context.Context, id string, input UpdateTaskInput) (*Task, error) {
task, ok := s.tasks[id]
if !ok {
return nil, contract.NotFound("task not found")
}
if input.Title != nil {
task.Title = *input.Title
}
if input.Completed != nil {
task.Completed = *input.Completed
}
return task, nil
}
func (s *TaskService) DeleteTask(ctx context.Context, id string) error {
if _, ok := s.tasks[id]; !ok {
return contract.NotFound("task not found")
}
delete(s.tasks, id)
return nil
}
func main() {
app := mizu.New()
service := &TaskService{tasks: make(map[string]*Task)}
c := contract.Register(service)
// REST API
app.Mount("/api", rest.Handler(c))
// OpenAPI spec
spec := openapi.Generate(c)
app.Get("/openapi.json", func(ctx *mizu.Ctx) error {
return ctx.JSON(200, spec)
})
app.Listen(":3000")
}
func generateID() string { return "task-1" }