├── .gitignore ├── LICENSE ├── README.md ├── build_mac.sh ├── build_win.sh ├── go.mod ├── go.sum ├── main.go ├── pipe ├── column2row.go ├── field.go ├── fmtrow.go ├── join.go ├── line.go ├── match.go ├── pipe.go ├── registry.go ├── replace.go ├── rsplit.go ├── surround.go ├── table.go └── trim.go ├── resource ├── pipeline.icns ├── pipeline.ico └── pipeline.png └── screenshot ├── addquotation.gif ├── commatolines.gif ├── findimageurl.gif └── screenshot1.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | build/ 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Allen Dang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PipeIt 2 | 3 | PipeIt is a text transformation, conversion, cleansing and extraction tool. 4 | 5 | PipeIt screen shot1 6 | 7 | # Features 8 | 9 | - Split - split text to text array by given separator. 10 | - RegexpSplit - split text to text array by given regexp expression. 11 | - Fields - Fields splits the string s around each instance of one or more consecutive white space characters. 12 | - Match - filter text array by regexp. 13 | - Replace - replace each element of a text array. 14 | - Surround - add prefix or suffix to each lement of a text array. 15 | - Trim - Trim returns a slice of the string s with all leading and trailing Unicode code points contained in cutset removed. 16 | - Join - join text array to single line of text by given separator. 17 | - Line - output text array line by line. 18 | 19 | And more pipes are comming... 20 | 21 | (More important, tell me your case will help me to create more pipes which will actually useful.) 22 | 23 | PipeIt also supports to read from Stdin, so you could pipe data using "cat file | PipeIt". 24 | 25 | # Usage 26 | 27 | ## Extract image links from a html source 28 | 29 | PipeIt demo to find image urls from html 30 | 31 | ## Add single quotation mark to every words 32 | 33 | PipeIt demo to add single quotation 34 | 35 | ## Replace the comma separated string to lines 36 | 37 | PipeIt demo to replace comma 38 | 39 | # The reason for creating it 40 | 41 | First of all, to test the GUI framework created by me, [giu](https://github.com/AllenDang/giu), for a real project. 42 | 43 | It turns out giu is really useful for this kind of application. It just costs me 6 hours to build it from ground. 44 | 45 | And I have this idea for years, to create a text process pipeline, to ease my daily text processing pain. 46 | 47 | Hope it could be useful to you as well. :) 48 | -------------------------------------------------------------------------------- /build_mac.sh: -------------------------------------------------------------------------------- 1 | gmdeploy -os=darwin -icon=./resource/pipeline.icns . 2 | -------------------------------------------------------------------------------- /build_win.sh: -------------------------------------------------------------------------------- 1 | echo " 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | PerMonitorV2, PerMonitor 13 | True 14 | 15 | 16 | 17 | " > PipeIt.exe.manifest 18 | 19 | rsrc -manifest PipeIt.exe.manifest -ico ./resource/pipeline.ico -arch amd64 -o rsrc.syso 20 | 21 | go build -ldflags='-s -w -H windowsgui -linkmode external -extldflags -static' . 22 | 23 | rm PipeIt.exe.manifest 24 | rm rsrc.syso 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/AllenDang/PipeIt 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/AllenDang/giu v0.6.1-0.20220113131307-67f04e276bc6 7 | github.com/davecgh/go-spew v1.1.1 // indirect 8 | github.com/kr/pretty v0.1.0 // indirect 9 | golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect 10 | golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect 11 | golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect 12 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/AllenDang/giu v0.6.1-0.20220113131307-67f04e276bc6 h1:VJ6hUagTss1Ypkb+FWjRQ6PR2lJ0MKWmA0ruh431j+w= 2 | github.com/AllenDang/giu v0.6.1-0.20220113131307-67f04e276bc6/go.mod h1:r1J1d26AT9OBlMF6xL7tBgUE9xHzJKGjSvHDysoc18U= 3 | github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8 h1:dKZMqib/yUDoCFigmz2agG8geZ/e3iRq304/KJXqKyw= 4 | github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8/go.mod h1:b4uuDd0s6KRIPa84cEEchdQ9ICh7K0OryZHbSzMca9k= 5 | github.com/AllenDang/imgui-go v1.12.1-0.20220112081257-3c75746ab409 h1:IfyLHdkAfYaGkCzScI/ahbqE5FL1kuN/iwLtw7Z+p3s= 6 | github.com/AllenDang/imgui-go v1.12.1-0.20220112081257-3c75746ab409/go.mod h1:lSWsbR1qGBZz405YzfHXcwEyWQq206QC7cYMHoLvmI4= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHrj19iqjARrPbaRisD7EuZEVJj6ZMLl1Q= 11 | github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M= 12 | github.com/go-gl/gl v0.0.0-20210315015930-ae072cafe09d/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= 13 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 14 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 15 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec h1:3FLiRYO6PlQFDpUU7OEFlWgjGD1jnBIVSJ5SYRWk+9c= 16 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 17 | github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= 18 | github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= 19 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 20 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 21 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 22 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 23 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 24 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 25 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 26 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= 27 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 28 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 29 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 30 | github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= 31 | github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= 32 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 33 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 34 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 35 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 36 | golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= 37 | golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 38 | golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 39 | golang.org/x/net v0.0.0-20220111093109-d55c255bac03 h1:0FB83qp0AzVJm+0wcIlauAjJ+tNdh7jLuacRYCIVv7s= 40 | golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 41 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 42 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 43 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= 45 | golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 46 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 47 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 48 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 49 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 50 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 51 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 52 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 53 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 54 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/AllenDang/PipeIt/pipe" 10 | g "github.com/AllenDang/giu" 11 | ) 12 | 13 | const ( 14 | saveDir string = "Save" 15 | ) 16 | 17 | var ( 18 | input string 19 | output string 20 | 21 | pipeHint string 22 | 23 | pipeline pipe.Pipeline 24 | 25 | savePipelineName string 26 | savedPipelines []string 27 | selectedIndex int32 28 | comboPreview string 29 | ) 30 | 31 | func changed() { 32 | if len(pipeline) > 0 { 33 | pipeHint = "Pipeline (left click pipe to config, right click to delete)" 34 | } else { 35 | pipeHint = "Pipeline (click + to add a pipe)" 36 | } 37 | 38 | output = "" 39 | 40 | var data interface{} = input 41 | for _, p := range pipeline { 42 | data = p.Process(data) 43 | } 44 | 45 | output = fmt.Sprint(data) 46 | } 47 | 48 | func buildConfigMenu(index int, configUI g.Layout) g.Layout { 49 | inType := pipe.DataTypeString 50 | outType := pipeline[index].GetInputType() 51 | 52 | if index > 0 { 53 | inType = pipeline[index-1].GetOutputType() 54 | } 55 | 56 | betweenPipes := pipe.QueryPipesBetween(inType, outType) 57 | 58 | var addBeforeMenuItems g.Layout 59 | for i, p := range betweenPipes { 60 | builder := p.Builder 61 | addBeforeMenuItems = append(addBeforeMenuItems, g.Selectable(fmt.Sprintf("%s##%d-%d", p.Name, index, i)).OnClick(func() { 62 | pipeline = append(pipeline[:index], append(pipe.Pipeline{builder()}, pipeline[index:]...)...) 63 | changed() 64 | })) 65 | addBeforeMenuItems = append(addBeforeMenuItems, g.Tooltip(p.Tip)) 66 | } 67 | 68 | var addBeforeMenu g.Layout 69 | if len(addBeforeMenuItems) > 0 { 70 | addBeforeMenu = append(addBeforeMenu, g.Menu(fmt.Sprintf("Add before##%d", index)).Layout(addBeforeMenuItems)) 71 | } else { 72 | addBeforeMenu = append(addBeforeMenu, g.Menu(fmt.Sprintf("Add before##%d", index)).Layout(g.Layout{g.Label("No suitable pipe")})) 73 | } 74 | 75 | return g.Layout{ 76 | g.Custom(func() { 77 | if configUI != nil { 78 | g.ContextMenu().ID(fmt.Sprintf("%s##%d", "configMenu", index)).MouseButton(g.MouseButtonLeft).Layout(configUI).Build() 79 | } 80 | }), 81 | g.ContextMenu().ID(fmt.Sprintf("%s##%d", "opMenu", index)).MouseButton(g.MouseButtonRight).Layout(g.Layout{ 82 | addBeforeMenu, 83 | g.Selectable("Delete").OnClick(func() { 84 | pipeline = append(pipeline[:index], pipeline[index+1:]...) 85 | changed() 86 | }), 87 | }), 88 | } 89 | } 90 | 91 | func buildPipesMenu() g.Widget { 92 | var widgets []g.Widget 93 | queryType := pipe.DataTypeString 94 | if len(pipeline) > 0 { 95 | queryType = pipeline[len(pipeline)-1].GetOutputType() 96 | } 97 | 98 | pipBuilders := pipe.QueryPipes(queryType) 99 | if pipBuilders == nil { 100 | widgets = append(widgets, g.Label("No suitable pipe")) 101 | } else { 102 | for i, pb := range pipBuilders { 103 | builder := pb.Builder 104 | widgets = append(widgets, 105 | g.Selectable(fmt.Sprintf("%s##%d", pb.Name, i)).OnClick(func() { 106 | pipeline = append(pipeline, builder()) 107 | changed() 108 | }), 109 | g.Tooltip(pb.Tip), 110 | ) 111 | } 112 | } 113 | 114 | return g.ContextMenu().ID("AvailabePipes").MouseButton(g.MouseButtonLeft).Layout(widgets...) 115 | } 116 | 117 | func buildPipeLineWidgets(pipes pipe.Pipeline) g.Widget { 118 | var widgets []g.Widget 119 | if len(pipes) > 0 { 120 | for i, p := range pipes { 121 | configUI := p.GetConfigUI(func() { changed() }) 122 | widgets = append(widgets, 123 | g.Button(fmt.Sprintf(" %s ##%d", p.GetName(), i)), 124 | g.Tooltip(p.GetTip()), 125 | buildConfigMenu(i, configUI), 126 | g.Label("->")) 127 | } 128 | } 129 | 130 | widgets = append(widgets, g.Button(" + "), buildPipesMenu()) 131 | 132 | return g.Row(widgets...) 133 | } 134 | 135 | func btnLoadClicked() { 136 | dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) 137 | dir = filepath.Join(dir, saveDir) 138 | 139 | f, err := os.Open(filepath.Join(dir, comboPreview)) 140 | if err != nil { 141 | g.Msgbox("Error", fmt.Sprintf("Load pipeline failed, error message is %s", err.Error())) 142 | return 143 | } 144 | defer f.Close() 145 | 146 | pl, err := pipe.DecodePipeline(f) 147 | if err != nil { 148 | g.Msgbox("Error", fmt.Sprintf("Load pipeline failed, error message is %s", err.Error())) 149 | return 150 | } 151 | 152 | pipeline = *pl 153 | changed() 154 | } 155 | 156 | func btnSaveClicked() { 157 | if len(pipeline) == 0 { 158 | g.Msgbox("Error", "Current pipeline is empty.") 159 | return 160 | } 161 | 162 | g.OpenPopup("Save Pipeline") 163 | } 164 | 165 | func onSave() { 166 | defer func() { 167 | g.CloseCurrentPopup() 168 | loadSavedPiplines() 169 | }() 170 | 171 | if len(savePipelineName) == 0 { 172 | g.Msgbox("Error", "Pipeline's name cannot be empty") 173 | return 174 | } 175 | 176 | // Prepare save dir 177 | dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) 178 | dir = filepath.Join(dir, saveDir) 179 | 180 | if _, err := os.Stat(dir); os.IsNotExist(err) { 181 | err := os.Mkdir(dir, os.ModeDir) 182 | if err != nil { 183 | g.Msgbox("Error", fmt.Sprintf("Failed to create save directory, error message is %s", err.Error())) 184 | return 185 | } 186 | } 187 | 188 | saveFilepath := filepath.Join(dir, fmt.Sprintf("%s.pl", savePipelineName)) 189 | 190 | buf, err := pipe.EncodePipeline(pipeline) 191 | if err != nil { 192 | g.Msgbox("Error", fmt.Sprintf("Save pipeline failed, error message is %s", err.Error())) 193 | return 194 | } 195 | 196 | err = ioutil.WriteFile(saveFilepath, buf.Bytes(), 0644) 197 | if err != nil { 198 | g.Msgbox("Error", fmt.Sprintf("Save pipeline failed, error message is %s", err.Error())) 199 | return 200 | } 201 | } 202 | 203 | func onCancel() { 204 | g.CloseCurrentPopup() 205 | } 206 | 207 | func onComboChanged() { 208 | comboPreview = savedPipelines[selectedIndex] 209 | } 210 | 211 | func loadSavedPiplines() { 212 | dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) 213 | dir = filepath.Join(dir, saveDir) 214 | 215 | // Get all saved *.pl filenames. 216 | var files []string 217 | _ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 218 | if err == nil && !info.IsDir() && filepath.Ext(path) == ".pl" { 219 | files = append(files, filepath.Base(path)) 220 | } 221 | return nil 222 | }) 223 | 224 | savedPipelines = files 225 | if int(selectedIndex) > len(savedPipelines) { 226 | selectedIndex = 0 227 | } 228 | if len(savedPipelines) > 0 { 229 | comboPreview = savedPipelines[selectedIndex] 230 | } 231 | } 232 | 233 | func loop() { 234 | g.SingleWindow().Layout(g.Layout{ 235 | g.SplitLayout(g.DirectionVertical, 300, 236 | g.Layout{ 237 | g.Label("Input - input or paste text below"), 238 | g.InputTextMultiline(&input).Size(-1, -1).OnChange(changed), 239 | }, 240 | g.Layout{ 241 | g.Dummy(0, 8), 242 | g.Row( 243 | g.Label(pipeHint), 244 | g.Combo("##savedPipeines", comboPreview, savedPipelines, &selectedIndex).Size(200).OnChange(onComboChanged), 245 | g.Button("Load").OnClick(btnLoadClicked), 246 | g.Button("Save").OnClick(btnSaveClicked), 247 | ), 248 | g.PopupModal("Save Pipeline").Flags(g.WindowFlagsNoResize).Layout(g.Layout{ 249 | g.Label("Enter the name of the pipeline "), 250 | g.InputText(&savePipelineName).Size(200), 251 | g.Row( 252 | g.Button("Save").OnClick(onSave), 253 | g.Button("Cancel").OnClick(onCancel), 254 | ), 255 | }), 256 | buildPipeLineWidgets(pipeline), 257 | g.Dummy(0, 8), 258 | g.Label("Output - output text which is processed by pipeline"), 259 | g.InputTextMultiline(&output).Size(-1, -1).Flags(g.InputTextFlagsReadOnly), 260 | }).Border(false), 261 | g.PrepareMsgbox(), 262 | }) 263 | } 264 | 265 | func readStdin() { 266 | stat, err := os.Stdin.Stat() 267 | if err != nil { 268 | return 269 | } 270 | 271 | if (stat.Mode() & os.ModeCharDevice) == 0 { 272 | // Data is being piped to stdin 273 | bytes, _ := ioutil.ReadAll(os.Stdin) 274 | input = string(bytes) 275 | } 276 | } 277 | 278 | func main() { 279 | pipeHint = "Pipeline (click + to add a pipe)" 280 | 281 | // Try to read from stdin if there is anything. 282 | readStdin() 283 | 284 | // Load saved pipelines. 285 | loadSavedPiplines() 286 | 287 | wnd := g.NewMasterWindow("PipeIt", 1024, 768, 0) 288 | wnd.Run(loop) 289 | } 290 | -------------------------------------------------------------------------------- /pipe/column2row.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | 6 | g "github.com/AllenDang/giu" 7 | ) 8 | 9 | type Column2RowPipe struct{} 10 | 11 | func init() { 12 | gob.Register(&Column2RowPipe{}) 13 | } 14 | 15 | func NewColumn2RowPipe() Pipe { 16 | return &Column2RowPipe{} 17 | } 18 | 19 | func (c *Column2RowPipe) GetName() string { 20 | return "C2R" 21 | } 22 | 23 | func (c *Column2RowPipe) GetTip() string { 24 | return "Shift table's column to row" 25 | } 26 | 27 | func (c *Column2RowPipe) GetInputType() DataType { 28 | return DataTypeTable 29 | } 30 | 31 | func (c *Column2RowPipe) GetOutputType() DataType { 32 | return DataTypeTable 33 | } 34 | 35 | func (c *Column2RowPipe) GetConfigUI(changed func()) g.Layout { 36 | return nil 37 | } 38 | 39 | func (c *Column2RowPipe) Process(data interface{}) interface{} { 40 | if table, ok := data.([][]string); ok { 41 | if len(table) > 0 && len(table[0]) > 0 { 42 | columnCount := len(table[0]) 43 | rows := make([][]string, columnCount) 44 | 45 | for _, r := range table { 46 | for i, c := range r { 47 | if i < columnCount { 48 | rows[i] = append(rows[i], c) 49 | } 50 | } 51 | } 52 | 53 | return rows 54 | } 55 | } 56 | 57 | return [][]string{[]string{"Error: Column2RowPipe only accepts table as input"}} 58 | } 59 | -------------------------------------------------------------------------------- /pipe/field.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "strings" 6 | 7 | g "github.com/AllenDang/giu" 8 | ) 9 | 10 | type FieldsPipe struct{} 11 | 12 | func init() { 13 | gob.Register(&FieldsPipe{}) 14 | } 15 | 16 | func NewFieldsPipe() Pipe { 17 | return &FieldsPipe{} 18 | } 19 | 20 | func (f *FieldsPipe) GetName() string { 21 | return "F" 22 | } 23 | 24 | func (f *FieldsPipe) GetTip() string { 25 | return "Fields splits the string s around each instance of one or more consecutive white space characters" 26 | } 27 | 28 | func (f *FieldsPipe) GetInputType() DataType { 29 | return DataTypeString 30 | } 31 | 32 | func (f *FieldsPipe) GetOutputType() DataType { 33 | return DataTypeStringArray 34 | } 35 | 36 | func (f *FieldsPipe) GetConfigUI(changed func()) g.Layout { 37 | return nil 38 | } 39 | 40 | func (f *FieldsPipe) Process(data interface{}) interface{} { 41 | if str, ok := data.(string); ok { 42 | return strings.Fields(str) 43 | } 44 | 45 | return []string{"Error: Fields only accepts string as input"} 46 | } 47 | -------------------------------------------------------------------------------- /pipe/fmtrow.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "strings" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type FmtRowPipe struct { 12 | FmtStr string 13 | } 14 | 15 | func init() { 16 | gob.Register(&FmtRowPipe{}) 17 | } 18 | 19 | func NewFmtRowPipe() Pipe { 20 | return &FmtRowPipe{ 21 | FmtStr: "", 22 | } 23 | } 24 | 25 | func (f *FmtRowPipe) GetName() string { 26 | return "FR" 27 | } 28 | 29 | func (f *FmtRowPipe) GetTip() string { 30 | return "Format row by using %[1]s %[2]s .. %[n]s to reference columns" 31 | } 32 | 33 | func (f *FmtRowPipe) GetInputType() DataType { 34 | return DataTypeTable 35 | } 36 | 37 | func (f *FmtRowPipe) GetOutputType() DataType { 38 | return DataTypeString 39 | } 40 | 41 | func (f *FmtRowPipe) GetConfigUI(changed func()) g.Layout { 42 | return g.Layout{ 43 | g.InputText(&(f.FmtStr)).Label("Format string").Size(300).OnChange(changed), 44 | } 45 | } 46 | 47 | func (f *FmtRowPipe) Process(data interface{}) interface{} { 48 | if table, ok := data.([][]string); ok { 49 | var sb strings.Builder 50 | 51 | for _, r := range table { 52 | var tempRow []interface{} 53 | for _, c := range r { 54 | tempRow = append(tempRow, c) 55 | } 56 | sb.WriteString(fmt.Sprintf(f.FmtStr+"\n", tempRow...)) 57 | } 58 | 59 | return sb.String() 60 | } 61 | 62 | return "Error: FmtRow only accepts table as input" 63 | } 64 | -------------------------------------------------------------------------------- /pipe/join.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "strings" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type JoinPipe struct { 12 | JoinWith string 13 | } 14 | 15 | func init() { 16 | gob.Register(&JoinPipe{}) 17 | } 18 | 19 | func NewJoinPipe() Pipe { 20 | return &JoinPipe{ 21 | JoinWith: ",", 22 | } 23 | } 24 | 25 | func (j *JoinPipe) GetName() string { 26 | return "J" 27 | } 28 | 29 | func (j *JoinPipe) GetTip() string { 30 | return fmt.Sprintf("Join string array with %s", j.JoinWith) 31 | } 32 | 33 | func (j *JoinPipe) GetInputType() DataType { 34 | return DataTypeStringArray 35 | } 36 | 37 | func (j *JoinPipe) GetOutputType() DataType { 38 | return DataTypeString 39 | } 40 | 41 | func (j *JoinPipe) GetConfigUI(changed func()) g.Layout { 42 | return g.Layout{ 43 | g.InputText(&(j.JoinWith)).Label("Join With").Size(100).OnChange(changed), 44 | } 45 | } 46 | 47 | func (j *JoinPipe) Process(data interface{}) interface{} { 48 | if strs, ok := data.([]string); ok { 49 | return strings.Join(strs, j.JoinWith) 50 | } 51 | 52 | return "Error: Join only accepts string array as input" 53 | } 54 | -------------------------------------------------------------------------------- /pipe/line.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "strings" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type LinePipe struct{} 12 | 13 | func init() { 14 | gob.Register(&LinePipe{}) 15 | } 16 | 17 | func NewLinePipe() Pipe { 18 | return &LinePipe{} 19 | } 20 | 21 | func (l *LinePipe) GetName() string { 22 | return "L" 23 | } 24 | 25 | func (l *LinePipe) GetTip() string { 26 | return "Output input string line by line" 27 | } 28 | 29 | func (l *LinePipe) GetInputType() DataType { 30 | return DataTypeStringArray 31 | } 32 | 33 | func (l *LinePipe) GetOutputType() DataType { 34 | return DataTypeString 35 | } 36 | 37 | func (l *LinePipe) GetConfigUI(changed func()) g.Layout { 38 | return nil 39 | } 40 | 41 | func (l *LinePipe) Process(data interface{}) interface{} { 42 | if strs, ok := data.([]string); ok { 43 | var sb strings.Builder 44 | for _, s := range strs { 45 | sb.WriteString(fmt.Sprintf("%s\n", s)) 46 | } 47 | 48 | return sb.String() 49 | } 50 | 51 | return "Error: Line only accepts string array as input" 52 | } 53 | -------------------------------------------------------------------------------- /pipe/match.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "regexp" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type MatchPipe struct { 12 | MatchWith string 13 | } 14 | 15 | func init() { 16 | gob.Register(&MatchPipe{}) 17 | } 18 | 19 | func NewMatchPipe() Pipe { 20 | return &MatchPipe{} 21 | } 22 | 23 | func (m *MatchPipe) GetName() string { 24 | return "M" 25 | } 26 | 27 | func (m *MatchPipe) GetTip() string { 28 | return fmt.Sprintf("Match input string array with regex %s", m.MatchWith) 29 | } 30 | 31 | func (m *MatchPipe) GetInputType() DataType { 32 | return DataTypeStringArray 33 | } 34 | 35 | func (m *MatchPipe) GetOutputType() DataType { 36 | return DataTypeStringArray 37 | } 38 | 39 | func (m *MatchPipe) GetConfigUI(changed func()) g.Layout { 40 | return g.Layout{ 41 | g.InputText(&(m.MatchWith)).Label("Match with").Size(100).OnChange(changed), 42 | } 43 | } 44 | 45 | func (m *MatchPipe) Process(data interface{}) interface{} { 46 | if strs, ok := data.([]string); ok { 47 | var result []string 48 | for _, s := range strs { 49 | if matched, _ := regexp.MatchString(m.MatchWith, s); matched { 50 | result = append(result, s) 51 | } 52 | } 53 | 54 | return result 55 | } 56 | 57 | return []string{"Error: Match only accepts string array as input"} 58 | } 59 | -------------------------------------------------------------------------------- /pipe/pipe.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | "io" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type DataType int 12 | 13 | const ( 14 | DataTypeString DataType = iota 15 | DataTypeStringArray 16 | DataTypeTable 17 | ) 18 | 19 | type Parameter struct { 20 | Type DataType 21 | Value interface{} 22 | } 23 | 24 | type Pipe interface { 25 | // Get information for the pipe including name, bgColor, nameColor and borderColor 26 | GetName() string 27 | GetTip() string 28 | GetInputType() DataType 29 | GetOutputType() DataType 30 | GetConfigUI(changed func()) g.Layout 31 | Process(data interface{}) interface{} 32 | } 33 | 34 | type Pipeline []Pipe 35 | 36 | func EncodePipeline(pl Pipeline) (*bytes.Buffer, error) { 37 | var buf bytes.Buffer 38 | enc := gob.NewEncoder(&buf) 39 | err := enc.Encode(&pl) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return &buf, nil 45 | } 46 | 47 | func DecodePipeline(r io.Reader) (*Pipeline, error) { 48 | var pl Pipeline 49 | dec := gob.NewDecoder(r) 50 | err := dec.Decode(&pl) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | return &pl, nil 56 | } 57 | -------------------------------------------------------------------------------- /pipe/registry.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | type PipeBuilder struct { 4 | Name string 5 | Tip string 6 | Builder func() Pipe 7 | } 8 | 9 | var ( 10 | pipeRegistry map[DataType][]*PipeBuilder 11 | ) 12 | 13 | func init() { 14 | pipeRegistry = make(map[DataType][]*PipeBuilder) 15 | pipeRegistry[DataTypeString] = []*PipeBuilder{ 16 | &PipeBuilder{"Split", "Split input string into string array using regexp expression", NewRegexpSplitPipe}, 17 | &PipeBuilder{"Fields", "Fields splits the string s around each instance of one or more consecutive white space characters", NewFieldsPipe}, 18 | &PipeBuilder{"Table", "Table parse input string to rows and columns", NewTablePipe}, 19 | } 20 | pipeRegistry[DataTypeStringArray] = []*PipeBuilder{ 21 | &PipeBuilder{"Join", "Join input string array with given separator", NewJoinPipe}, 22 | &PipeBuilder{"Match", "Match input string array with given regex", NewMatchPipe}, 23 | &PipeBuilder{"Surround", "Add prefix and suffix to each element of input string array", NewSurroundPipe}, 24 | &PipeBuilder{"Replace", "Search and replace for each element of input string array", NewReplacePipe}, 25 | &PipeBuilder{"Line", "Output input string array line by line", NewLinePipe}, 26 | &PipeBuilder{"Trim", "Trim returns a slice of the string s with all leading and trailing Unicode code points contained in cutset removed", NewTrimPipe}, 27 | } 28 | pipeRegistry[DataTypeTable] = []*PipeBuilder{ 29 | &PipeBuilder{"Column2Row", "Shift table's column to row", NewColumn2RowPipe}, 30 | &PipeBuilder{"FmtRow", "Format row to string", NewFmtRowPipe}, 31 | } 32 | } 33 | 34 | func QueryPipes(byType DataType) []*PipeBuilder { 35 | if v, ok := pipeRegistry[byType]; ok { 36 | return v 37 | } 38 | 39 | return nil 40 | } 41 | 42 | func QueryPipesBetween(inType, outType DataType) []*PipeBuilder { 43 | if inPipes, ok := pipeRegistry[inType]; ok { 44 | var pipes []*PipeBuilder 45 | for _, p := range inPipes { 46 | if p.Builder().GetOutputType() == outType { 47 | pipes = append(pipes, p) 48 | } 49 | } 50 | return pipes 51 | } 52 | 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /pipe/replace.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "regexp" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type ReplacePipe struct { 12 | Replace string 13 | With string 14 | } 15 | 16 | func init() { 17 | gob.Register(&ReplacePipe{}) 18 | } 19 | 20 | func NewReplacePipe() Pipe { 21 | return &ReplacePipe{} 22 | } 23 | 24 | func (r *ReplacePipe) GetName() string { 25 | return "R" 26 | } 27 | 28 | func (r *ReplacePipe) GetTip() string { 29 | return fmt.Sprintf("Replace each string of input string array from %s to %s", r.Replace, r.With) 30 | } 31 | 32 | func (r *ReplacePipe) GetInputType() DataType { 33 | return DataTypeStringArray 34 | } 35 | 36 | func (r *ReplacePipe) GetOutputType() DataType { 37 | return DataTypeStringArray 38 | } 39 | 40 | func (r *ReplacePipe) GetConfigUI(changed func()) g.Layout { 41 | return g.Layout{ 42 | g.InputText(&(r.Replace)).Label("Replace").Size(100).OnChange(changed), 43 | g.InputText(&(r.With)).Label("With").Size(100).OnChange(changed), 44 | } 45 | } 46 | 47 | func (r *ReplacePipe) Process(data interface{}) interface{} { 48 | if strs, ok := data.([]string); ok { 49 | re, err := regexp.Compile(r.Replace) 50 | if err != nil { 51 | return []string{"Error: Invalid regex"} 52 | } 53 | 54 | var result []string 55 | for _, s := range strs { 56 | result = append(result, re.ReplaceAllString(s, r.With)) 57 | } 58 | 59 | return result 60 | } 61 | 62 | return []string{"Error: Replace only accepts string array as input"} 63 | } 64 | -------------------------------------------------------------------------------- /pipe/rsplit.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "regexp" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type RegexpSplitPipe struct { 12 | SplitWith string 13 | } 14 | 15 | func init() { 16 | gob.Register(&RegexpSplitPipe{}) 17 | } 18 | 19 | func NewRegexpSplitPipe() Pipe { 20 | return &RegexpSplitPipe{ 21 | SplitWith: ",", 22 | } 23 | } 24 | 25 | func (r *RegexpSplitPipe) GetName() string { 26 | return "S" 27 | } 28 | 29 | func (r *RegexpSplitPipe) GetTip() string { 30 | return fmt.Sprintf("Split input string with regexp: %s", r.SplitWith) 31 | } 32 | 33 | func (r *RegexpSplitPipe) GetInputType() DataType { 34 | return DataTypeString 35 | } 36 | 37 | func (r *RegexpSplitPipe) GetOutputType() DataType { 38 | return DataTypeStringArray 39 | } 40 | 41 | func (r *RegexpSplitPipe) GetConfigUI(changed func()) g.Layout { 42 | return g.Layout{ 43 | g.InputText(&(r.SplitWith)).Label("Split").Size(100).OnChange(changed), 44 | } 45 | } 46 | 47 | func (r *RegexpSplitPipe) Process(data interface{}) interface{} { 48 | if str, ok := data.(string); ok { 49 | re, err := regexp.Compile(r.SplitWith) 50 | if err == nil { 51 | return re.Split(str, -1) 52 | } else { 53 | return []string{err.Error()} 54 | } 55 | } 56 | 57 | return []string{"Error: RegexpSplit only accepts string as input"} 58 | } 59 | -------------------------------------------------------------------------------- /pipe/surround.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "strings" 7 | 8 | g "github.com/AllenDang/giu" 9 | ) 10 | 11 | type SurroundPipe struct { 12 | Prefix string 13 | Suffix string 14 | } 15 | 16 | func init() { 17 | gob.Register(&SurroundPipe{}) 18 | } 19 | 20 | func NewSurroundPipe() Pipe { 21 | return &SurroundPipe{ 22 | Prefix: "'", 23 | Suffix: "'", 24 | } 25 | } 26 | 27 | func (p *SurroundPipe) GetName() string { 28 | return "SR" 29 | } 30 | 31 | func (p *SurroundPipe) GetTip() string { 32 | return fmt.Sprintf("Surround each string of input string array with %s as Prefix and %s as Suffix", p.Prefix, p.Suffix) 33 | } 34 | 35 | func (p *SurroundPipe) GetInputType() DataType { 36 | return DataTypeStringArray 37 | } 38 | 39 | func (p *SurroundPipe) GetOutputType() DataType { 40 | return DataTypeStringArray 41 | } 42 | 43 | func (p *SurroundPipe) GetConfigUI(changed func()) g.Layout { 44 | return g.Layout{ 45 | g.Label("Use %d to generate series number"), 46 | g.InputText(&(p.Prefix)).Label("Prefix").Size(100).OnChange(changed), 47 | g.InputText(&(p.Suffix)).Label("Suffix").Size(100).OnChange(changed), 48 | } 49 | } 50 | 51 | func (p *SurroundPipe) Process(data interface{}) interface{} { 52 | if strs, ok := data.([]string); ok { 53 | var result []string 54 | for i, s := range strs { 55 | pf := strings.Replace(p.Prefix, "%d", fmt.Sprintf("%d", i+1), -1) 56 | sf := strings.Replace(p.Suffix, "%d", fmt.Sprintf("%d", i+1), -1) 57 | result = append(result, fmt.Sprintf("%s%s%s", pf, s, sf)) 58 | } 59 | 60 | return result 61 | } 62 | 63 | return []string{"Error: Surround only accepts string array as input"} 64 | } 65 | -------------------------------------------------------------------------------- /pipe/table.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "regexp" 6 | 7 | g "github.com/AllenDang/giu" 8 | ) 9 | 10 | type TablePipe struct { 11 | SplitRowWith string 12 | SplitColumnWith string 13 | } 14 | 15 | func init() { 16 | gob.Register(&TablePipe{}) 17 | } 18 | 19 | func NewTablePipe() Pipe { 20 | return &TablePipe{ 21 | SplitRowWith: "\n", 22 | SplitColumnWith: ",", 23 | } 24 | } 25 | 26 | func (t *TablePipe) GetName() string { 27 | return "T" 28 | } 29 | 30 | func (t *TablePipe) GetTip() string { 31 | return "Table parse input string to rows and columns" 32 | } 33 | 34 | func (t *TablePipe) GetInputType() DataType { 35 | return DataTypeString 36 | } 37 | 38 | func (t *TablePipe) GetOutputType() DataType { 39 | return DataTypeTable 40 | } 41 | 42 | func (t *TablePipe) GetConfigUI(changed func()) g.Layout { 43 | return g.Layout{ 44 | g.InputText(&(t.SplitRowWith)).Label("Split row with").Size(100).OnChange(changed), 45 | g.InputText(&(t.SplitColumnWith)).Label("Split column with").Size(100).OnChange(changed), 46 | } 47 | } 48 | 49 | func (t *TablePipe) Process(data interface{}) interface{} { 50 | if str, ok := data.(string); ok { 51 | re, err := regexp.Compile(t.SplitRowWith) 52 | if err != nil { 53 | return [][]string{[]string{err.Error()}} 54 | } 55 | 56 | ce, err := regexp.Compile(t.SplitColumnWith) 57 | if err != nil { 58 | return [][]string{[]string{err.Error()}} 59 | } 60 | 61 | tempRows := re.Split(str, -1) 62 | 63 | var rows [][]string 64 | 65 | for _, r := range tempRows { 66 | rows = append(rows, ce.Split(r, -1)) 67 | } 68 | 69 | return rows 70 | } 71 | 72 | return [][]string{[]string{"Error: Table only accepts string as input"}} 73 | } 74 | -------------------------------------------------------------------------------- /pipe/trim.go: -------------------------------------------------------------------------------- 1 | package pipe 2 | 3 | import ( 4 | "encoding/gob" 5 | "strings" 6 | 7 | g "github.com/AllenDang/giu" 8 | ) 9 | 10 | type TrimPipe struct { 11 | TrimWith string 12 | } 13 | 14 | func init() { 15 | gob.Register(&TrimPipe{}) 16 | } 17 | 18 | func NewTrimPipe() Pipe { 19 | return &TrimPipe{} 20 | } 21 | 22 | func (t *TrimPipe) GetName() string { 23 | return "T" 24 | } 25 | 26 | func (t *TrimPipe) GetTip() string { 27 | return "Trim returns a slice of the string s with all leading and trailing Unicode code points contained in cutset removed" 28 | } 29 | 30 | func (t *TrimPipe) GetInputType() DataType { 31 | return DataTypeStringArray 32 | } 33 | 34 | func (t *TrimPipe) GetOutputType() DataType { 35 | return DataTypeStringArray 36 | } 37 | 38 | func (t *TrimPipe) GetConfigUI(changed func()) g.Layout { 39 | return g.Layout{ 40 | g.InputText(&(t.TrimWith)).Label("Trim with").Size(100).OnChange(changed), 41 | } 42 | } 43 | 44 | func (t *TrimPipe) Process(data interface{}) interface{} { 45 | if strs, ok := data.([]string); ok { 46 | var results []string 47 | for _, s := range strs { 48 | results = append(results, strings.Trim(s, t.TrimWith)) 49 | } 50 | 51 | return results 52 | } 53 | 54 | return []string{"Error: Trim only accepts string array as input"} 55 | } 56 | -------------------------------------------------------------------------------- /resource/pipeline.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenDang/PipeIt/849da234d858308326e4beb872b0e25de418080c/resource/pipeline.icns -------------------------------------------------------------------------------- /resource/pipeline.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenDang/PipeIt/849da234d858308326e4beb872b0e25de418080c/resource/pipeline.ico -------------------------------------------------------------------------------- /resource/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenDang/PipeIt/849da234d858308326e4beb872b0e25de418080c/resource/pipeline.png -------------------------------------------------------------------------------- /screenshot/addquotation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenDang/PipeIt/849da234d858308326e4beb872b0e25de418080c/screenshot/addquotation.gif -------------------------------------------------------------------------------- /screenshot/commatolines.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenDang/PipeIt/849da234d858308326e4beb872b0e25de418080c/screenshot/commatolines.gif -------------------------------------------------------------------------------- /screenshot/findimageurl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenDang/PipeIt/849da234d858308326e4beb872b0e25de418080c/screenshot/findimageurl.gif -------------------------------------------------------------------------------- /screenshot/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenDang/PipeIt/849da234d858308326e4beb872b0e25de418080c/screenshot/screenshot1.png --------------------------------------------------------------------------------