]> git.lizzy.rs Git - go-anidb.git/commitdiff
misc: Support partial episodes
authorDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Tue, 16 Jul 2013 16:52:51 +0000 (13:52 -0300)
committerDiogo Franco (Kovensky) <diogomfranco@gmail.com>
Tue, 16 Jul 2013 16:52:51 +0000 (13:52 -0300)
misc/episode.go
misc/episode_test.go
misc/episodelist_test.go
misc/episoderange.go
misc/episoderange_test.go

index 0530f55874f7e1b5f85644193aaf57041498bad4..72140d4abb602967f7ba90fe6e3d3c3d34a27469 100644 (file)
@@ -73,6 +73,10 @@ func (et EpisodeType) String() string {
 type Episode struct {
        Type   EpisodeType
        Number int
+       Part   int
+       Parts  int
+}
+
 // returns how many digits are needed to represent this int
 func scale(i int) int {
        return 1 + int(math.Floor(math.Log10(float64(i))))
@@ -97,7 +101,14 @@ func (ep *Episode) scale() int {
 func (ep *Episode) ContainsEpisodes(ec EpisodeContainer) bool {
        switch e := ec.(type) {
        case *Episode:
-               return ep != nil && ep.Type == e.Type && ep.Number == e.Number
+               if ep == nil {
+                       return false
+               }
+               basic := ep.Type == e.Type && ep.Number == e.Number
+               if ep.Part < 0 { // a whole episode contains any partial episodes
+                       return basic
+               }
+               return basic && ep.Part == e.Part
        case *EpisodeRange:
        case *EpisodeList:
                return EpisodeList{&EpisodeRange{Type: ep.Type, Start: ep, End: ep}}.ContainsEpisodes(ep)
@@ -107,7 +118,16 @@ func (ep *Episode) ContainsEpisodes(ec EpisodeContainer) bool {
 }
 
 func (ep *Episode) Format(width int) string {
-       return fmt.Sprintf("%s%0"+strconv.Itoa(width)+"d", ep.Type, ep.Number)
+       if ep.Part < 0 { // whole episode
+               return fmt.Sprintf("%s%0"+strconv.Itoa(width)+"d", ep.Type, ep.Number)
+       }
+       if ep.Parts != 0 { // part X of Y
+               frac := float64(ep.Number) + float64(ep.Part)/float64(ep.Parts)
+
+               return fmt.Sprintf("%s%0"+strconv.Itoa(width)+".2f", ep.Type, frac)
+       }
+       // part N
+       return fmt.Sprintf("%s%0"+strconv.Itoa(width)+"d.%d", ep.Type, ep.Number, ep.Part)
 }
 
 func (ep *Episode) FormatLog(max int) string {
@@ -117,12 +137,24 @@ func (ep *Episode) FormatLog(max int) string {
 // Parses a string in the usual AniDB API episode format and converts into
 // an Episode.
 func ParseEpisode(s string) *Episode {
+       p := int64(-1)
+
+       parts := strings.Split(s, ".")
+       switch len(parts) {
+       case 1: // no worries
+       case 2:
+               s = parts[0]
+               p, _ = strconv.ParseInt(parts[1], 10, 32)
+       default: // too many dots
+               return nil
+       }
+
        if no, err := strconv.ParseInt(s, 10, 32); err == nil {
-               return &Episode{Type: EpisodeTypeRegular, Number: int(no)}
+               return &Episode{Type: EpisodeTypeRegular, Number: int(no), Part: int(p)}
        } else if len(s) < 1 {
                // s too short
        } else if no, err = strconv.ParseInt(s[1:], 10, 30); err == nil {
-               return &Episode{Type: parseEpisodeType(s[:1]), Number: int(no)}
+               return &Episode{Type: parseEpisodeType(s[:1]), Number: int(no), Part: int(p)}
        }
        return nil
 }
index 373e805e734dcafeb5392c22cd30192c96f4cf2e..33116fc5350b16de2592b673467342ad137c7bc1 100644 (file)
@@ -12,21 +12,12 @@ func ExampleParseEpisode() {
        fmt.Printf("%#v\n", misc.ParseEpisode("")) // invalid episode
 
        // Output:
-       // &misc.Episode{Type:1, Number:1}
-       // &misc.Episode{Type:2, Number:2}
-       // &misc.Episode{Type:1, Number:3}
+       // &misc.Episode{Type:1, Number:1, Part:-1, Parts:0}
+       // &misc.Episode{Type:2, Number:2, Part:-1, Parts:0}
+       // &misc.Episode{Type:1, Number:3, Part:-1, Parts:0}
        // (*misc.Episode)(nil)
 }
 
-//     ParseEpisodeRange("1")     <=> ep := ParseEpisode("1");
-//             &EpisodeRange{Type: EpisodeTypeRegular, Start: ep, End: ep}
-//     ParseEpisodeRange("S1-")   <=>
-//             &EpisodeRange{Type: EpisodeTypeSpecial, Start: ParseEpisode("S1")}
-//     ParseEpisodeRange("T1-T3") <=>
-//             &EpisodeRange{Type: EpisodeTypeTrailer, Start: ParseEpisode("T1"), End: ParseEpisode("T3")}
-//     ParseEpisodeRange("5-S3")  <=> nil // different episode types in range
-//     ParseEpisodeRange("")      <=> nil // invalid start of range
-
 func ExampleParseEpisodeRange() {
        fmt.Println(misc.ParseEpisodeRange("01"))
        fmt.Println(misc.ParseEpisodeRange("S1-")) // endless range
@@ -41,3 +32,23 @@ func ExampleParseEpisodeRange() {
        // <nil>
        // <nil>
 }
+
+func ExamplePartialEpisode() {
+       eps := []*misc.Episode{
+               misc.ParseEpisode("1.0"),
+               misc.ParseEpisode("1.1"),
+       }
+       for _, ep := range eps {
+               fmt.Printf("%#v %s\n", ep, ep)
+       }
+       for _, ep := range eps {
+               ep.Parts = 2
+               fmt.Printf("%s\n", ep)
+       }
+
+       // Output:
+       // &misc.Episode{Type:1, Number:1, Part:0, Parts:0} 1.0
+       // &misc.Episode{Type:1, Number:1, Part:1, Parts:0} 1.1
+       // 1.00
+       // 1.50
+}
index 753a4daace61e80ef4d24adedb3e74fc3df54e31..a7567469e756badf55af9216e392ec81c8d59ca4 100644 (file)
@@ -5,27 +5,9 @@ import (
        "github.com/Kovensky/go-anidb/misc"
 )
 
-func ExampleEpisodeRange_Merge() {
-       a := misc.ParseEpisodeRange("5-7")
-       b := misc.ParseEpisodeRange("8-12")
-       fmt.Println(a.Merge(b)) // 5-7 + 8-12
+func ExampleEpisodeList_Simplify() {
+       a := misc.ParseEpisodeList("1,2,3,5,10-14,13-15,,S3-S6,C7-C10,S1,S7,S8-")
+       fmt.Println(a.Simplify())
 
-       b = misc.ParseEpisodeRange("3-6")
-       fmt.Println(a.Merge(b)) // 5-7 + 3-6
-
-       b = misc.ParseEpisodeRange("10-12")
-       fmt.Println(a.Merge(b)) // 5-7 + 10-12 (invalid, not touching)
-
-       b = misc.ParseEpisodeRange("S1-S3")
-       fmt.Println(a.Merge(b)) // 5-7 + S1-S3 (invalid, different types)
-
-       a = misc.ParseEpisodeRange("S3-S10")
-       fmt.Println(a.Merge(b)) // S3-S10 + S1-S3
-
-       // Output:
-       // 05-12
-       // 3-7
-       // <nil>
-       // <nil>
-       // S01-S10
+       // Output: 01-03,05,10-15,S1,S3-,C07-C10
 }
index fbc5cd35d6e98977568370f11ce69582ea6be0bb..0f11b0cbc671037b7d236923cd95e3ce28394f8d 100644 (file)
@@ -106,7 +106,13 @@ func (a *EpisodeRange) Merge(b *EpisodeRange) (c *EpisodeRange) {
        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
@@ -115,7 +121,13 @@ func (a *EpisodeRange) Merge(b *EpisodeRange) (c *EpisodeRange) {
                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
@@ -135,8 +147,9 @@ 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
                        }
                }
@@ -144,46 +157,131 @@ func (a *EpisodeRange) Equals(b *EpisodeRange) bool {
        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
                }
        }
index a7567469e756badf55af9216e392ec81c8d59ca4..df6726865b09e4dd49bedfce7da68acda30c260c 100644 (file)
@@ -5,9 +5,48 @@ import (
        "github.com/Kovensky/go-anidb/misc"
 )
 
-func ExampleEpisodeList_Simplify() {
-       a := misc.ParseEpisodeList("1,2,3,5,10-14,13-15,,S3-S6,C7-C10,S1,S7,S8-")
-       fmt.Println(a.Simplify())
+func ExampleEpisodeRange_Merge() {
+       a := misc.ParseEpisodeRange("5-7")
+       b := misc.ParseEpisodeRange("8-12")
+       fmt.Println(a.Merge(b)) // 5-7 + 8-12
 
-       // Output: 01-03,05,10-15,S1,S3-,C07-C10
+       b = misc.ParseEpisodeRange("3-6")
+       fmt.Println(a.Merge(b)) // 5-7 + 3-6
+
+       b = misc.ParseEpisodeRange("10-12")
+       fmt.Println(a.Merge(b)) // 5-7 + 10-12 (invalid, not touching)
+
+       b = misc.ParseEpisodeRange("S1-S3")
+       fmt.Println(a.Merge(b)) // 5-7 + S1-S3 (invalid, different types)
+
+       a = misc.ParseEpisodeRange("S3-S10")
+       fmt.Println(a.Merge(b)) // S3-S10 + S1-S3
+
+       // Output:
+       // 05-12
+       // 3-7
+       // <nil>
+       // <nil>
+       // S01-S10
+}
+
+func ExampleEpisodeRange_PartialMerge() {
+       a := misc.ParseEpisodeRange("2.1-2.3")
+       b := misc.ParseEpisodeRange("3.0")
+       fmt.Println(a.Merge(b)) // 2.1-2.3 + 3.0
+
+       b = misc.ParseEpisodeRange("3.1")
+       fmt.Println(a.Merge(b)) // 2.1-2.3 + 3.1
+
+       b = misc.ParseEpisodeRange("1")
+       fmt.Println(a.Merge(b)) // 2.1-2.3 + 1
+
+       a = misc.ParseEpisodeRange("2.0-2.3")
+       fmt.Println(a.Merge(b)) // 2.0-2.3 + 1
+
+       // Output:
+       // 2.1-3.0
+       // <nil>
+       // <nil>
+       // 1-2.3
 }