diff --git a/cmd/fdroidcl/installed.go b/cmd/fdroidcl/installed.go new file mode 100644 index 0000000..5c4657f --- /dev/null +++ b/cmd/fdroidcl/installed.go @@ -0,0 +1,66 @@ +/* Copyright (c) 2015, Daniel Martí */ +/* See LICENSE for licensing information */ + +package main + +import ( + "log" + + "github.com/mvdan/fdroidcl" + "github.com/mvdan/fdroidcl/adb" +) + +var cmdInstalled = &Command{ + Name: "installed", + Short: "List installed apps", +} + +func init() { + cmdInstalled.Run = runInstalled +} + +func runInstalled(args []string) { + index := mustLoadIndex() + startAdbIfNeeded() + device := oneDevice() + installed := mustInstalled(device) + apps := filterAppsInstalled(index.Apps, installed) + printApps(apps) +} + +func oneDevice() adb.Device { + devices, err := adb.Devices() + if err != nil { + log.Fatalf("Could not get devices: %v", err) + } + if len(devices) == 0 { + log.Fatalf("No devices found") + } + if len(devices) > 1 { + log.Fatalf("Too many devices found") + } + return devices[0] +} + +func mustInstalled(device adb.Device) []string { + installed, err := device.Installed() + if err != nil { + log.Fatalf("Could not get installed packages: %v", err) + } + return installed +} + +func filterAppsInstalled(apps []fdroidcl.App, installed []string) []fdroidcl.App { + instMap := make(map[string]struct{}, len(installed)) + for _, id := range installed { + instMap[id] = struct{}{} + } + var result []fdroidcl.App + for _, app := range apps { + if _, e := instMap[app.ID]; !e { + continue + } + result = append(result, app) + } + return result +} diff --git a/cmd/fdroidcl/list.go b/cmd/fdroidcl/list.go index 16b0e6e..ce60754 100644 --- a/cmd/fdroidcl/list.go +++ b/cmd/fdroidcl/list.go @@ -5,8 +5,10 @@ package main import ( "encoding/hex" + "fmt" "log" "os" + "strings" "github.com/mvdan/fdroidcl" ) @@ -57,3 +59,9 @@ func printApps(apps []fdroidcl.App) { printApp(app, maxIDLen) } } + +func printApp(app fdroidcl.App, IDLen int) { + fmt.Printf("%s%s %s %s\n", app.ID, strings.Repeat(" ", IDLen-len(app.ID)), + app.Name, app.CurApk.VName) + fmt.Printf(" %s\n", app.Summary) +} diff --git a/cmd/fdroidcl/main.go b/cmd/fdroidcl/main.go index 90ac7c4..2c4b24a 100644 --- a/cmd/fdroidcl/main.go +++ b/cmd/fdroidcl/main.go @@ -8,133 +8,8 @@ import ( "fmt" "log" "os" - "path/filepath" - "strings" - - "github.com/mvdan/appdir" - - "github.com/mvdan/fdroidcl" - "github.com/mvdan/fdroidcl/adb" ) -func appMatches(fields []string, terms []string) bool { -fieldLoop: - for _, field := range fields { - for _, term := range terms { - if !strings.Contains(field, term) { - continue fieldLoop - } - } - return true - } - return false -} - -func filterAppsSearch(apps []fdroidcl.App, terms []string) []fdroidcl.App { - for _, term := range terms { - term = strings.ToLower(term) - } - var result []fdroidcl.App - for _, app := range apps { - fields := []string{ - strings.ToLower(app.ID), - strings.ToLower(app.Name), - strings.ToLower(app.Summary), - strings.ToLower(app.Desc), - } - if !appMatches(fields, terms) { - continue - } - result = append(result, app) - } - return result -} - -func filterAppsInstalled(apps []fdroidcl.App, installed []string) []fdroidcl.App { - instMap := make(map[string]struct{}, len(installed)) - for _, id := range installed { - instMap[id] = struct{}{} - } - var result []fdroidcl.App - for _, app := range apps { - if _, e := instMap[app.ID]; !e { - continue - } - result = append(result, app) - } - return result -} - -func printApp(app fdroidcl.App, IDLen int) { - fmt.Printf("%s%s %s %s\n", app.ID, strings.Repeat(" ", IDLen-len(app.ID)), - app.Name, app.CurApk.VName) - fmt.Printf(" %s\n", app.Summary) -} - -func printAppDetailed(app fdroidcl.App) { - p := func(title string, format string, args ...interface{}) { - if format == "" { - fmt.Println(title) - } else { - fmt.Printf("%s %s\n", title, fmt.Sprintf(format, args...)) - } - } - p("Package :", "%s", app.ID) - p("Name :", "%s", app.Name) - p("Summary :", "%s", app.Summary) - p("Current Version :", "%s (%d)", app.CurApk.VName, app.CurApk.VCode) - p("Upstream Version :", "%s (%d)", app.CVName, app.CVCode) - p("License :", "%s", app.License) - if app.Categs != nil { - p("Categories :", "%s", strings.Join(app.Categs, ", ")) - } - if app.Website != "" { - p("Website :", "%s", app.Website) - } - if app.Source != "" { - p("Source :", "%s", app.Source) - } - if app.Tracker != "" { - p("Tracker :", "%s", app.Tracker) - } - if app.Changelog != "" { - p("Changelog :", "%s", app.Changelog) - } - if app.Donate != "" { - p("Donate :", "%s", app.Donate) - } - if app.Bitcoin != "" { - p("Bitcoin :", "bitcoin:%s", app.Bitcoin) - } - if app.Litecoin != "" { - p("Litecoin :", "litecoin:%s", app.Litecoin) - } - if app.Dogecoin != "" { - p("Dogecoin :", "dogecoin:%s", app.Dogecoin) - } - if app.FlattrID != "" { - p("Flattr :", "https://flattr.com/thing/%s", app.FlattrID) - } - fmt.Println() - p("Description :", "") - fmt.Println() - app.TextDesc(os.Stdout) - fmt.Println() - p("Available Versions :", "") - for _, apk := range app.Apks { - fmt.Println() - p(" Name :", "%s (%d)", apk.VName, apk.VCode) - p(" Size :", "%d", apk.Size) - p(" MinSdk :", "%d", apk.MinSdk) - if apk.MaxSdk > 0 { - p(" MaxSdk :", "%d", apk.MaxSdk) - } - if apk.ABIs != nil { - p(" ABIs :", "%s", strings.Join(apk.ABIs, ", ")) - } - } -} - func init() { flag.Usage = func() { fmt.Fprintln(os.Stderr, "Usage: fdroidcl [-h] []") @@ -149,44 +24,6 @@ func init() { } } -func appSubdir(appdir string) string { - p := filepath.Join(appdir, "fdroidcl") - if err := os.MkdirAll(p, 0755); err != nil { - log.Fatalf("Could not create app dir: %v", err) - } - return p -} - -func indexPath(name string) string { - cache, err := appdir.Cache() - if err != nil { - log.Fatalf("Could not determine cache dir: %v", err) - } - return filepath.Join(appSubdir(cache), repoName+".jar") -} - -func mustInstalled(device adb.Device) []string { - installed, err := device.Installed() - if err != nil { - log.Fatalf("Could not get installed packages: %v", err) - } - return installed -} - -func oneDevice() adb.Device { - devices, err := adb.Devices() - if err != nil { - log.Fatalf("Could not get devices: %v", err) - } - if len(devices) == 0 { - log.Fatalf("No devices found") - } - if len(devices) > 1 { - log.Fatalf("Too many devices found") - } - return devices[0] -} - // A Command is an implementation of a go command // like go build or go fix. type Command struct { @@ -208,6 +45,10 @@ type Command struct { var commands = []*Command{ cmdUpdate, cmdList, + cmdSearch, + cmdShow, + cmdDevices, + cmdInstalled, } func main() { @@ -230,43 +71,6 @@ func main() { } switch args[0] { - case "search": - args = args[1:] - index := mustLoadIndex() - apps := filterAppsSearch(index.Apps, args) - printApps(apps) - case "show": - args = args[1:] - index := mustLoadIndex() - found := make(map[string]*fdroidcl.App, len(args)) - for _, appID := range args { - found[appID] = nil - } - for i := range index.Apps { - app := &index.Apps[i] - _, e := found[app.ID] - if !e { - continue - } - found[app.ID] = app - } - for i, appID := range args { - app, _ := found[appID] - if app == nil { - log.Fatalf("Could not find app with ID '%s'", appID) - } - if i > 0 { - fmt.Printf("\n--\n\n") - } - printAppDetailed(*app) - } - case "installed": - index := mustLoadIndex() - startAdbIfNeeded() - device := oneDevice() - installed := mustInstalled(device) - apps := filterAppsInstalled(index.Apps, installed) - printApps(apps) default: log.Printf("Unrecognised command '%s'\n\n", args[0]) flag.Usage() diff --git a/cmd/fdroidcl/search.go b/cmd/fdroidcl/search.go new file mode 100644 index 0000000..57a1d29 --- /dev/null +++ b/cmd/fdroidcl/search.go @@ -0,0 +1,58 @@ +/* Copyright (c) 2015, Daniel Martí */ +/* See LICENSE for licensing information */ + +package main + +import ( + "strings" + + "github.com/mvdan/fdroidcl" +) + +var cmdSearch = &Command{ + Name: "search", + Short: "Search available apps", +} + +func init() { + cmdSearch.Run = runSearch +} + +func runSearch(args []string) { + index := mustLoadIndex() + apps := filterAppsSearch(index.Apps, args) + printApps(apps) +} + +func filterAppsSearch(apps []fdroidcl.App, terms []string) []fdroidcl.App { + for _, term := range terms { + term = strings.ToLower(term) + } + var result []fdroidcl.App + for _, app := range apps { + fields := []string{ + strings.ToLower(app.ID), + strings.ToLower(app.Name), + strings.ToLower(app.Summary), + strings.ToLower(app.Desc), + } + if !appMatches(fields, terms) { + continue + } + result = append(result, app) + } + return result +} + +func appMatches(fields []string, terms []string) bool { +fieldLoop: + for _, field := range fields { + for _, term := range terms { + if !strings.Contains(field, term) { + continue fieldLoop + } + } + return true + } + return false +} diff --git a/cmd/fdroidcl/show.go b/cmd/fdroidcl/show.go new file mode 100644 index 0000000..77deba8 --- /dev/null +++ b/cmd/fdroidcl/show.go @@ -0,0 +1,112 @@ +/* Copyright (c) 2015, Daniel Martí */ +/* See LICENSE for licensing information */ + +package main + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/mvdan/fdroidcl" +) + +var cmdShow = &Command{ + Name: "show", + Short: "Show detailed info of an app", +} + +func init() { + cmdShow.Run = runShow +} + +func runShow(args []string) { + index := mustLoadIndex() + found := make(map[string]*fdroidcl.App, len(args)) + for _, appID := range args { + found[appID] = nil + } + for i := range index.Apps { + app := &index.Apps[i] + _, e := found[app.ID] + if !e { + continue + } + found[app.ID] = app + } + for i, appID := range args { + app, _ := found[appID] + if app == nil { + log.Fatalf("Could not find app with ID '%s'", appID) + } + if i > 0 { + fmt.Printf("\n--\n\n") + } + printAppDetailed(*app) + } +} + +func printAppDetailed(app fdroidcl.App) { + p := func(title string, format string, args ...interface{}) { + if format == "" { + fmt.Println(title) + } else { + fmt.Printf("%s %s\n", title, fmt.Sprintf(format, args...)) + } + } + p("Package :", "%s", app.ID) + p("Name :", "%s", app.Name) + p("Summary :", "%s", app.Summary) + p("Current Version :", "%s (%d)", app.CurApk.VName, app.CurApk.VCode) + p("Upstream Version :", "%s (%d)", app.CVName, app.CVCode) + p("License :", "%s", app.License) + if app.Categs != nil { + p("Categories :", "%s", strings.Join(app.Categs, ", ")) + } + if app.Website != "" { + p("Website :", "%s", app.Website) + } + if app.Source != "" { + p("Source :", "%s", app.Source) + } + if app.Tracker != "" { + p("Tracker :", "%s", app.Tracker) + } + if app.Changelog != "" { + p("Changelog :", "%s", app.Changelog) + } + if app.Donate != "" { + p("Donate :", "%s", app.Donate) + } + if app.Bitcoin != "" { + p("Bitcoin :", "bitcoin:%s", app.Bitcoin) + } + if app.Litecoin != "" { + p("Litecoin :", "litecoin:%s", app.Litecoin) + } + if app.Dogecoin != "" { + p("Dogecoin :", "dogecoin:%s", app.Dogecoin) + } + if app.FlattrID != "" { + p("Flattr :", "https://flattr.com/thing/%s", app.FlattrID) + } + fmt.Println() + p("Description :", "") + fmt.Println() + app.TextDesc(os.Stdout) + fmt.Println() + p("Available Versions :", "") + for _, apk := range app.Apks { + fmt.Println() + p(" Name :", "%s (%d)", apk.VName, apk.VCode) + p(" Size :", "%d", apk.Size) + p(" MinSdk :", "%d", apk.MinSdk) + if apk.MaxSdk > 0 { + p(" MaxSdk :", "%d", apk.MaxSdk) + } + if apk.ABIs != nil { + p(" ABIs :", "%s", strings.Join(apk.ABIs, ", ")) + } + } +} diff --git a/cmd/fdroidcl/update.go b/cmd/fdroidcl/update.go index a63df30..1c565f3 100644 --- a/cmd/fdroidcl/update.go +++ b/cmd/fdroidcl/update.go @@ -6,6 +6,10 @@ package main import ( "fmt" "log" + "os" + "path/filepath" + + "github.com/mvdan/appdir" ) var cmdUpdate = &Command{ @@ -30,3 +34,19 @@ func updateIndex() error { } return nil } + +func indexPath(name string) string { + cache, err := appdir.Cache() + if err != nil { + log.Fatalf("Could not determine cache dir: %v", err) + } + return filepath.Join(appSubdir(cache), repoName+".jar") +} + +func appSubdir(appdir string) string { + p := filepath.Join(appdir, "fdroidcl") + if err := os.MkdirAll(p, 0755); err != nil { + log.Fatalf("Could not create app dir: %v", err) + } + return p +}