commit d1dd8e3025766be03521c488a2f65cdd1202e33b
parent b9fff5d7c191955ae61eb22983fdd23993d995f0
Author: Oliver Lowe <o@olowe.co>
Date: Tue, 16 Jul 2024 15:51:59 +1000
sdp: parse time zone adjustments
That's all the fields in SDP handled!
Diffstat:
3 files changed, 86 insertions(+), 10 deletions(-)
diff --git a/sdp/parser.go b/sdp/parser.go
@@ -20,7 +20,7 @@ type parser struct {
session Session
}
-var ftab = [...]string{"i", "u", "e", "p", "c", "b", "t", "r", "z", "a", "m", "a"}
+var ftab = [...]string{"i", "u", "e", "p", "c", "b", "t", "r", "z", "a", "m"}
var mtab = [...]string{"i", "c", "b", "a", "m"}
@@ -141,10 +141,15 @@ func (p *parser) parseOptional() error {
p.session.Repeat = &repeat
p.next = ftab[8:]
case "z":
- return fmt.Errorf("parse time desc %s not yet implemented", p.value)
+ var err error
+ p.session.Adjustments, err = parseAdjustments(p.value)
+ if err != nil {
+ return fmt.Errorf("parse time adjustments: %w", err)
+ }
+ p.next = ftab[9:]
case "a":
p.session.Attributes = strings.Fields(p.value)
- p.next = ftab[9:]
+ p.next = ftab[10:]
case "m":
m, err := parseMedia(p.value)
if err != nil {
@@ -153,6 +158,8 @@ func (p *parser) parseOptional() error {
p.session.Media = append(p.session.Media, m)
p.next = mtab[:]
return p.parseMedia()
+ default:
+ return fmt.Errorf("unknown field key %s", p.key)
}
}
return p.err
@@ -237,7 +244,7 @@ func parseRepeat(s string) (Repeat, error) {
// guard against negative durations, decimals.
// these are valid for time.ParseDuration, but not for our Repeat.
if i := strings.IndexAny(s, "-."); i > 0 {
- return Repeat{}, fmt.Errorf("illegal character %c", s[i])
+ return Repeat{}, fmt.Errorf("illegal character in duration: %c", s[i])
}
fields := strings.Fields(s)
if len(fields) < 3 {
diff --git a/sdp/sdp.go b/sdp/sdp.go
@@ -26,10 +26,11 @@ type Session struct {
Bandwidth *Bandwidth
// Time holds the start time and stop time of the Session, at
// the first and second index respectively.
- Time [2]time.Time
- Repeat *Repeat
- Attributes []string
- Media []Media
+ Time [2]time.Time
+ Repeat *Repeat
+ Adjustments []TimeAdjustment
+ Attributes []string
+ Media []Media
}
type Origin struct {
@@ -228,3 +229,30 @@ func parseConnInfo(s string) (ConnInfo, error) {
}
return conn, nil
}
+
+type TimeAdjustment struct {
+ When time.Time
+ Offset time.Duration
+}
+
+func parseAdjustments(line string) ([]TimeAdjustment, error) {
+ fields := strings.Fields(line)
+ if len(fields)%2 != 0 {
+ return nil, fmt.Errorf("odd field count %d", len(fields))
+ }
+ var adjustments []TimeAdjustment
+ for i := 0; i < len(fields); i += 2 {
+ var adj TimeAdjustment
+ t, err := strconv.Atoi(fields[i])
+ if err != nil {
+ return nil, fmt.Errorf("time %s: %w", fields[i], err)
+ }
+ adj.When = time.Unix(int64(t-sinceTimeZero), 0).UTC()
+ adj.Offset, err = parseDuration(fields[i+1])
+ if err != nil {
+ return nil, fmt.Errorf("offset %s: %w", fields[i+1], err)
+ }
+ adjustments = append(adjustments, adj)
+ }
+ return adjustments, nil
+}
diff --git a/sdp/sdp_test.go b/sdp/sdp_test.go
@@ -26,7 +26,10 @@ func TestReadSession(t *testing.T) {
Host: "www.jdoe.example.com",
Path: "/home.html",
},
- Email: &mail.Address{"Jane Doe", "jane@jdoe.example.com"},
+ Email: &mail.Address{
+ Name: "Jane Doe",
+ Address: "jane@jdoe.example.com",
+ },
Phone: "+16175556011",
Connection: &ConnInfo{
Type: "IP4",
@@ -61,7 +64,10 @@ func TestReadSession(t *testing.T) {
want: Session{
Origin: Origin{"jdoe", 3724394400, 3724394405, "IP4", "198.51.100.1"},
Name: "Call to John Smith",
- Email: &mail.Address{"Jane Doe", "jane@jdoe.example.com"},
+ Email: &mail.Address{
+ Name: "Jane Doe",
+ Address: "jane@jdoe.example.com",
+ },
},
},
{
@@ -229,3 +235,38 @@ func TestDuration(t *testing.T) {
})
}
}
+
+func TestParseAdjustments(t *testing.T) {
+ var cases = []struct {
+ name string
+ line string
+ want []TimeAdjustment
+ wantErr bool
+ }{
+ {"uneven", "3730922900 -2h 123456789", nil, true},
+ {"missing offset", "3730922900 ", nil, true},
+ {"garbage", "hello world!", nil, true},
+ {
+ "from rfc",
+ "3730928400 -1h 3749680800 0",
+ []TimeAdjustment{
+ {time.Date(2018, time.March, 25, 1, 0, 0, 0, time.UTC), -time.Hour},
+ {time.Date(2018, time.October, 28, 2, 0, 0, 0, time.UTC), 0},
+ },
+ false,
+ },
+ }
+ for _, tt := range cases {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := parseAdjustments(tt.line)
+ if err != nil && !tt.wantErr {
+ t.Fatal(err)
+ } else if err == nil && tt.wantErr {
+ t.Error("unexpected nil error")
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("parseAdjustments(%q) = %v, want %v", tt.line, got, tt.want)
+ }
+ })
+ }
+}