func usage() { pef("goim is a tool for interacting with a local copy of IMDB.\n") pef("Usage:\n\n goim {command} [flags] [arguments]\n") pef("Use 'goim help {command}' for more details on {command}.\n") fun.Sort(func(c1, c2 *command) bool { return c1.name < c2.name }, commands) pef("A list of the main commands:\n") tabw := tabwriter.NewWriter(os.Stderr, 0, 0, 4, ' ', 0) for _, c := range commands { if c.name == "ftp" || c.other { continue } fmt.Fprintf(tabw, " %s\t%s\n", c.name, c.shortHelp) } tabw.Flush() pef("") pef("A list of other commands:\n") for _, c := range commands { if c.name == "ftp" || !c.other { continue } fmt.Fprintf(tabw, " %s\t%s\n", c.name, c.shortHelp) } tabw.Flush() pef("") os.Exit(1) }
func init() { less := func(f1, f2 string) bool { return f1 < f2 } fields := fun.QuickSort(less, fun.Keys(qualifiedColumns)).([]string) sortFields := strings.Join(fields, ", ") genres := strings.Join(imdb.EnumGenres, ", ") mpaas := strings.Join(imdb.EnumMPAA, ", ") commands = []command{ { "movie", nil, false, "Restricts results to only include movies. Note that this may " + "be combined with other entity types to form a disjunction.", func(s *Searcher, v string) error { s.Entity(imdb.EntityMovie) return nil }, }, { "tvshow", nil, false, "Restricts results to only include TV shows. Note that this may " + "be combined with other entity types to form a disjunction.", func(s *Searcher, v string) error { s.Entity(imdb.EntityTvshow) return nil }, }, { "episode", nil, false, "Restricts results to only include episodes. Note that this may " + "be combined with other entity types to form a disjunction.", func(s *Searcher, v string) error { s.Entity(imdb.EntityEpisode) return nil }, }, { "actor", nil, false, "Restricts results to only include actors. Note that this may " + "be combined with other entity types to form a disjunction.", func(s *Searcher, v string) error { s.Entity(imdb.EntityActor) return nil }, }, { "genre", nil, true, "Restricts results to only include entities matching the genre " + "given. Multiple genres will be combined disjunctively. " + "Available genres: " + genres, func(s *Searcher, v string) error { s.Genre(v) return nil }, }, { "mpaa", nil, true, "Restricts results to only include entities with the MPAA rating " + "given. Multiple MPAA ratings will be combined " + "disjunctively. Available MPAA ratings: " + mpaas, func(s *Searcher, v string) error { s.MPAA(v) return nil }, }, { "credits", nil, true, "A sub-search for media entities that restricts results to " + "only actors media item returned from this sub-search.", func(s *Searcher, v string) error { return addSub(s, "credits", v, s.Credits) }, }, { "cast", nil, true, "A sub-search for cast entities that restricts results to " + "only media entities in which the cast member appeared.", func(s *Searcher, v string) error { return addSub(s, "cast", v, s.Cast) }, }, { "show", nil, true, "A sub-search for TV shows that restricts results to " + "only episodes in the TV show.", func(s *Searcher, v string) error { return addSub(s, "show", v, s.Tvshow) }, }, { "debug", nil, false, "When enabled, the SQL queries used in the search will be logged " + "to stderr.", func(s *Searcher, v string) error { s.debug = true return nil }, }, { "id", []string{"atom"}, true, "Precisely selects a single identity with the atom identifier " + "given. e.g., {id:123} returns the entity with id 123." + "Note that one SHOULD NOT rely on any specific atom " + "identifier to always correspond to a specific entity, since " + "identifiers can (sadly) change when updating your database.", func(s *Searcher, v string) error { n, err := strconv.Atoi(v) if err != nil { return ef("Invalid integer '%s' for atom id: %s", v, err) } s.Atom(imdb.Atom(n)) return nil }, }, { "years", []string{"year"}, true, "Only show search results for the year or years specified. " + "e.g., {1990-1999} only shows movies in the 90s.", func(s *Searcher, v string) error { return addRange(v, s.Years) }, }, { "rank", nil, true, "Only show search results with the rank or ranks specified. " + "e.g., {70-} only shows entities with a rank of 70 or " + "better. Ranks are on a scale of 0 to 100, where 100 is the " + "best.", func(s *Searcher, v string) error { return addRange(v, s.Ranks) }, }, { "votes", nil, true, "Only show search results with ranks that have the vote count " + "specified. e.g., {10000-} only shows entities with a rank " + "that has 10,000 or more votes.", func(s *Searcher, v string) error { return addRange(v, s.Votes) }, }, { "billing", []string{"billed"}, true, "Only show search results with credits with the billing position " + "specified. e.g., {1-5} only shows movies where the actor " + "was in the top 5 billing order (or only shows actors of a " + "movie in the top 5 billing positions).", func(s *Searcher, v string) error { return addRange(v, s.Billed) }, }, { "seasons", []string{"s"}, true, "Only show search results for the season or seasons specified. " + "e.g., {seasons:1} only shows episodes from the first season " + "of a TV show. Note that this only filters episodes---movies " + "and TV shows are still returned otherwise.", func(s *Searcher, v string) error { return addRange(v, s.Seasons) }, }, { "episodes", []string{"e"}, true, "Only show search results for the season or seasons specified. " + "e.g., {episodes:1-5} only shows the first five episodes of " + "a of a season. Note that this only filters " + "episodes---movies and TV shows are still returned otherwise.", func(s *Searcher, v string) error { return addRange(v, s.Episodes) }, }, { "notv", nil, false, "Removes 'made for TV' movies from the search results.", func(s *Searcher, v string) error { s.NoTvMovies() return nil }, }, { "novideo", nil, false, "Removes 'made for video' movies from the search results.", func(s *Searcher, v string) error { s.NoVideoMovies() return nil }, }, { "similar", nil, true, "Sets the threshold at which to return results from a fuzzy text " + "search. Results scoring below this threshold are omitted. " + "Note that setting this value too low can dramatically " + "increase the search time.", func(s *Searcher, v string) error { n, err := strconv.ParseFloat(v, 64) if err != nil { return ef("Invalid float '%s' for similar: %s", v, err) } s.SimilarThreshold(n) return nil }, }, { "limit", nil, true, "Specifies a limit on the total number of search results returned.", func(s *Searcher, v string) error { n, err := strconv.Atoi(v) if err != nil { return ef("Invalid integer '%s' for limit: %s", v, err) } s.Limit(int(n)) return nil }, }, { "sort", nil, true, "Sorts the search results according to the field given. It may " + "be specified multiple times for more specific sorting. Note " + "that this doesn't really work with fuzzy searching, since " + "results are always sorted by their similarity with the " + "query in a fuzzy search. e.g., {sort:episode desc} sorts " + "episode in descending (biggest to smallest) order. " + "Valid sort fields: " + sortFields + ".", func(s *Searcher, v string) error { fields := strings.Fields(v) if len(fields) != 2 { return ef("Invalid sort format "+ "(must have field and order): '%s'", v) } s.Sort(fields[0], fields[1]) return nil }, }, } // Add synonyms of commands to the map of commands. for _, cmd := range commands { allCommands[cmd.name] = cmd for _, synonym := range cmd.synonyms { allCommands[synonym] = cmd } Commands = append(Commands, Command{ Name: cmd.name, Synonyms: cmd.synonyms, Description: cmd.description, }) } fun.Sort(func(c1, c2 Command) bool { return c1.Name < c2.Name }, Commands) }