├── .gitignore ├── README.md ├── alfred.go ├── cache.go └── example └── example.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-alfred 2 | 3 | Go-alfred is a utility library for quickly writing lightning fast [Alfred 2](http://www.alfredapp.com/) workflows using Golang. 4 | 5 | ## Example usage 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "os" 12 | "github.com/pascalw/go-alfred" 13 | "net/http" 14 | "io/ioutil" 15 | "encoding/json" 16 | ) 17 | 18 | func main() { 19 | queryTerms := os.Args[1:] 20 | 21 | // optimize query terms for fuzzy matching 22 | alfred.InitTerms(queryTerms) 23 | 24 | // create a new alfred workflow response 25 | response := alfred.NewResponse() 26 | repos := getRepos() 27 | 28 | for _, repo := range repos { 29 | // check if the repo name fuzzy matches the query terms 30 | if ! alfred.MatchesTerms(queryTerms, repo.Name) { continue } 31 | 32 | // it matched so add a new response item 33 | response.AddItem(&alfred.AlfredResponseItem{ 34 | Valid: true, 35 | Uid: repo.URL, 36 | Title: repo.Name, 37 | Arg: repo.URL, 38 | }) 39 | } 40 | 41 | // finally print the resulting Alfred Workflow XML 42 | response.Print() 43 | } 44 | ``` 45 | 46 | See [Example/](https://github.com/pascalw/go-alfred/blob/master/example/example.go) for details. 47 | -------------------------------------------------------------------------------- /alfred.go: -------------------------------------------------------------------------------- 1 | package alfred 2 | 3 | import ( 4 | "strings" 5 | "fmt" 6 | "encoding/xml" 7 | ) 8 | 9 | type AlfredResponse struct { 10 | Items []AlfredResponseItem 11 | XMLName struct{} `xml:"items"` 12 | } 13 | 14 | type AlfredResponseItem struct { 15 | Valid bool `xml:"valid,attr"` 16 | Arg string `xml:"arg,attr,omitempty"` 17 | Uid string `xml:"uid,attr,omitempty"` 18 | Title string `xml:"title"` 19 | Subtitle string `xml:"subtitle"` 20 | Icon string `xml:"icon"` 21 | 22 | XMLName struct{} `xml:"item"` 23 | } 24 | 25 | const xmlHeader = `` + "\n" 26 | 27 | func NewResponse() *AlfredResponse { 28 | return new(AlfredResponse).Init() 29 | } 30 | 31 | func (response *AlfredResponse) Init() *AlfredResponse { 32 | response.Items = []AlfredResponseItem{} 33 | return response 34 | } 35 | 36 | func (response *AlfredResponse) AddItem(item *AlfredResponseItem) { 37 | response.Items = append(response.Items, *item) 38 | } 39 | 40 | func (response *AlfredResponse) Print() { 41 | var xmlOutput, _ = xml.Marshal(response) 42 | fmt.Print(xmlHeader, string(xmlOutput)) 43 | } 44 | 45 | func InitTerms(params []string) { 46 | for index, term := range params { 47 | params[index] = strings.ToLower(term) 48 | } 49 | } 50 | 51 | func MatchesTerms(queryTerms []string, itemName string) bool { 52 | nameLower := strings.ToLower(itemName) 53 | 54 | for _, term := range queryTerms { 55 | if ! strings.Contains(nameLower, term) { return false } 56 | } 57 | 58 | return true 59 | } 60 | -------------------------------------------------------------------------------- /cache.go: -------------------------------------------------------------------------------- 1 | package alfred 2 | 3 | import ( 4 | "os" 5 | "encoding/json" 6 | "time" 7 | ) 8 | 9 | func WriteCache(obj interface{}) { 10 | cacheFile, err := os.Create("cache") 11 | if err != nil { panic(err) } 12 | 13 | defer cacheFile.Close() 14 | json.NewEncoder(cacheFile).Encode(obj) 15 | } 16 | 17 | func ReadCache(into interface {}, expireAfter time.Duration) ( err error) { 18 | cacheFile, err := os.Open("cache") 19 | if err != nil { return err } 20 | 21 | defer cacheFile.Close() 22 | 23 | fileInfo, _ := cacheFile.Stat() 24 | if time.Since(fileInfo.ModTime()) > expireAfter { 25 | return nil 26 | } 27 | 28 | json.NewDecoder(cacheFile).Decode(&into) 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "github.com/pascalw/go-alfred" 6 | "net/http" 7 | "io/ioutil" 8 | "encoding/json" 9 | ) 10 | 11 | func main() { 12 | queryTerms := os.Args[1:] 13 | 14 | // optimize query terms for fuzzy matching 15 | alfred.InitTerms(queryTerms) 16 | 17 | // create a new alfred workflow response 18 | response := alfred.NewResponse() 19 | repos := getRepos() 20 | 21 | for _, repo := range repos { 22 | // check if the repo name fuzzy matches the query terms 23 | if ! alfred.MatchesTerms(queryTerms, repo.Name) { continue } 24 | 25 | // it matched so add a new response item 26 | response.AddItem(&alfred.AlfredResponseItem{ 27 | Valid: true, 28 | Uid: repo.URL, 29 | Title: repo.Name, 30 | Arg: repo.URL, 31 | }) 32 | } 33 | 34 | // finally print the resulting Alfred Workflow XML 35 | response.Print() 36 | } 37 | 38 | type Repo struct { 39 | Name string 40 | URL string 41 | } 42 | 43 | func getRepos() []Repo { 44 | resp, err := http.Get("https://api.github.com/users/pascalw/repos") 45 | if err != nil { panic(err) } 46 | 47 | defer resp.Body.Close() 48 | body, _ := ioutil.ReadAll(resp.Body) 49 | 50 | var repos []Repo 51 | json.Unmarshal(body, &repos) 52 | 53 | return repos 54 | } 55 | --------------------------------------------------------------------------------