End *Episode // The end of the range; may be nil, which represents an endless range
}
+func EpisodeToRange(ep *Episode) *EpisodeRange {
+ return &EpisodeRange{
+ Type: ep.Type,
+ Start: ep,
+ End: ep,
+ }
+}
+
// Converts the EpisodeRange into AniDB API range format.
func (er *EpisodeRange) String() string {
return er.Format(er.scale())
if a.touches(b) {
c = &EpisodeRange{Type: a.Type}
- if a.Start.Number <= b.Start.Number {
+ if a.Start.Number == b.Start.Number {
+ if a.Start.Part <= b.Start.Part {
+ c.Start = a.Start
+ } else {
+ c.Start = b.Start
+ }
+ } else if a.Start.Number < b.Start.Number {
c.Start = a.Start
} else {
c.Start = b.Start
switch {
case a.End == nil || b.End == nil:
c.End = nil
- case a.End.Number >= b.End.Number:
+ case a.End.Number == b.End.Number:
+ if a.End.Part >= b.End.Part {
+ c.End = a.End
+ } else {
+ c.End = b.End
+ }
+ case a.End.Number > b.End.Number:
c.End = a.End
default:
c.End = b.End
return
}
+// Check if the given range is not nil, has a defined start
+// and, if it has an end, that the end ends after the start.
+func (er *EpisodeRange) Valid() bool {
+ switch {
+ case er == nil, er.Start == nil:
+ return false
+ case er.End == nil:
+ return true
+ case er.Start.Number < er.End.Number:
+ return true
+ case er.Start.Number > er.End.Number:
+ return false
+ case er.Start.Part <= er.End.Part:
+ return true
+ default:
+ return false
+ }
+}
+
+// Simplifies the Start/End ranges if one contains the other.
+// Sets the pointers to be identical if the range is modified.
+//
+// Modifies in-place, returns itself.
+func (er *EpisodeRange) Simplify() *EpisodeRange {
+ switch {
+ case er.Start.ContainsEpisodes(er.End):
+ er.End = er.Start
+ case er.End != nil && er.End.ContainsEpisodes(er.Start):
+ er.Start = er.End
+ }
+ return er
+}
+
+// Splits the range into one or two ranges, using the given
+// Episode as the split point. The Episode is not included in
+// the resulting ranges.
+func (er *EpisodeRange) Split(ep *Episode) []*EpisodeRange {
+ if !er.ContainsEpisodes(ep) { // implies same type
+ return []*EpisodeRange{er}
+ }
+ if !er.Valid() {
+ return []*EpisodeRange{nil, nil}
+ }
+
+ a := *er.Start
+
+ inf := er.End == nil
+ b := Episode{}
+ if !inf {
+ b = *er.End
+ }
+
+ end := &b
+ if inf {
+ end = nil
+ }
+
+ switch {
+ case a.ContainsEpisodes(ep) && b.ContainsEpisodes(ep):
+ return []*EpisodeRange{nil, nil}
+ case a.ContainsEpisodes(ep):
+ if ep.Part >= 0 {
+ a.Inc()
+ } else {
+ a.IncNumber()
+ }
+ if a.Number == b.Number && b.Parts > 0 {
+ a.Parts = b.Parts
+ }
+
+ r := &EpisodeRange{
+ Type: er.Type,
+ Start: &a,
+ End: end,
+ }
+ return []*EpisodeRange{nil, r.Simplify()}
+ case b.ContainsEpisodes(ep):
+ if ep.Part >= 0 {
+ b.Dec()
+ } else {
+ b.DecNumber()
+ }
+ if b.Number == a.Number {
+ if a.Parts > 0 {
+ b.Parts = a.Parts
+ b.Part = a.Parts - 1
+ } else if b.Part < 0 {
+ b.Part = a.Part
+ }
+ }
+ r := &EpisodeRange{
+ Type: er.Type,
+ Start: &a,
+ End: &b,
+ }
+ return []*EpisodeRange{r.Simplify(), nil}
+ default:
+ ra := &EpisodeRange{
+ Type: er.Type,
+ Start: &a,
+ End: ep,
+ }
+ rb := &EpisodeRange{
+ Type: er.Type,
+ Start: ep,
+ End: end,
+ }
+
+ ra = ra.Split(ep)[0]
+ rb = rb.Split(ep)[1]
+
+ if ra.Valid() {
+ ra.Simplify()
+ } else {
+ ra = nil
+ }
+ if rb.Valid() {
+ rb.Simplify()
+ } else {
+ rb = nil
+ }
+
+ return []*EpisodeRange{ra, rb}
+ }
+}
+
// Returns true if both ranges are of the same type and
// have identical start/end positions
func (a *EpisodeRange) Equals(b *EpisodeRange) bool {
}
if a.Type == b.Type {
- if a.End == b.End || (a.End != nil && b.End != nil && a.End.Number == b.End.Number) {
- if a.Start == b.Start || a.Start.Number == b.Start.Number {
+ if a.End == b.End || (a.End != nil && b.End != nil &&
+ a.End.Number == b.End.Number && a.End.Part == b.End.Part) {
+ if a.Start == b.Start || a.Start.Number == b.Start.Number && a.Start.Part == b.Start.Part {
return true
}
}
return false
}
+// CORNER CASE: e.g. 1.3,2.0 (or 1.3,2) always touch,
+// even if there's an unlisted 1.4 between them; unless
+// the part count is known.
func (a *EpisodeRange) touches(b *EpisodeRange) bool {
if a == nil || b == nil || a.Type != b.Type {
return false
}
switch {
+ case a == b:
+ // log.Println("same pointers")
+ return true
+ case a.Start == b.Start, a.End != nil && a.End == b.End:
+ // log.Println("share pointers")
+ return true
+
case a.End == nil:
switch {
case b.End == nil:
- // both infinite
+ // log.Println("both infinite")
return true
- case b.End.Number >= a.Start.Number-1:
- // {b [ } a ...
- // start-1 so it's still true when they're only adjacent
+ case b.End.Number == a.Start.Number:
+ switch {
+ // either is whole, or parts are adjacent/overlap
+ case b.End.Part == -1, a.Start.Part == -1,
+ b.End.Part >= a.Start.Part-1:
+ // log.Printf("{ %s [} %s ...", b.End, a.Start)
+ return true
+ }
+ // only if start of next range is whole or is first part
+ case b.End.Number == a.Start.Number-1 && a.Start.Part <= 0:
+ switch {
+ // end is whole, or is last part, or part count is unknown
+ case b.End.Part == -1, b.End.Parts == 0,
+ b.End.Part == b.End.Parts:
+ // log.Printf("{ %s }[ %s ...", b.End, a.Start)
+ return true
+ }
+ case b.End.Number > a.Start.Number:
+ // log.Printf("{ %s [ } %s ...", b.End, a.Start)
return true
}
case b.End == nil:
switch {
- case a.End.Number >= b.Start.Number-1:
- // [a { ] b ...
+ case a.End.Number == b.Start.Number:
+ switch {
+ case a.End.Part == -1, b.Start.Part == -1,
+ a.End.Part >= b.Start.Part-1:
+ // log.Printf("[ %s {] %s ...", a.End, b.Start)
+ return true
+ }
+ case a.End.Number == b.Start.Number-1 && b.Start.Part <= 0:
+ switch {
+ case a.End.Part == -1, a.End.Parts == 0,
+ a.End.Part == a.End.Parts:
+ // log.Printf("[ %s ]{ %s ...", a.End, b.Start)
+ return true
+ }
+ case a.End.Number > b.Start.Number:
+ // log.Printf("[ %s { ] %s ...", a.End, b.Start)
return true
}
- case a.Start.Number == b.Start.Number || a.End.Number == b.End.Number:
+ case a.Start.Number == b.Start.Number:
// touching
- return true
+ switch {
+ // either is whole, or parts are immediately adjacent
+ case a.Start.Part == -1, b.Start.Part == -1,
+ a.Start.Part == b.Start.Part,
+ a.Start.Part == b.Start.Part-1,
+ a.Start.Part == b.Start.Part+1:
+ // log.Printf("[{ %s - %s ]}", a.End, b.Start)
+ return true
+ }
+ case a.End.Number == b.End.Number:
+ switch {
+ case a.End.Part == -1, b.End.Part == -1,
+ a.End.Part == b.End.Part,
+ a.End.Part == b.End.Part-1,
+ a.End.Part == b.End.Part+1:
+ // log.Printf("{[ %s - %s }]", b.End, a.Start)
+ return true
+ }
case a.End.Number < b.End.Number:
switch {
- case a.End.Number >= b.Start.Number-1:
- // [a { ] b}
+ case a.End.Number == b.Start.Number:
+ switch {
+ case a.End.Part == -1, b.Start.Part == -1,
+ a.End.Part >= b.Start.Part-1:
+ // log.Printf("[ %s {] %s }", a.End, b.Start)
+ return true
+ }
+ case a.End.Number == b.Start.Number-1 && b.Start.Part <= 0:
+ switch {
+ case b.End.Part == -1, b.End.Parts == 0,
+ b.End.Part == b.End.Parts:
+ // log.Printf("[ %s ]{ %s }", a.End, b.Start)
+ return true
+ }
+ case a.End.Number > b.Start.Number:
+ // log.Printf("[ %s { ] %s }", a.End, b.Start)
return true
}
case b.End.Number < a.End.Number:
switch {
- case b.End.Number >= a.Start.Number-1:
- // {b [ } a]
+ case b.End.Number == a.Start.Number:
+ switch {
+ case b.End.Part == -1, a.Start.Part == -1,
+ b.End.Part >= a.Start.Part-1:
+ // log.Printf("{ %s [} %s ]", b.End, a.Start)
+ return true
+ }
+ case b.End.Number == a.Start.Number-1 && a.Start.Part <= 0:
+ switch {
+ case b.End.Part == -1, b.End.Parts == 0,
+ b.End.Part == b.End.Parts:
+ // log.Printf("{ %s }[ %s ]", b.End, a.Start)
+ return true
+ }
+ case b.End.Number > a.Start.Number:
+ // log.Printf("{ %s [ } %s ]", b.End, a.Start)
return true
}
}