Skip to content

Commit

Permalink
alfred workflow support (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
sjzar authored Oct 31, 2023
1 parent 659169c commit 79e28e4
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 34 deletions.
6 changes: 5 additions & 1 deletion cmd/ips/cmd_myip.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package ips

import (
"fmt"

"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -48,6 +50,8 @@ func init() {
myipCmd.Flags().StringVarP(&rootTextValuesSep, "text-values-sep", "", "", "Specify the separator for values in text output. (default is space)")
myipCmd.Flags().BoolVarP(&rootJson, "json", "j", false, "Output the results in JSON format.")
myipCmd.Flags().BoolVarP(&rootJsonIndent, "json-indent", "", false, "Output the results in indent JSON format.")
myipCmd.Flags().BoolVarP(&rootAlfred, "alfred", "", false, "Output the results in Alfred format.")

}

var myipCmd = &cobra.Command{
Expand All @@ -66,5 +70,5 @@ func MyIP(cmd *cobra.Command, args []string) {
if err != nil {
return
}
cmd.Println(ip)
fmt.Println(ip)
}
4 changes: 4 additions & 0 deletions cmd/ips/cmd_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func init() {
rootCmd.Flags().StringVarP(&rootTextValuesSep, "text-values-sep", "", "", "Specify the separator for values in text output. (default is space)")
rootCmd.Flags().BoolVarP(&rootJson, "json", "j", false, "Output the results in JSON format.")
rootCmd.Flags().BoolVarP(&rootJsonIndent, "json-indent", "", false, "Output the results in indent JSON format.")
rootCmd.Flags().BoolVarP(&rootAlfred, "alfred", "", false, "Output the results in Alfred format.")
}

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -101,6 +102,9 @@ func Root(cmd *cobra.Command, args []string) {
if err != nil {
log.Fatal(err)
}
if len(ret) == 0 {
continue
}
fmt.Println(ret)
}
return
Expand Down
7 changes: 7 additions & 0 deletions cmd/ips/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ var (
// rootJsonIndent defines whether to output in indented JSON format.
rootJsonIndent bool

// rootAlfred defines whether to output in Alfred format.
rootAlfred bool

// dump & pack command flags
// operate
// dpFields specifies the fields to output for dump and pack operations.
Expand Down Expand Up @@ -194,6 +197,10 @@ func GetFlagConfig() *ips.Config {
conf.JsonIndent = rootJsonIndent
}

if rootAlfred {
conf.OutputType = ips.OutputTypeAlfred
}

// dump & pack command flags
if len(dpFields) != 0 {
conf.DPFields = dpFields
Expand Down
3 changes: 3 additions & 0 deletions internal/ips/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const (
// OutputTypeJSON represents the JSON output format.
OutputTypeJSON = "json"

// OutputTypeAlfred represents the Alfred output format.
OutputTypeAlfred = "alfred"

// DefaultFields represents the default output fields.
DefaultFields = "country,province,city,isp"
)
Expand Down
90 changes: 57 additions & 33 deletions internal/ips/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,25 @@ import (
// based on the Manager configuration. It returns the combined result as a string.
func (m *Manager) ParseText(text string) (string, error) {

buf := &bytes.Buffer{}
tp := parser.NewTextParser(text).Parse()

infoList := make([]interface{}, 0, len(tp.Segments))
for _, segment := range tp.Segments {
info, err := m.parseSegment(segment)
if err != nil {
log.Debug("m.parseSegment error: ", err)
return "", err
}
result, err := m.serialize(segment, info)
if err != nil {
log.Debug("m.serialize error: ", err)
return "", err
}
buf.WriteString(result)
infoList = append(infoList, info)
}

result, err := m.serialize(infoList)
if err != nil {
log.Debug("m.serialize error: ", err)
return "", err
}

return buf.String(), nil
return result, nil
}

// parseSegment processes the provided segment and returns the corresponding data.
Expand Down Expand Up @@ -127,49 +128,72 @@ func (m *Manager) parseDomain(content string) (*model.DomainInfo, error) {

// serialize takes a segment and its associated data, then serializes the data
// based on the Manager configuration and returns the serialized string.
func (m *Manager) serialize(segment parser.Segment, data interface{}) (string, error) {
func (m *Manager) serialize(data []interface{}) (string, error) {
switch m.Conf.OutputType {
case OutputTypeJSON:
switch v := data.(type) {
case *model.IPInfo:
return m.serializeIPInfoToJSON(v)
case *model.DomainInfo:
case string:
return v, nil
list := &model.DataList{}
for _, info := range data {
switch v := info.(type) {
case *model.IPInfo:
list.AddItem(v.Output(m.Conf.UseDBFields))
case *model.DomainInfo:
case string:
continue
}
}
return m.serializeDataToJSON(list)
case OutputTypeAlfred:
list := &model.DataList{}
for _, info := range data {
switch v := info.(type) {
case *model.IPInfo:
list.AddAlfredItemByIPInfo(v)
case *model.DomainInfo:
case string:
continue
}
}
list.AddAlfredItemEmpty()
return m.serializeDataToJSON(list)
default:
// default is OutputTypeText
switch v := data.(type) {
case *model.IPInfo:
return m.serializeIPInfoToText(segment.Content, v)
case *model.DomainInfo:
case string:
return v, nil
buf := &bytes.Buffer{}
for _, info := range data {
switch v := info.(type) {
case *model.IPInfo:
ret, err := m.serializeIPInfoToText(v)
if err != nil {
return "", err
}
buf.WriteString(ret)
case *model.DomainInfo:
case string:
buf.WriteString(v)
}
}
return buf.String(), nil
}

// impossible
return "", nil
}

// serializeIPInfoToText takes an IPInfo and the original content, then serializes
// serializeIPInfoToText takes an IPInfo, then serializes
// the IPInfo to a text format based on the Manager configuration.
func (m *Manager) serializeIPInfoToText(content string, ipInfo *model.IPInfo) (string, error) {
func (m *Manager) serializeIPInfoToText(ipInfo *model.IPInfo) (string, error) {
values := strings.Join(util.DeleteEmptyValue(ipInfo.Values()), m.Conf.TextValuesSep)
if values != "" {
ret := strings.Replace(m.Conf.TextFormat, "%origin", content, 1)
ret := strings.Replace(m.Conf.TextFormat, "%origin", ipInfo.IP.String(), 1)
ret = strings.Replace(ret, "%values", values, 1)
return ret, nil
}

return content, nil
return ipInfo.IP.String(), nil
}

// serializeIPInfoToJSON serializes the provided IPInfo to a JSON format
// serializeDataToJSON serializes the provided DataList to a JSON format
// based on the Manager configuration. It returns the JSON string.
func (m *Manager) serializeIPInfoToJSON(ipInfo *model.IPInfo) (string, error) {
values := ipInfo.Output(m.Conf.UseDBFields)

func (m *Manager) serializeDataToJSON(values *model.DataList) (string, error) {
if len(values.Items) == 0 {
return "", nil
}
var ret []byte
var err error
if m.Conf.JsonIndent {
Expand All @@ -182,7 +206,7 @@ func (m *Manager) serializeIPInfoToJSON(ipInfo *model.IPInfo) (string, error) {
return "", err
}

return string(ret) + "\n", nil
return string(ret), nil
}

// createReader sets up and returns an IP reader based on the specified format and file.
Expand Down
92 changes: 92 additions & 0 deletions pkg/model/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2023 [email protected]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package model

import (
"fmt"
"strings"

"github.com/sjzar/ips/internal/util"
)

// AlfredItem represents an item to be displayed in Alfred's result list.
type AlfredItem struct {
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Arg string `json:"arg"`
Icon AlfredIcon `json:"icon"`
Valid bool `json:"valid"`
Text AlfredText `json:"text"`
}

// AlfredIcon represents the icon for an AlfredItem.
type AlfredIcon struct {
Type string `json:"type"`
Path string `json:"path"`
}

// AlfredText provides additional text information for an AlfredItem.
type AlfredText struct {
Copy string `json:"copy"`
}

// DataList holds a list of items to be displayed in Alfred's result list.
type DataList struct {
Items []interface{} `json:"items"`
}

// AddItem appends a new item to the DataList's Items slice.
func (d *DataList) AddItem(item interface{}) {
if d.Items == nil {
d.Items = make([]interface{}, 0)
}
d.Items = append(d.Items, item)
}

// AddAlfredItemByIPInfo creates an AlfredItem from the provided IPInfo
// and adds it to the DataList.
func (d *DataList) AddAlfredItemByIPInfo(info *IPInfo) {
values := strings.Join(util.DeleteEmptyValue(info.Values()), " ")
item := AlfredItem{
Title: fmt.Sprintf("%s [%s]", info.IP, values),
Subtitle: "Copy to clipboard",
Arg: values,
Icon: AlfredIcon{},
Valid: true,
Text: AlfredText{
Copy: values,
},
}
d.AddItem(item)
}

// AddAlfredItemEmpty adds a default "Not found" AlfredItem to the DataList
// if the list is empty.
func (d *DataList) AddAlfredItemEmpty() {
if len(d.Items) > 0 {
return
}
item := AlfredItem{
Title: "Not found",
Subtitle: "No information found",
Arg: "",
Icon: AlfredIcon{},
Valid: false,
Text: AlfredText{},
}
d.AddItem(item)
}
Binary file added script/alfredworkflow/ips.alfredworkflow
Binary file not shown.

0 comments on commit 79e28e4

Please sign in to comment.