aboutsummaryrefslogtreecommitdiffhomepage
path: root/backend/api/convert.go
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-21 10:29:21 +0900
committernsfisis <nsfisis@gmail.com>2026-02-21 10:29:21 +0900
commite8db174d3e464a5764a9f4bfd82172261bd50519 (patch)
tree68cb8f0713fcc1f960a650d879232cb4c20ca6cd /backend/api/convert.go
parent1be106ac53caa019a8912af932a43570fa8c052d (diff)
downloadphperkaigi-2026-albatross-e8db174d3e464a5764a9f4bfd82172261bd50519.tar.gz
phperkaigi-2026-albatross-e8db174d3e464a5764a9f4bfd82172261bd50519.tar.zst
phperkaigi-2026-albatross-e8db174d3e464a5764a9f4bfd82172261bd50519.zip
refactor(api): separate business logic into game, tournament, session packages
Extract business logic from api/handler.go into dedicated service packages: - session: context helpers (resolves admin → api import dependency) - game: game state, code submission, ranking, watch logic - tournament: bracket construction and seed ordering - api/convert.go: domain → API type conversion functions api/handler.go is now a thin adapter that delegates to services and maps domain errors to HTTP status codes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'backend/api/convert.go')
-rw-r--r--backend/api/convert.go160
1 files changed, 160 insertions, 0 deletions
diff --git a/backend/api/convert.go b/backend/api/convert.go
new file mode 100644
index 0000000..f05f3bf
--- /dev/null
+++ b/backend/api/convert.go
@@ -0,0 +1,160 @@
+package api
+
+import (
+ "github.com/oapi-codegen/nullable"
+
+ "albatross-2026-backend/game"
+ "albatross-2026-backend/tournament"
+)
+
+func toAPIUser(p game.Player) User {
+ return User{
+ UserID: p.UserID,
+ Username: p.Username,
+ DisplayName: p.DisplayName,
+ IconPath: p.IconPath,
+ IsAdmin: p.IsAdmin,
+ Label: toNullable(p.Label),
+ }
+}
+
+func toAPIGame(g game.GameDetail) Game {
+ var startedAt *int64
+ if g.StartedAt != nil {
+ ts := g.StartedAt.Unix()
+ startedAt = &ts
+ }
+ mainPlayers := make([]User, len(g.MainPlayers))
+ for i, p := range g.MainPlayers {
+ mainPlayers[i] = toAPIUser(p)
+ }
+ return Game{
+ GameID: g.GameID,
+ GameType: GameType(g.GameType),
+ IsPublic: g.IsPublic,
+ DisplayName: g.DisplayName,
+ DurationSeconds: g.DurationSeconds,
+ StartedAt: startedAt,
+ Problem: Problem{
+ ProblemID: g.Problem.ProblemID,
+ Title: g.Problem.Title,
+ Description: g.Problem.Description,
+ Language: ProblemLanguage(g.Problem.Language),
+ SampleCode: g.Problem.SampleCode,
+ },
+ MainPlayers: mainPlayers,
+ }
+}
+
+func toAPILatestState(s game.LatestState) LatestGameState {
+ var score nullable.Nullable[int]
+ if s.Score != nil {
+ score = nullable.NewNullableWithValue(*s.Score)
+ } else {
+ score = nullable.NewNullNullable[int]()
+ }
+ var submittedAt nullable.Nullable[int64]
+ if s.BestScoreSubmittedAt != nil {
+ submittedAt = nullable.NewNullableWithValue(*s.BestScoreSubmittedAt)
+ } else {
+ submittedAt = nullable.NewNullNullable[int64]()
+ }
+ return LatestGameState{
+ Code: s.Code,
+ Score: score,
+ BestScoreSubmittedAt: submittedAt,
+ Status: ExecutionStatus(s.Status),
+ }
+}
+
+func toAPIRankingEntry(r game.RankingEntry) RankingEntry {
+ var code nullable.Nullable[string]
+ if r.Code != nil {
+ code = nullable.NewNullableWithValue(*r.Code)
+ } else {
+ code = nullable.NewNullNullable[string]()
+ }
+ return RankingEntry{
+ Player: toAPIUser(r.Player),
+ Score: r.Score,
+ SubmittedAt: r.SubmittedAt,
+ Code: code,
+ }
+}
+
+func toAPISubmission(s game.SubmissionDetail) Submission {
+ return Submission{
+ SubmissionID: s.SubmissionID,
+ GameID: s.GameID,
+ Code: s.Code,
+ CodeSize: s.CodeSize,
+ Status: ExecutionStatus(s.Status),
+ CreatedAt: s.CreatedAt,
+ }
+}
+
+func toAPITournamentUser(p tournament.Player) User {
+ return User{
+ UserID: p.UserID,
+ Username: p.Username,
+ DisplayName: p.DisplayName,
+ IconPath: p.IconPath,
+ IsAdmin: p.IsAdmin,
+ Label: toNullable(p.Label),
+ }
+}
+
+func toAPITournamentPlayerPtr(p *tournament.Player) *User {
+ if p == nil {
+ return nil
+ }
+ u := toAPITournamentUser(*p)
+ return &u
+}
+
+func toAPITournament(t tournament.TournamentBracket) Tournament {
+ entries := make([]TournamentEntry, len(t.Entries))
+ for i, e := range t.Entries {
+ entries[i] = TournamentEntry{
+ User: toAPITournamentUser(e.User),
+ Seed: e.Seed,
+ }
+ }
+ matches := make([]TournamentMatch, len(t.Matches))
+ for i, m := range t.Matches {
+ matches[i] = TournamentMatch{
+ TournamentMatchID: m.TournamentMatchID,
+ Round: m.Round,
+ Position: m.Position,
+ GameID: m.GameID,
+ Player1: toAPITournamentPlayerPtr(m.Player1),
+ Player2: toAPITournamentPlayerPtr(m.Player2),
+ Player1Score: m.Player1Score,
+ Player2Score: m.Player2Score,
+ WinnerUserID: m.WinnerUserID,
+ IsBye: m.IsBye,
+ }
+ }
+ return Tournament{
+ TournamentID: t.TournamentID,
+ DisplayName: t.DisplayName,
+ BracketSize: t.BracketSize,
+ NumRounds: t.NumRounds,
+ Entries: entries,
+ Matches: matches,
+ }
+}
+
+func toNullable[T any](p *T) nullable.Nullable[T] {
+ if p == nil {
+ return nullable.NewNullNullable[T]()
+ }
+ return nullable.NewNullableWithValue(*p)
+}
+
+func toNullableWith[T, U any](p *T, f func(T) U) nullable.Nullable[U] {
+ if p == nil {
+ return nullable.NewNullNullable[U]()
+ }
+ return nullable.NewNullableWithValue(f(*p))
+}