summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--course_groups.go118
-rw-r--r--course_types.go (renamed from wsx.go)24
-rw-r--r--courses.go110
-rw-r--r--database.go (renamed from db.go)0
-rw-r--r--endpoint_auth.go (renamed from auth.go)0
-rw-r--r--endpoint_export.go (renamed from export.go)0
-rw-r--r--endpoint_index.go (renamed from index.go)0
-rw-r--r--endpoint_newcourses.go (renamed from newcourses.go)0
-rw-r--r--endpoint_ws.go (renamed from wsh.go)0
-rw-r--r--errors.go (renamed from err.go)0
-rw-r--r--misc_utils.go (renamed from utils.go)0
-rw-r--r--usem.go2
-rw-r--r--ws_connection.go (renamed from wsc.go)20
-rw-r--r--ws_utils.go (renamed from wsp.go)30
14 files changed, 161 insertions, 143 deletions
diff --git a/course_groups.go b/course_groups.go
new file mode 100644
index 0000000..c1541ce
--- /dev/null
+++ b/course_groups.go
@@ -0,0 +1,118 @@
+/*
+ * Course groups
+ *
+ * Copyright (C) 2024 Runxi Yu <https://runxiyu.org>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package main
+
+import (
+ "context"
+ "fmt"
+)
+
+type userCourseGroupsT map[courseGroupT]struct{}
+
+type courseGroupT string
+
+func checkCourseGroup(cg courseGroupT) bool {
+ _, ok := courseGroups[cg]
+ return ok
+}
+
+const (
+ mw1 courseGroupT = "MW1"
+ mw2 courseGroupT = "MW2"
+ mw3 courseGroupT = "MW3"
+ tt1 courseGroupT = "TT1"
+ tt2 courseGroupT = "TT2"
+ tt3 courseGroupT = "TT3"
+)
+
+var courseGroups = map[courseGroupT]string{
+ mw1: "Monday/Wednesday CCA1",
+ mw2: "Monday/Wednesday CCA2",
+ mw3: "Monday/Wednesday CCA3",
+ tt1: "Tuesday/Thursday CCA1",
+ tt2: "Tuesday/Thursday CCA2",
+ tt3: "Tuesday/Thursday CCA3",
+}
+
+func populateUserCourseGroups(
+ ctx context.Context,
+ userCourseGroups *userCourseGroupsT,
+ userID string,
+) error {
+ rows, err := db.Query(
+ ctx,
+ "SELECT courseid FROM choices WHERE userid = $1",
+ userID,
+ )
+ if err != nil {
+ return fmt.Errorf(
+ "%w: %w",
+ errUnexpectedDBError,
+ err,
+ )
+ }
+ for {
+ if !rows.Next() {
+ err := rows.Err()
+ if err != nil {
+ return fmt.Errorf(
+ "%w: %w",
+ errUnexpectedDBError,
+ err,
+ )
+ }
+ break
+ }
+ var thisCourseID int
+ err := rows.Scan(&thisCourseID)
+ if err != nil {
+ return fmt.Errorf(
+ "%w: %w",
+ errUnexpectedDBError,
+ err,
+ )
+ }
+ var thisGroupName courseGroupT
+ _course, ok := courses.Load(thisCourseID)
+ if !ok {
+ return fmt.Errorf(
+ "%w: %d",
+ errNoSuchCourse,
+ thisCourseID,
+ )
+ }
+ course, ok := _course.(*courseT)
+ if !ok {
+ panic("courses map has non-\"*courseT\" items")
+ }
+ thisGroupName = course.Group
+ if _, ok := (*userCourseGroups)[thisGroupName]; ok {
+ return fmt.Errorf(
+ "%w: user %v, group %v",
+ errMultipleChoicesInOneGroup,
+ userID,
+ thisGroupName,
+ )
+ }
+ (*userCourseGroups)[thisGroupName] = struct{}{}
+ }
+ return nil
+}
diff --git a/wsx.go b/course_types.go
index d27b72f..f82678c 100644
--- a/wsx.go
+++ b/course_types.go
@@ -1,5 +1,5 @@
/*
- * Generic WebSocket auxiliary functions
+ * Course types
*
* Copyright (C) 2024 Runxi Yu <https://runxiyu.org>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -20,17 +20,19 @@
package main
-import (
- "context"
- "fmt"
+type courseTypeT string
- "github.com/coder/websocket"
+const (
+ sport courseTypeT = "Sport"
+ nonSport courseTypeT = "Non-sport"
)
-func writeText(ctx context.Context, c *websocket.Conn, msg string) error {
- err := c.Write(ctx, websocket.MessageText, []byte(msg))
- if err != nil {
- return fmt.Errorf("%w: %w", errWebSocketWrite, err)
- }
- return nil
+var courseTypes = map[courseTypeT]struct{}{
+ sport: {},
+ nonSport: {},
+}
+
+func checkCourseType(ct courseTypeT) bool {
+ _, ok := courseTypes[ct]
+ return ok
}
diff --git a/courses.go b/courses.go
index e365571..4591cfb 100644
--- a/courses.go
+++ b/courses.go
@@ -29,11 +29,6 @@ import (
"github.com/coder/websocket"
)
-type (
- courseTypeT string
- courseGroupT string
-)
-
type courseT struct {
/*
* Selected is usually accessed atomically, but a lock is still
@@ -55,44 +50,6 @@ type courseT struct {
Usems sync.Map /* string, *usemT */
}
-const (
- sport courseTypeT = "Sport"
- nonSport courseTypeT = "Non-sport"
-)
-
-var courseTypes = map[courseTypeT]struct{}{
- sport: {},
- nonSport: {},
-}
-
-const (
- mw1 courseGroupT = "MW1"
- mw2 courseGroupT = "MW2"
- mw3 courseGroupT = "MW3"
- tt1 courseGroupT = "TT1"
- tt2 courseGroupT = "TT2"
- tt3 courseGroupT = "TT3"
-)
-
-var courseGroups = map[courseGroupT]string{
- mw1: "Monday/Wednesday CCA1",
- mw2: "Monday/Wednesday CCA2",
- mw3: "Monday/Wednesday CCA3",
- tt1: "Tuesday/Thursday CCA1",
- tt2: "Tuesday/Thursday CCA2",
- tt3: "Tuesday/Thursday CCA3",
-}
-
-func checkCourseType(ct courseTypeT) bool {
- _, ok := courseTypes[ct]
- return ok
-}
-
-func checkCourseGroup(cg courseGroupT) bool {
- _, ok := courseGroups[cg]
- return ok
-}
-
var courses sync.Map /* int, *courseT */
var numCourses uint32 /* atomic */
@@ -172,73 +129,6 @@ func setupCourses(ctx context.Context) error {
return nil
}
-type userCourseGroupsT map[courseGroupT]struct{}
-
-func populateUserCourseGroups(
- ctx context.Context,
- userCourseGroups *userCourseGroupsT,
- userID string,
-) error {
- rows, err := db.Query(
- ctx,
- "SELECT courseid FROM choices WHERE userid = $1",
- userID,
- )
- if err != nil {
- return fmt.Errorf(
- "%w: %w",
- errUnexpectedDBError,
- err,
- )
- }
- for {
- if !rows.Next() {
- err := rows.Err()
- if err != nil {
- return fmt.Errorf(
- "%w: %w",
- errUnexpectedDBError,
- err,
- )
- }
- break
- }
- var thisCourseID int
- err := rows.Scan(&thisCourseID)
- if err != nil {
- return fmt.Errorf(
- "%w: %w",
- errUnexpectedDBError,
- err,
- )
- }
- var thisGroupName courseGroupT
- _course, ok := courses.Load(thisCourseID)
- if !ok {
- return fmt.Errorf(
- "%w: %d",
- errNoSuchCourse,
- thisCourseID,
- )
- }
- course, ok := _course.(*courseT)
- if !ok {
- panic("courses map has non-\"*courseT\" items")
- }
- thisGroupName = course.Group
- if _, ok := (*userCourseGroups)[thisGroupName]; ok {
- return fmt.Errorf(
- "%w: user %v, group %v",
- errMultipleChoicesInOneGroup,
- userID,
- thisGroupName,
- )
- }
- (*userCourseGroups)[thisGroupName] = struct{}{}
- }
- return nil
-}
-
func (course *courseT) decrementSelectedAndPropagate(
ctx context.Context,
conn *websocket.Conn,
diff --git a/db.go b/database.go
index e08f42f..e08f42f 100644
--- a/db.go
+++ b/database.go
diff --git a/auth.go b/endpoint_auth.go
index 58eb46b..58eb46b 100644
--- a/auth.go
+++ b/endpoint_auth.go
diff --git a/export.go b/endpoint_export.go
index 401c632..401c632 100644
--- a/export.go
+++ b/endpoint_export.go
diff --git a/index.go b/endpoint_index.go
index 512fe1d..512fe1d 100644
--- a/index.go
+++ b/endpoint_index.go
diff --git a/newcourses.go b/endpoint_newcourses.go
index 5963e1b..5963e1b 100644
--- a/newcourses.go
+++ b/endpoint_newcourses.go
diff --git a/wsh.go b/endpoint_ws.go
index 2ea0a2a..2ea0a2a 100644
--- a/wsh.go
+++ b/endpoint_ws.go
diff --git a/err.go b/errors.go
index 4597b3f..4597b3f 100644
--- a/err.go
+++ b/errors.go
diff --git a/utils.go b/misc_utils.go
index 4830fc1..4830fc1 100644
--- a/utils.go
+++ b/misc_utils.go
diff --git a/usem.go b/usem.go
index 3edfb64..403c3cf 100644
--- a/usem.go
+++ b/usem.go
@@ -1,5 +1,5 @@
/*
- * Additional synchronization routines
+ * Increase-unblocking capped semaphores
*
* Copyright (C) 2024 Runxi Yu <https://runxiyu.org>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/wsc.go b/ws_connection.go
index 2f00302..c407a70 100644
--- a/wsc.go
+++ b/ws_connection.go
@@ -24,7 +24,6 @@ import (
"context"
"errors"
"fmt"
- "log"
"sync"
"sync/atomic"
"time"
@@ -348,22 +347,3 @@ func handleConn(
var cancelPool sync.Map /* string, *context.CancelFunc */
var chanPool sync.Map /* string, *chan string */
-
-func propagate(msg string) {
- chanPool.Range(func(_userID, _ch interface{}) bool {
- ch, ok := _ch.(*chan string)
- if !ok {
- panic("chanPool has non-\"*chan string\" key")
- }
- select {
- case *ch <- msg:
- default:
- userID, ok := _userID.(string)
- if !ok {
- panic("chanPool has non-string key")
- }
- log.Println("WARNING: SendQ exceeded for " + userID)
- }
- return true
- })
-}
diff --git a/wsp.go b/ws_utils.go
index b41f765..949863f 100644
--- a/wsp.go
+++ b/ws_utils.go
@@ -1,5 +1,5 @@
/*
- * WebSocket-based protocol auxiliary functions
+ * WebSocket auxiliary functions
*
* Copyright (C) 2024 Runxi Yu <https://runxiyu.org>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -23,6 +23,7 @@ package main
import (
"context"
"fmt"
+ "log"
"sync/atomic"
"github.com/coder/websocket"
@@ -141,3 +142,30 @@ func sendSelectedUpdate(
}
return nil
}
+
+func propagate(msg string) {
+ chanPool.Range(func(_userID, _ch interface{}) bool {
+ ch, ok := _ch.(*chan string)
+ if !ok {
+ panic("chanPool has non-\"*chan string\" key")
+ }
+ select {
+ case *ch <- msg:
+ default:
+ userID, ok := _userID.(string)
+ if !ok {
+ panic("chanPool has non-string key")
+ }
+ log.Println("WARNING: SendQ exceeded for " + userID)
+ }
+ return true
+ })
+}
+
+func writeText(ctx context.Context, c *websocket.Conn, msg string) error {
+ err := c.Write(ctx, websocket.MessageText, []byte(msg))
+ if err != nil {
+ return fmt.Errorf("%w: %w", errWebSocketWrite, err)
+ }
+ return nil
+}