8 type canInfinite interface {
13 // A range of episodes with a start and possibly without an end.
14 type EpisodeRange struct {
15 Type EpisodeType // Must be equal to both the Start and End types; if End is nil, must be equal to the Start type
16 Start *Episode // The start of the range
17 End *Episode // The end of the range; may be nil, which represents an endless range
20 func EpisodeToRange(ep *Episode) *EpisodeRange {
28 // Converts the EpisodeRange into AniDB API range format.
29 func (er *EpisodeRange) String() string {
30 return er.Format(er.scale())
33 func (er *EpisodeRange) Format(width int) string {
34 if er.Start == er.End || (er.End != nil && *(er.Start) == *(er.End)) {
35 return er.Start.Format(width)
39 return fmt.Sprintf("%s-", er.Start.Format(width))
41 return fmt.Sprintf("%s-%s", er.Start.Format(width), er.End.Format(width))
44 func (er *EpisodeRange) FormatLog(max int) string {
45 return er.Format(scale(max))
48 func (er *EpisodeRange) scale() int {
52 s, e := er.Start.scale(), er.End.scale()
59 func (er *EpisodeRange) Infinite() bool {
60 return er != nil && er.End == nil
63 // Returns the number of episodes that Episodes() would return.
65 // Returns -1 for infinite ranges.
66 func (er *EpisodeRange) Len() int {
72 if er.Start.Parts > 0 {
73 c += er.Start.Parts - er.Start.Part
75 c += 1 + er.End.Number - er.Start.Number
83 // Returns a channel that can be used to iterate using for/range.
85 // If the EpisodeRange is infinite, then the channel is also infinite.
86 // The caller is allowed to close the channel in such case.
87 func (er *EpisodeRange) Episodes() chan Episode {
88 ch := make(chan Episode, 1)
105 // we allow the caller to close the channel on infinite lists
106 defer func() { recover(); abort = true }()
115 for ; !abort && ep.Parts > 0 && ep.Number == start.Number; ep.IncPart() {
118 for ; !abort; ep.IncNumber() {
121 case start.Part == -1 && end.Part == -1:
122 for ; ep.Number <= end.Number; ep.IncNumber() {
125 case start.Parts > 0:
126 for ; ep.Number == start.Number; ep.IncPart() {
131 for ; ep.Number < end.Number; ep.IncNumber() {
137 for ; ep.Part <= end.Part; ep.IncPart() {
145 // If ec is an *Episode, returns true if the Episode is of the same type as the range
146 // and has a Number >= Start.Number; if End is defined, then the episode's Number must
147 // also be <= End.Number.
149 // If ec is an *EpisodeRange, returns true if they are both of the same type and
150 // the ec's Start.Number is >= this range's Start.Number;
151 // also returns true if this EpisodeRange is unbounded or if the ec is bounded
152 // and ec's End.Number is <= this range's End.Number.
154 // If ec is an EpisodeList, returns true if all listed EpisodeRanges are contained
155 // by this EpisodeRange.
157 // Returns false otherwise.
158 func (er *EpisodeRange) ContainsEpisodes(ec EpisodeContainer) bool {
163 switch e := ec.(type) {
165 if e.Type == er.Type && e.Number >= er.Start.Number {
168 } else if e.Number <= er.End.Number {
173 if e.Type == er.Type {
174 if e.Start.Number >= er.Start.Number {
177 } else if e.End == nil {
178 return false // a finite set can't contain an infinite one
179 } else if e.End.Number <= er.End.Number {
185 for _, ec := range e {
186 if !er.ContainsEpisodes(ec) {
196 // Tries to merge a with b, returning a new *EpisodeRange that's
197 // a superset of both a and b.
199 // Returns nil if a and b don't intersect, or are not adjacent.
200 func (a *EpisodeRange) Merge(b *EpisodeRange) (c *EpisodeRange) {
202 c = &EpisodeRange{Type: a.Type}
205 case a.Start.Number == b.Start.Number:
207 case a.Start.Part < 0:
209 case b.Start.Part < 0:
211 case a.Start.Part <= b.Start.Part:
216 case a.Start.Number < b.Start.Number:
223 case a.End == nil || b.End == nil:
225 case a.End.Number == b.End.Number:
231 case a.End.Part >= b.End.Part:
236 case a.End.Number > b.End.Number:
245 // Check if the given range is not nil, has a defined start
246 // and, if it has an end, that the end ends after the start.
247 func (er *EpisodeRange) Valid() bool {
249 case er == nil, er.Start == nil:
253 case er.Start.Number < er.End.Number:
255 case er.Start.Number > er.End.Number:
257 case er.Start.Part <= er.End.Part:
264 // Simplifies the Start/End ranges if one contains the other.
265 // Sets the pointers to be identical if the range is modified.
267 // Modifies in-place, returns itself.
268 func (er *EpisodeRange) Simplify() *EpisodeRange {
270 case er.Start.ContainsEpisodes(er.End):
272 case er.End != nil && er.End.ContainsEpisodes(er.Start):
278 // Splits the range into one or two ranges, using the given
279 // Episode as the split point. The Episode is not included in
280 // the resulting ranges.
281 func (er *EpisodeRange) Split(ep *Episode) []*EpisodeRange {
283 return []*EpisodeRange{nil, nil}
285 if !er.ContainsEpisodes(ep) { // implies same type
286 return []*EpisodeRange{er}
303 case a.ContainsEpisodes(ep) && b.ContainsEpisodes(ep):
304 return []*EpisodeRange{nil, nil}
305 case a.ContainsEpisodes(ep):
311 if a.Number == b.Number && b.Parts > 0 {
320 return []*EpisodeRange{nil, r.Simplify()}
321 case b.ContainsEpisodes(ep):
327 if b.Number == a.Number {
331 } else if b.Part < 0 {
340 return []*EpisodeRange{r.Simplify(), nil}
367 return []*EpisodeRange{ra, rb}
371 // Returns true if both ranges are of the same type and
372 // have identical start/end positions
373 func (a *EpisodeRange) Equals(b *EpisodeRange) bool {
374 if a == b { // pointers to the same thing
377 if a == nil || b == nil {
381 if a.Type == b.Type {
382 if a.End == b.End || (a.End != nil && b.End != nil &&
383 a.End.Number == b.End.Number && a.End.Part == b.End.Part) {
384 if a.Start == b.Start || a.Start.Number == b.Start.Number && a.Start.Part == b.Start.Part {
392 // CORNER CASE: e.g. 1.3,2.0 (or 1.3,2) never touch,
393 // unless it's known that 1.3 is the last part.
394 func (a *EpisodeRange) touches(b *EpisodeRange) bool {
395 if a == nil || b == nil || a.Type != b.Type {
401 // log.Println("same pointers")
403 case a.Start == b.Start, a.End != nil && a.End == b.End:
404 // log.Println("share pointers")
410 // log.Println("both infinite")
413 case b.End.Number == a.Start.Number:
415 // either is whole, or parts are adjacent/overlap
416 case b.End.Part == -1, a.Start.Part == -1,
417 b.End.Part >= a.Start.Part-1:
418 // log.Printf("{ %s [} %s ...", b.End, a.Start)
421 // only if start of next range is whole or is first part
422 case b.End.Number == a.Start.Number-1 && a.Start.Part <= 0:
424 // end is whole, or is last part, or part count is unknown
425 case b.End.Part == -1, b.End.Parts == 0,
426 b.End.Part == b.End.Parts:
427 // log.Printf("{ %s }[ %s ...", b.End, a.Start)
430 case b.End.Number > a.Start.Number:
431 // log.Printf("{ %s [ } %s ...", b.End, a.Start)
437 case a.End.Number == b.Start.Number:
439 case a.End.Part == -1, b.Start.Part == -1,
440 a.End.Part >= b.Start.Part-1:
441 // log.Printf("[ %s {] %s ...", a.End, b.Start)
444 case a.End.Number == b.Start.Number-1 && b.Start.Part <= 0:
446 case a.End.Part == -1, a.End.Parts == 0,
447 a.End.Part == a.End.Parts:
448 // log.Printf("[ %s ]{ %s ...", a.End, b.Start)
451 case a.End.Number > b.Start.Number:
452 // log.Printf("[ %s { ] %s ...", a.End, b.Start)
456 case a.Start.Number == b.Start.Number:
459 // either is whole, or parts are immediately adjacent
460 case a.Start.Part == -1, b.Start.Part == -1,
461 a.Start.Part == b.Start.Part,
462 a.Start.Part == b.Start.Part-1,
463 a.Start.Part == b.Start.Part+1:
464 // log.Printf("[{ %s - %s ]}", a.End, b.Start)
467 case a.End.Number == b.End.Number:
469 case a.End.Part == -1, b.End.Part == -1,
470 a.End.Part == b.End.Part,
471 a.End.Part == b.End.Part-1,
472 a.End.Part == b.End.Part+1:
473 // log.Printf("{[ %s - %s }]", b.End, a.Start)
477 case a.End.Number < b.End.Number:
479 case a.End.Number == b.Start.Number:
481 case a.End.Part == -1, b.Start.Part == -1,
482 a.End.Part >= b.Start.Part-1:
483 // log.Printf("[ %s {] %s }", a.End, b.Start)
486 case a.End.Number == b.Start.Number-1 && b.Start.Part <= 0:
488 case a.End.Part == -1, a.End.Part == a.End.Parts-1,
489 a.End.Part == b.Start.Parts:
490 // log.Printf("[ %s ]{ %s }", a.End, b.Start)
493 case a.End.Number > b.Start.Number:
494 // log.Printf("[ %s { ] %s }", a.End, b.Start)
498 case b.End.Number < a.End.Number:
500 case b.End.Number == a.Start.Number:
502 case b.End.Part == -1, a.Start.Part == -1,
503 b.End.Part >= a.Start.Part-1:
504 // log.Printf("{ %s [} %s ]", b.End, a.Start)
507 case b.End.Number == a.Start.Number-1 && a.Start.Part <= 0:
509 case b.End.Part == -1, b.End.Part == b.End.Parts-1,
510 b.End.Part == b.End.Parts:
511 // log.Printf("{ %s }[ %s ]", b.End, a.Start)
514 case b.End.Number > a.Start.Number:
515 // log.Printf("{ %s [ } %s ]", b.End, a.Start)
522 // Parses a string in the AniDB API range format and converts into an EpisodeRange.
523 func ParseEpisodeRange(s string) *EpisodeRange {
524 parts := strings.Split(s, "-")
530 for i := range parts {
531 eps[i] = ParseEpisode(parts[i])
537 // Not an interval (just "epno") --
538 // convert into interval starting and ending in the same episode
543 if len(parts) > 1 && eps[1] != nil && eps[0].Type != eps[1].Type {
546 return &EpisodeRange{