// Package period understands the Compass lesson label: // // [period] - [subject code] - [room code] - [teacher initials] // // for example "1 - 7SCF - H14LAB - KNI". It is a leaf package so the parser, // the brief and the calendar all agree on what a routine class period is // (AGENTS.md §4 FR-16/FR-17, §7). package period import ( "regexp" "strings" ) // labelRe matches the period part of a lesson label: "5", "C" or // "RLE GROUP D". A school event's first part ("End of Year Senior Music // Concert") does not match, which is how a lesson is told from an event. var labelRe = regexp.MustCompile(`^(\d{1,2}|[A-Z]|RLE GROUP [A-Z])$`) // IsPeriodLabel reports whether s is a period slot rather than an event name. func IsPeriodLabel(s string) bool { return labelRe.MatchString(strings.TrimSpace(s)) } // Split breaks a lesson title into its components. ok is false when the first // part is not a period label, so the caller renders the title unchanged. // // A lesson may have fewer than four parts when Compass omits the room or the // teacher ("1 - 11HLEGB5 -"), so missing components come back as "". func Split(title string) (period, subject, room, teacher string, ok bool) { parts := SplitDash(title) if len(parts) < 2 || !IsPeriodLabel(parts[0]) { return "", "", "", "", false } period = parts[0] subject = parts[1] if len(parts) >= 3 { room = parts[2] } if len(parts) >= 4 { teacher = parts[3] } return period, subject, room, teacher, true } // SplitDash splits on " - " and trims each part. func SplitDash(s string) []string { raw := strings.Split(s, " - ") out := make([]string, 0, len(raw)) for _, p := range raw { out = append(out, strings.TrimSpace(p)) } return out }