├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── cmd.go ├── cmd_test.go ├── convert.go ├── convert_test.go ├── dir.go ├── dir_test.go ├── example_test.go ├── file.go ├── file_test.go ├── go.mod ├── go.sum ├── html.go ├── html_test.go ├── http.go ├── http_test.go ├── math.go ├── math_test.go ├── path.go ├── path_test.go ├── regex.go ├── regex_test.go ├── slice.go ├── slice_test.go ├── string.go ├── string_test.go ├── testdata ├── SaveFile.txt ├── SaveFileS.txt ├── sample_file.txt └── statDir │ ├── SaveFile.txt │ ├── SaveFileS.txt │ ├── sample_file.txt │ └── secondLevel │ ├── SaveFile.txt │ ├── SaveFileS.txt │ └── sample_file.txt ├── time.go └── url.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | .idea 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.iml 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.3.x 5 | - 1.4.x 6 | - 1.5.x 7 | - 1.6.x 8 | - 1.7.x 9 | - 1.8.x 10 | - 1.9.x 11 | - 1.10.x 12 | - 1.11.x 13 | - 1.12.x 14 | 15 | install: go get -v -t 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Common Functions 2 | ================ 3 | 4 | [![Build Status](https://travis-ci.org/unknwon/com.svg)](https://travis-ci.org/unknwon/com) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/unknwon/com) 5 | 6 | This is an open source project for commonly used functions for the Go programming language. 7 | 8 | This package need >= **go 1.3** 9 | 10 | Code Convention: based on [Go Code Convention](https://github.com/unknwon/go-code-convention). 11 | 12 | ## Contribute 13 | 14 | Your contribute is welcome, but you have to check following steps after you added some functions and commit them: 15 | 16 | 1. Make sure you wrote user-friendly comments for **all functions** . 17 | 2. Make sure you wrote test cases with any possible condition for **all functions** in file `*_test.go`. 18 | 3. Make sure you wrote benchmarks for **all functions** in file `*_test.go`. 19 | 4. Make sure you wrote useful examples for **all functions** in file `example_test.go`. 20 | 5. Make sure you ran `go test` and got **PASS** . 21 | -------------------------------------------------------------------------------- /cmd.go: -------------------------------------------------------------------------------- 1 | // +build go1.3 2 | 3 | // Copyright 2013 com authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 6 | // not use this file except in compliance with the License. You may obtain 7 | // a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | // License for the specific language governing permissions and limitations 15 | // under the License. 16 | 17 | // Package com is an open source project for commonly used functions for the Go programming language. 18 | package com 19 | 20 | import ( 21 | "bytes" 22 | "fmt" 23 | "os/exec" 24 | "runtime" 25 | "strings" 26 | ) 27 | 28 | // ExecCmdDirBytes executes system command in given directory 29 | // and return stdout, stderr in bytes type, along with possible error. 30 | func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) { 31 | bufOut := new(bytes.Buffer) 32 | bufErr := new(bytes.Buffer) 33 | 34 | cmd := exec.Command(cmdName, args...) 35 | cmd.Dir = dir 36 | cmd.Stdout = bufOut 37 | cmd.Stderr = bufErr 38 | 39 | err := cmd.Run() 40 | return bufOut.Bytes(), bufErr.Bytes(), err 41 | } 42 | 43 | // ExecCmdBytes executes system command 44 | // and return stdout, stderr in bytes type, along with possible error. 45 | func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) { 46 | return ExecCmdDirBytes("", cmdName, args...) 47 | } 48 | 49 | // ExecCmdDir executes system command in given directory 50 | // and return stdout, stderr in string type, along with possible error. 51 | func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) { 52 | bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...) 53 | return string(bufOut), string(bufErr), err 54 | } 55 | 56 | // ExecCmd executes system command 57 | // and return stdout, stderr in string type, along with possible error. 58 | func ExecCmd(cmdName string, args ...string) (string, string, error) { 59 | return ExecCmdDir("", cmdName, args...) 60 | } 61 | 62 | // _________ .__ .____ 63 | // \_ ___ \ ____ | | ___________ | | ____ ____ 64 | // / \ \/ / _ \| | / _ \_ __ \ | | / _ \ / ___\ 65 | // \ \___( <_> ) |_( <_> ) | \/ | |__( <_> ) /_/ > 66 | // \______ /\____/|____/\____/|__| |_______ \____/\___ / 67 | // \/ \/ /_____/ 68 | 69 | // Color number constants. 70 | const ( 71 | Gray = uint8(iota + 90) 72 | Red 73 | Green 74 | Yellow 75 | Blue 76 | Magenta 77 | //NRed = uint8(31) // Normal 78 | EndColor = "\033[0m" 79 | ) 80 | 81 | // getColorLevel returns colored level string by given level. 82 | func getColorLevel(level string) string { 83 | level = strings.ToUpper(level) 84 | switch level { 85 | case "TRAC": 86 | return fmt.Sprintf("\033[%dm%s\033[0m", Blue, level) 87 | case "ERRO": 88 | return fmt.Sprintf("\033[%dm%s\033[0m", Red, level) 89 | case "WARN": 90 | return fmt.Sprintf("\033[%dm%s\033[0m", Magenta, level) 91 | case "SUCC": 92 | return fmt.Sprintf("\033[%dm%s\033[0m", Green, level) 93 | default: 94 | return level 95 | } 96 | } 97 | 98 | // ColorLogS colors log and return colored content. 99 | // Log format: [ error ]. 100 | // Level: TRAC -> blue; ERRO -> red; WARN -> Magenta; SUCC -> green; others -> default. 101 | // Content: default; path: yellow; error -> red. 102 | // Level has to be surrounded by "[" and "]". 103 | // Highlights have to be surrounded by "# " and " #"(space), "#" will be deleted. 104 | // Paths have to be surrounded by "( " and " )"(space). 105 | // Errors have to be surrounded by "[ " and " ]"(space). 106 | // Note: it hasn't support windows yet, contribute is welcome. 107 | func ColorLogS(format string, a ...interface{}) string { 108 | log := fmt.Sprintf(format, a...) 109 | 110 | var clog string 111 | 112 | if runtime.GOOS != "windows" { 113 | // Level. 114 | i := strings.Index(log, "]") 115 | if log[0] == '[' && i > -1 { 116 | clog += "[" + getColorLevel(log[1:i]) + "]" 117 | } 118 | 119 | log = log[i+1:] 120 | 121 | // Error. 122 | log = strings.Replace(log, "[ ", fmt.Sprintf("[\033[%dm", Red), -1) 123 | log = strings.Replace(log, " ]", EndColor+"]", -1) 124 | 125 | // Path. 126 | log = strings.Replace(log, "( ", fmt.Sprintf("(\033[%dm", Yellow), -1) 127 | log = strings.Replace(log, " )", EndColor+")", -1) 128 | 129 | // Highlights. 130 | log = strings.Replace(log, "# ", fmt.Sprintf("\033[%dm", Gray), -1) 131 | log = strings.Replace(log, " #", EndColor, -1) 132 | 133 | } else { 134 | // Level. 135 | i := strings.Index(log, "]") 136 | if log[0] == '[' && i > -1 { 137 | clog += "[" + log[1:i] + "]" 138 | } 139 | 140 | log = log[i+1:] 141 | 142 | // Error. 143 | log = strings.Replace(log, "[ ", "[", -1) 144 | log = strings.Replace(log, " ]", "]", -1) 145 | 146 | // Path. 147 | log = strings.Replace(log, "( ", "(", -1) 148 | log = strings.Replace(log, " )", ")", -1) 149 | 150 | // Highlights. 151 | log = strings.Replace(log, "# ", "", -1) 152 | log = strings.Replace(log, " #", "", -1) 153 | } 154 | return clog + log 155 | } 156 | 157 | // ColorLog prints colored log to stdout. 158 | // See color rules in function 'ColorLogS'. 159 | func ColorLog(format string, a ...interface{}) { 160 | fmt.Print(ColorLogS(format, a...)) 161 | } 162 | -------------------------------------------------------------------------------- /cmd_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "fmt" 19 | "runtime" 20 | "strings" 21 | "testing" 22 | ) 23 | 24 | func TestColorLogS(t *testing.T) { 25 | if runtime.GOOS != "windows" { 26 | // Trace + path. 27 | cls := ColorLogS("[TRAC] Trace level test with path( %s )", "/path/to/somethere") 28 | clsR := fmt.Sprintf( 29 | "[\033[%dmTRAC%s] Trace level test with path(\033[%dm%s%s)", 30 | Blue, EndColor, Yellow, "/path/to/somethere", EndColor) 31 | if cls != clsR { 32 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 33 | } 34 | 35 | // Error + error. 36 | cls = ColorLogS("[ERRO] Error level test with error[ %s ]", "test error") 37 | clsR = fmt.Sprintf( 38 | "[\033[%dmERRO%s] Error level test with error[\033[%dm%s%s]", 39 | Red, EndColor, Red, "test error", EndColor) 40 | if cls != clsR { 41 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 42 | } 43 | 44 | // Warning + highlight. 45 | cls = ColorLogS("[WARN] Warnning level test with highlight # %s #", "special offer!") 46 | clsR = fmt.Sprintf( 47 | "[\033[%dmWARN%s] Warnning level test with highlight \033[%dm%s%s", 48 | Magenta, EndColor, Gray, "special offer!", EndColor) 49 | if cls != clsR { 50 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 51 | } 52 | 53 | // Success. 54 | cls = ColorLogS("[SUCC] Success level test") 55 | clsR = fmt.Sprintf( 56 | "[\033[%dmSUCC%s] Success level test", 57 | Green, EndColor) 58 | if cls != clsR { 59 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 60 | } 61 | 62 | // Default. 63 | cls = ColorLogS("[INFO] Default level test") 64 | clsR = fmt.Sprintf( 65 | "[INFO] Default level test") 66 | if cls != clsR { 67 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 68 | } 69 | } else { 70 | // Trace + path. 71 | cls := ColorLogS("[TRAC] Trace level test with path( %s )", "/path/to/somethere") 72 | clsR := fmt.Sprintf( 73 | "[TRAC] Trace level test with path(%s)", 74 | "/path/to/somethere") 75 | if cls != clsR { 76 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 77 | } 78 | 79 | // Error + error. 80 | cls = ColorLogS("[ERRO] Error level test with error[ %s ]", "test error") 81 | clsR = fmt.Sprintf( 82 | "[ERRO] Error level test with error[%s]", 83 | "test error") 84 | if cls != clsR { 85 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 86 | } 87 | 88 | // Warning + highlight. 89 | cls = ColorLogS("[WARN] Warnning level test with highlight # %s #", "special offer!") 90 | clsR = fmt.Sprintf( 91 | "[WARN] Warnning level test with highlight %s", 92 | "special offer!") 93 | if cls != clsR { 94 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 95 | } 96 | 97 | // Success. 98 | cls = ColorLogS("[SUCC] Success level test") 99 | clsR = fmt.Sprintf( 100 | "[SUCC] Success level test") 101 | if cls != clsR { 102 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 103 | } 104 | 105 | // Default. 106 | cls = ColorLogS("[INFO] Default level test") 107 | clsR = fmt.Sprintf( 108 | "[INFO] Default level test") 109 | if cls != clsR { 110 | t.Errorf("ColorLogS:\n Expect => %s\n Got => %s\n", clsR, cls) 111 | } 112 | } 113 | } 114 | 115 | func TestExecCmd(t *testing.T) { 116 | stdout, stderr, err := ExecCmd("go", "help", "get") 117 | if err != nil { 118 | t.Errorf("ExecCmd:\n Expect => %v\n Got => %v\n", nil, err) 119 | } else if len(stderr) != 0 { 120 | t.Errorf("ExecCmd:\n Expect => %s\n Got => %s\n", "", stderr) 121 | } else if !strings.HasPrefix(stdout, "usage: go get") { 122 | t.Errorf("ExecCmd:\n Expect => %s\n Got => %s\n", "usage: go get", stdout) 123 | } 124 | } 125 | 126 | func BenchmarkColorLogS(b *testing.B) { 127 | log := fmt.Sprintf( 128 | "[WARN] This is a tesing log that should be colored, path( %s ),"+ 129 | " highlight # %s #, error [ %s ].", 130 | "path to somewhere", "highlighted content", "tesing error") 131 | for i := 0; i < b.N; i++ { 132 | ColorLogS(log) 133 | } 134 | } 135 | 136 | func BenchmarkExecCmd(b *testing.B) { 137 | for i := 0; i < b.N; i++ { 138 | ExecCmd("go", "help", "get") 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "fmt" 19 | "strconv" 20 | ) 21 | 22 | // Convert string to specify type. 23 | type StrTo string 24 | 25 | func (f StrTo) Exist() bool { 26 | return string(f) != string(rune(0x1E)) 27 | } 28 | 29 | func (f StrTo) Uint8() (uint8, error) { 30 | v, err := strconv.ParseUint(f.String(), 10, 8) 31 | return uint8(v), err 32 | } 33 | 34 | func (f StrTo) Int() (int, error) { 35 | v, err := strconv.ParseInt(f.String(), 10, 0) 36 | return int(v), err 37 | } 38 | 39 | func (f StrTo) Int64() (int64, error) { 40 | v, err := strconv.ParseInt(f.String(), 10, 64) 41 | return int64(v), err 42 | } 43 | 44 | func (f StrTo) Float64() (float64, error) { 45 | v, err := strconv.ParseFloat(f.String(), 64) 46 | return float64(v), err 47 | } 48 | 49 | func (f StrTo) MustUint8() uint8 { 50 | v, _ := f.Uint8() 51 | return v 52 | } 53 | 54 | func (f StrTo) MustInt() int { 55 | v, _ := f.Int() 56 | return v 57 | } 58 | 59 | func (f StrTo) MustInt64() int64 { 60 | v, _ := f.Int64() 61 | return v 62 | } 63 | 64 | func (f StrTo) MustFloat64() float64 { 65 | v, _ := f.Float64() 66 | return v 67 | } 68 | 69 | func (f StrTo) String() string { 70 | if f.Exist() { 71 | return string(f) 72 | } 73 | return "" 74 | } 75 | 76 | // Convert any type to string. 77 | func ToStr(value interface{}, args ...int) (s string) { 78 | switch v := value.(type) { 79 | case bool: 80 | s = strconv.FormatBool(v) 81 | case float32: 82 | s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32)) 83 | case float64: 84 | s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64)) 85 | case int: 86 | s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) 87 | case int8: 88 | s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) 89 | case int16: 90 | s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) 91 | case int32: 92 | s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) 93 | case int64: 94 | s = strconv.FormatInt(v, argInt(args).Get(0, 10)) 95 | case uint: 96 | s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) 97 | case uint8: 98 | s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) 99 | case uint16: 100 | s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) 101 | case uint32: 102 | s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) 103 | case uint64: 104 | s = strconv.FormatUint(v, argInt(args).Get(0, 10)) 105 | case string: 106 | s = v 107 | case []byte: 108 | s = string(v) 109 | default: 110 | s = fmt.Sprintf("%v", v) 111 | } 112 | return s 113 | } 114 | 115 | type argInt []int 116 | 117 | func (a argInt) Get(i int, args ...int) (r int) { 118 | if i >= 0 && i < len(a) { 119 | r = a[i] 120 | } else if len(args) > 0 { 121 | r = args[0] 122 | } 123 | return 124 | } 125 | 126 | // HexStr2int converts hex format string to decimal number. 127 | func HexStr2int(hexStr string) (int, error) { 128 | num := 0 129 | length := len(hexStr) 130 | for i := 0; i < length; i++ { 131 | char := hexStr[length-i-1] 132 | factor := -1 133 | 134 | switch { 135 | case char >= '0' && char <= '9': 136 | factor = int(char) - '0' 137 | case char >= 'a' && char <= 'f': 138 | factor = int(char) - 'a' + 10 139 | default: 140 | return -1, fmt.Errorf("invalid hex: %s", string(char)) 141 | } 142 | 143 | num += factor * PowInt(16, i) 144 | } 145 | return num, nil 146 | } 147 | 148 | // Int2HexStr converts decimal number to hex format string. 149 | func Int2HexStr(num int) (hex string) { 150 | if num == 0 { 151 | return "0" 152 | } 153 | 154 | for num > 0 { 155 | r := num % 16 156 | 157 | c := "?" 158 | if r >= 0 && r <= 9 { 159 | c = string(rune(r + '0')) 160 | } else { 161 | c = string(rune(r + 'a' - 10)) 162 | } 163 | hex = c + hex 164 | num = num / 16 165 | } 166 | return hex 167 | } 168 | -------------------------------------------------------------------------------- /convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "testing" 19 | 20 | . "github.com/smartystreets/goconvey/convey" 21 | ) 22 | 23 | func TestHexStr2int(t *testing.T) { 24 | Convey("Convert hex format string to decimal", t, func() { 25 | hexDecs := map[string]int{ 26 | "1": 1, 27 | "002": 2, 28 | "011": 17, 29 | "0a1": 161, 30 | "35e": 862, 31 | } 32 | 33 | for hex, dec := range hexDecs { 34 | val, err := HexStr2int(hex) 35 | So(err, ShouldBeNil) 36 | So(val, ShouldEqual, dec) 37 | } 38 | }) 39 | } 40 | 41 | func TestInt2HexStr(t *testing.T) { 42 | Convey("Convert decimal to hex format string", t, func() { 43 | decHexs := map[int]string{ 44 | 1: "1", 45 | 2: "2", 46 | 17: "11", 47 | 161: "a1", 48 | 862: "35e", 49 | } 50 | 51 | for dec, hex := range decHexs { 52 | val := Int2HexStr(dec) 53 | So(val, ShouldEqual, hex) 54 | } 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /dir.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "os" 21 | "path" 22 | "strings" 23 | ) 24 | 25 | // IsDir returns true if given path is a directory, 26 | // or returns false when it's a file or does not exist. 27 | func IsDir(dir string) bool { 28 | f, e := os.Stat(dir) 29 | if e != nil { 30 | return false 31 | } 32 | return f.IsDir() 33 | } 34 | 35 | func statDir(dirPath, recPath string, includeDir, isDirOnly, followSymlinks bool) ([]string, error) { 36 | dir, err := os.Open(dirPath) 37 | if err != nil { 38 | return nil, err 39 | } 40 | defer dir.Close() 41 | 42 | fis, err := dir.Readdir(0) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | statList := make([]string, 0) 48 | for _, fi := range fis { 49 | if strings.Contains(fi.Name(), ".DS_Store") { 50 | continue 51 | } 52 | 53 | relPath := path.Join(recPath, fi.Name()) 54 | curPath := path.Join(dirPath, fi.Name()) 55 | if fi.IsDir() { 56 | if includeDir { 57 | statList = append(statList, relPath+"/") 58 | } 59 | s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks) 60 | if err != nil { 61 | return nil, err 62 | } 63 | statList = append(statList, s...) 64 | } else if !isDirOnly { 65 | statList = append(statList, relPath) 66 | } else if followSymlinks && fi.Mode()&os.ModeSymlink != 0 { 67 | link, err := os.Readlink(curPath) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | if IsDir(link) { 73 | if includeDir { 74 | statList = append(statList, relPath+"/") 75 | } 76 | s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks) 77 | if err != nil { 78 | return nil, err 79 | } 80 | statList = append(statList, s...) 81 | } 82 | } 83 | } 84 | return statList, nil 85 | } 86 | 87 | // StatDir gathers information of given directory by depth-first. 88 | // It returns slice of file list and includes subdirectories if enabled; 89 | // it returns error and nil slice when error occurs in underlying functions, 90 | // or given path is not a directory or does not exist. 91 | // 92 | // Slice does not include given path itself. 93 | // If subdirectories is enabled, they will have suffix '/'. 94 | func StatDir(rootPath string, includeDir ...bool) ([]string, error) { 95 | if !IsDir(rootPath) { 96 | return nil, errors.New("not a directory or does not exist: " + rootPath) 97 | } 98 | 99 | isIncludeDir := false 100 | if len(includeDir) >= 1 { 101 | isIncludeDir = includeDir[0] 102 | } 103 | return statDir(rootPath, "", isIncludeDir, false, false) 104 | } 105 | 106 | // LstatDir gathers information of given directory by depth-first. 107 | // It returns slice of file list, follows symbolic links and includes subdirectories if enabled; 108 | // it returns error and nil slice when error occurs in underlying functions, 109 | // or given path is not a directory or does not exist. 110 | // 111 | // Slice does not include given path itself. 112 | // If subdirectories is enabled, they will have suffix '/'. 113 | func LstatDir(rootPath string, includeDir ...bool) ([]string, error) { 114 | if !IsDir(rootPath) { 115 | return nil, errors.New("not a directory or does not exist: " + rootPath) 116 | } 117 | 118 | isIncludeDir := false 119 | if len(includeDir) >= 1 { 120 | isIncludeDir = includeDir[0] 121 | } 122 | return statDir(rootPath, "", isIncludeDir, false, true) 123 | } 124 | 125 | // GetAllSubDirs returns all subdirectories of given root path. 126 | // Slice does not include given path itself. 127 | func GetAllSubDirs(rootPath string) ([]string, error) { 128 | if !IsDir(rootPath) { 129 | return nil, errors.New("not a directory or does not exist: " + rootPath) 130 | } 131 | return statDir(rootPath, "", true, true, false) 132 | } 133 | 134 | // LgetAllSubDirs returns all subdirectories of given root path, including 135 | // following symbolic links, if any. 136 | // Slice does not include given path itself. 137 | func LgetAllSubDirs(rootPath string) ([]string, error) { 138 | if !IsDir(rootPath) { 139 | return nil, errors.New("not a directory or does not exist: " + rootPath) 140 | } 141 | return statDir(rootPath, "", true, true, true) 142 | } 143 | 144 | // GetFileListBySuffix returns an ordered list of file paths. 145 | // It recognize if given path is a file, and don't do recursive find. 146 | func GetFileListBySuffix(dirPath, suffix string) ([]string, error) { 147 | if !IsExist(dirPath) { 148 | return nil, fmt.Errorf("given path does not exist: %s", dirPath) 149 | } else if IsFile(dirPath) { 150 | return []string{dirPath}, nil 151 | } 152 | 153 | // Given path is a directory. 154 | dir, err := os.Open(dirPath) 155 | if err != nil { 156 | return nil, err 157 | } 158 | 159 | fis, err := dir.Readdir(0) 160 | if err != nil { 161 | return nil, err 162 | } 163 | 164 | files := make([]string, 0, len(fis)) 165 | for _, fi := range fis { 166 | if strings.HasSuffix(fi.Name(), suffix) { 167 | files = append(files, path.Join(dirPath, fi.Name())) 168 | } 169 | } 170 | 171 | return files, nil 172 | } 173 | 174 | // CopyDir copy files recursively from source to target directory. 175 | // 176 | // The filter accepts a function that process the path info. 177 | // and should return true for need to filter. 178 | // 179 | // It returns error when error occurs in underlying functions. 180 | func CopyDir(srcPath, destPath string, filters ...func(filePath string) bool) error { 181 | // Check if target directory exists. 182 | if IsExist(destPath) { 183 | return errors.New("file or directory alreay exists: " + destPath) 184 | } 185 | 186 | err := os.MkdirAll(destPath, os.ModePerm) 187 | if err != nil { 188 | return err 189 | } 190 | 191 | // Gather directory info. 192 | infos, err := StatDir(srcPath, true) 193 | if err != nil { 194 | return err 195 | } 196 | 197 | var filter func(filePath string) bool 198 | if len(filters) > 0 { 199 | filter = filters[0] 200 | } 201 | 202 | for _, info := range infos { 203 | if filter != nil && filter(info) { 204 | continue 205 | } 206 | 207 | curPath := path.Join(destPath, info) 208 | if strings.HasSuffix(info, "/") { 209 | err = os.MkdirAll(curPath, os.ModePerm) 210 | } else { 211 | err = Copy(path.Join(srcPath, info), curPath) 212 | } 213 | if err != nil { 214 | return err 215 | } 216 | } 217 | return nil 218 | } 219 | -------------------------------------------------------------------------------- /dir_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "os" 19 | "testing" 20 | 21 | . "github.com/smartystreets/goconvey/convey" 22 | ) 23 | 24 | func TestIsDir(t *testing.T) { 25 | Convey("Check if given path is a directory", t, func() { 26 | Convey("Pass a file name", func() { 27 | So(IsDir("file.go"), ShouldEqual, false) 28 | }) 29 | Convey("Pass a directory name", func() { 30 | So(IsDir("testdata"), ShouldEqual, true) 31 | }) 32 | Convey("Pass a invalid path", func() { 33 | So(IsDir("foo"), ShouldEqual, false) 34 | }) 35 | }) 36 | } 37 | 38 | func TestCopyDir(t *testing.T) { 39 | Convey("Items of two slices should be same", t, func() { 40 | _, err := StatDir("testdata", true) 41 | So(err, ShouldEqual, nil) 42 | 43 | err = CopyDir("testdata", "testdata2") 44 | So(err, ShouldEqual, nil) 45 | 46 | _, err = StatDir("testdata2", true) 47 | os.RemoveAll("testdata2") 48 | So(err, ShouldEqual, nil) 49 | }) 50 | } 51 | 52 | func BenchmarkIsDir(b *testing.B) { 53 | for i := 0; i < b.N; i++ { 54 | IsDir("file.go") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com_test 16 | 17 | import ( 18 | "fmt" 19 | "io/ioutil" 20 | "net/http" 21 | 22 | "github.com/unknwon/com" 23 | ) 24 | 25 | // ------------------------------ 26 | // cmd.go 27 | // ------------------------------ 28 | 29 | func ExampleColorLogS() { 30 | coloredLog := com.ColorLogS(fmt.Sprintf( 31 | "[WARN] This is a tesing log that should be colored, path( %s ),"+ 32 | " highlight # %s #, error [ %s ].", 33 | "path to somewhere", "highlighted content", "tesing error")) 34 | fmt.Println(coloredLog) 35 | } 36 | 37 | func ExampleColorLog() { 38 | com.ColorLog(fmt.Sprintf( 39 | "[WARN] This is a tesing log that should be colored, path( %s ),"+ 40 | " highlight # %s #, error [ %s ].", 41 | "path to somewhere", "highlighted content", "tesing error")) 42 | } 43 | 44 | func ExampleExecCmd() { 45 | stdout, stderr, err := com.ExecCmd("go", "help", "get") 46 | fmt.Println(stdout, stderr, err) 47 | } 48 | 49 | // ------------- END ------------ 50 | 51 | // ------------------------------ 52 | // html.go 53 | // ------------------------------ 54 | 55 | func ExampleHtml2JS() { 56 | htm := "
Click me
\n\r" 57 | js := string(com.Html2JS([]byte(htm))) 58 | fmt.Println(js) 59 | // Output:
Click me
\n 60 | } 61 | 62 | // ------------- END ------------ 63 | 64 | // ------------------------------ 65 | // path.go 66 | // ------------------------------ 67 | 68 | func ExampleGetGOPATHs() { 69 | gps := com.GetGOPATHs() 70 | fmt.Println(gps) 71 | } 72 | 73 | func ExampleGetSrcPath() { 74 | srcPath, err := com.GetSrcPath("github.com/unknwon/com") 75 | if err != nil { 76 | fmt.Println(err) 77 | return 78 | } 79 | fmt.Println(srcPath) 80 | } 81 | 82 | func ExampleHomeDir() { 83 | hd, err := com.HomeDir() 84 | fmt.Println(hd, err) 85 | } 86 | 87 | // ------------- END ------------ 88 | 89 | // ------------------------------ 90 | // file.go 91 | // ------------------------------ 92 | 93 | func ExampleIsFile() { 94 | if com.IsFile("file.go") { 95 | fmt.Println("file.go exists") 96 | return 97 | } 98 | fmt.Println("file.go is not a file or does not exist") 99 | } 100 | 101 | func ExampleIsExist() { 102 | if com.IsExist("file.go") { 103 | fmt.Println("file.go exists") 104 | return 105 | } 106 | fmt.Println("file.go does not exist") 107 | } 108 | 109 | // ------------- END ------------ 110 | 111 | // ------------------------------ 112 | // dir.go 113 | // ------------------------------ 114 | 115 | func ExampleIsDir() { 116 | if com.IsDir("files") { 117 | fmt.Println("directory 'files' exists") 118 | return 119 | } 120 | fmt.Println("'files' is not a directory or does not exist") 121 | } 122 | 123 | // ------------- END ------------ 124 | 125 | // ------------------------------ 126 | // string.go 127 | // ------------------------------ 128 | 129 | func ExampleIsLetter() { 130 | fmt.Println(com.IsLetter('1')) 131 | fmt.Println(com.IsLetter('[')) 132 | fmt.Println(com.IsLetter('a')) 133 | fmt.Println(com.IsLetter('Z')) 134 | // Output: 135 | // false 136 | // false 137 | // true 138 | // true 139 | } 140 | 141 | func ExampleExpand() { 142 | match := map[string]string{ 143 | "domain": "gowalker.org", 144 | "subdomain": "github.com", 145 | } 146 | s := "http://{domain}/{subdomain}/{0}/{1}" 147 | fmt.Println(com.Expand(s, match, "unknwon", "gowalker")) 148 | // Output: http://gowalker.org/github.com/unknwon/gowalker 149 | } 150 | 151 | // ------------- END ------------ 152 | 153 | // ------------------------------ 154 | // http.go 155 | // ------------------------------ 156 | 157 | func ExampleHttpGet() ([]byte, error) { 158 | rc, err := com.HttpGet(&http.Client{}, "http://gowalker.org", nil) 159 | if err != nil { 160 | return nil, err 161 | } 162 | p, err := ioutil.ReadAll(rc) 163 | rc.Close() 164 | return p, err 165 | } 166 | 167 | func ExampleHttpGetBytes() ([]byte, error) { 168 | p, err := com.HttpGetBytes(&http.Client{}, "http://gowalker.org", nil) 169 | return p, err 170 | } 171 | 172 | func ExampleHttpGetJSON() interface{} { 173 | j := com.HttpGetJSON(&http.Client{}, "http://gowalker.org", nil) 174 | return j 175 | } 176 | 177 | type rawFile struct { 178 | name string 179 | rawURL string 180 | data []byte 181 | } 182 | 183 | func (rf *rawFile) Name() string { 184 | return rf.name 185 | } 186 | 187 | func (rf *rawFile) RawUrl() string { 188 | return rf.rawURL 189 | } 190 | 191 | func (rf *rawFile) Data() []byte { 192 | return rf.data 193 | } 194 | 195 | func (rf *rawFile) SetData(p []byte) { 196 | rf.data = p 197 | } 198 | 199 | func ExampleFetchFiles() { 200 | // Code that should be outside of your function body. 201 | // type rawFile struct { 202 | // name string 203 | // rawURL string 204 | // data []byte 205 | // } 206 | 207 | // func (rf *rawFile) Name() string { 208 | // return rf.name 209 | // } 210 | 211 | // func (rf *rawFile) RawUrl() string { 212 | // return rf.rawURL 213 | // } 214 | 215 | // func (rf *rawFile) Data() []byte { 216 | // return rf.data 217 | // } 218 | 219 | // func (rf *rawFile) SetData(p []byte) { 220 | // rf.data = p 221 | // } 222 | 223 | files := []com.RawFile{ 224 | &rawFile{rawURL: "http://example.com"}, 225 | &rawFile{rawURL: "http://example.com/foo"}, 226 | } 227 | err := com.FetchFiles(&http.Client{}, files, nil) 228 | fmt.Println(err, len(files[0].Data()), len(files[1].Data())) 229 | } 230 | 231 | func ExampleFetchFilesCurl() { 232 | // Code that should be outside of your function body. 233 | // type rawFile struct { 234 | // name string 235 | // rawURL string 236 | // data []byte 237 | // } 238 | 239 | // func (rf *rawFile) Name() string { 240 | // return rf.name 241 | // } 242 | 243 | // func (rf *rawFile) RawUrl() string { 244 | // return rf.rawURL 245 | // } 246 | 247 | // func (rf *rawFile) Data() []byte { 248 | // return rf.data 249 | // } 250 | 251 | // func (rf *rawFile) SetData(p []byte) { 252 | // rf.data = p 253 | // } 254 | 255 | files := []com.RawFile{ 256 | &rawFile{rawURL: "http://example.com"}, 257 | &rawFile{rawURL: "http://example.com/foo"}, 258 | } 259 | err := com.FetchFilesCurl(files) 260 | fmt.Println(err, len(files[0].Data()), len(files[1].Data())) 261 | } 262 | 263 | // ------------- END ------------ 264 | 265 | // ------------------------------ 266 | // regex.go 267 | // ------------------------------ 268 | 269 | func ExampleIsEmail() { 270 | fmt.Println(com.IsEmail("test@example.com")) 271 | fmt.Println(com.IsEmail("@example.com")) 272 | // Output: 273 | // true 274 | // false 275 | } 276 | 277 | func ExampleIsUrl() { 278 | fmt.Println(com.IsUrl("http://example.com")) 279 | fmt.Println(com.IsUrl("http//example.com")) 280 | // Output: 281 | // true 282 | // false 283 | } 284 | 285 | // ------------- END ------------ 286 | 287 | // ------------------------------ 288 | // slice.go 289 | // ------------------------------ 290 | 291 | func ExampleAppendStr() { 292 | s := []string{"a"} 293 | s = com.AppendStr(s, "a") 294 | s = com.AppendStr(s, "b") 295 | fmt.Println(s) 296 | // Output: [a b] 297 | } 298 | 299 | // ------------- END ------------ 300 | -------------------------------------------------------------------------------- /file.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "io/ioutil" 21 | "math" 22 | "os" 23 | "path" 24 | ) 25 | 26 | // Storage unit constants. 27 | const ( 28 | Byte = 1 29 | KByte = Byte * 1024 30 | MByte = KByte * 1024 31 | GByte = MByte * 1024 32 | TByte = GByte * 1024 33 | PByte = TByte * 1024 34 | EByte = PByte * 1024 35 | ) 36 | 37 | func logn(n, b float64) float64 { 38 | return math.Log(n) / math.Log(b) 39 | } 40 | 41 | func humanateBytes(s uint64, base float64, sizes []string) string { 42 | if s < 10 { 43 | return fmt.Sprintf("%dB", s) 44 | } 45 | e := math.Floor(logn(float64(s), base)) 46 | suffix := sizes[int(e)] 47 | val := float64(s) / math.Pow(base, math.Floor(e)) 48 | f := "%.0f" 49 | if val < 10 { 50 | f = "%.1f" 51 | } 52 | 53 | return fmt.Sprintf(f+"%s", val, suffix) 54 | } 55 | 56 | // HumaneFileSize calculates the file size and generate user-friendly string. 57 | func HumaneFileSize(s uint64) string { 58 | sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"} 59 | return humanateBytes(s, 1024, sizes) 60 | } 61 | 62 | // FileMTime returns file modified time and possible error. 63 | func FileMTime(file string) (int64, error) { 64 | f, err := os.Stat(file) 65 | if err != nil { 66 | return 0, err 67 | } 68 | return f.ModTime().Unix(), nil 69 | } 70 | 71 | // FileSize returns file size in bytes and possible error. 72 | func FileSize(file string) (int64, error) { 73 | f, err := os.Stat(file) 74 | if err != nil { 75 | return 0, err 76 | } 77 | return f.Size(), nil 78 | } 79 | 80 | // Copy copies file from source to target path. 81 | func Copy(src, dest string) error { 82 | // Gather file information to set back later. 83 | si, err := os.Lstat(src) 84 | if err != nil { 85 | return err 86 | } 87 | 88 | // Handle symbolic link. 89 | if si.Mode()&os.ModeSymlink != 0 { 90 | target, err := os.Readlink(src) 91 | if err != nil { 92 | return err 93 | } 94 | // NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link, 95 | // which will lead "no such file or directory" error. 96 | return os.Symlink(target, dest) 97 | } 98 | 99 | sr, err := os.Open(src) 100 | if err != nil { 101 | return err 102 | } 103 | defer sr.Close() 104 | 105 | dw, err := os.Create(dest) 106 | if err != nil { 107 | return err 108 | } 109 | defer dw.Close() 110 | 111 | if _, err = io.Copy(dw, sr); err != nil { 112 | return err 113 | } 114 | 115 | // Set back file information. 116 | if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil { 117 | return err 118 | } 119 | return os.Chmod(dest, si.Mode()) 120 | } 121 | 122 | // WriteFile writes data to a file named by filename. 123 | // If the file does not exist, WriteFile creates it 124 | // and its upper level paths. 125 | func WriteFile(filename string, data []byte) error { 126 | os.MkdirAll(path.Dir(filename), os.ModePerm) 127 | return ioutil.WriteFile(filename, data, 0655) 128 | } 129 | 130 | // IsFile returns true if given path is a file, 131 | // or returns false when it's a directory or does not exist. 132 | func IsFile(filePath string) bool { 133 | f, e := os.Stat(filePath) 134 | if e != nil { 135 | return false 136 | } 137 | return !f.IsDir() 138 | } 139 | 140 | // IsExist checks whether a file or directory exists. 141 | // It returns false when the file or directory does not exist. 142 | func IsExist(path string) bool { 143 | _, err := os.Stat(path) 144 | return err == nil || os.IsExist(err) 145 | } 146 | -------------------------------------------------------------------------------- /file_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "testing" 19 | 20 | . "github.com/smartystreets/goconvey/convey" 21 | ) 22 | 23 | func TestIsFile(t *testing.T) { 24 | if !IsFile("file.go") { 25 | t.Errorf("IsExist:\n Expect => %v\n Got => %v\n", true, false) 26 | } 27 | 28 | if IsFile("testdata") { 29 | t.Errorf("IsExist:\n Expect => %v\n Got => %v\n", false, true) 30 | } 31 | 32 | if IsFile("files.go") { 33 | t.Errorf("IsExist:\n Expect => %v\n Got => %v\n", false, true) 34 | } 35 | } 36 | 37 | func TestIsExist(t *testing.T) { 38 | Convey("Check if file or directory exists", t, func() { 39 | Convey("Pass a file name that exists", func() { 40 | So(IsExist("file.go"), ShouldEqual, true) 41 | }) 42 | Convey("Pass a directory name that exists", func() { 43 | So(IsExist("testdata"), ShouldEqual, true) 44 | }) 45 | Convey("Pass a directory name that does not exist", func() { 46 | So(IsExist(".hg"), ShouldEqual, false) 47 | }) 48 | }) 49 | } 50 | 51 | func BenchmarkIsFile(b *testing.B) { 52 | for i := 0; i < b.N; i++ { 53 | IsFile("file.go") 54 | } 55 | } 56 | 57 | func BenchmarkIsExist(b *testing.B) { 58 | for i := 0; i < b.N; i++ { 59 | IsExist("file.go") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/unknwon/com 2 | 3 | require ( 4 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect 5 | github.com/jtolds/gls v4.2.1+incompatible // indirect 6 | github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect 7 | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= 2 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 3 | github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= 4 | github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 5 | github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= 6 | github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 7 | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= 8 | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= 9 | -------------------------------------------------------------------------------- /html.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "html" 19 | "regexp" 20 | "strings" 21 | ) 22 | 23 | // Html2JS converts []byte type of HTML content into JS format. 24 | func Html2JS(data []byte) []byte { 25 | s := string(data) 26 | s = strings.Replace(s, `\`, `\\`, -1) 27 | s = strings.Replace(s, "\n", `\n`, -1) 28 | s = strings.Replace(s, "\r", "", -1) 29 | s = strings.Replace(s, "\"", `\"`, -1) 30 | s = strings.Replace(s, "", "<table>", -1) 31 | return []byte(s) 32 | } 33 | 34 | // encode html chars to string 35 | func HtmlEncode(str string) string { 36 | return html.EscapeString(str) 37 | } 38 | 39 | // HtmlDecode decodes string to html chars 40 | func HtmlDecode(str string) string { 41 | return html.UnescapeString(str) 42 | } 43 | 44 | // strip tags in html string 45 | func StripTags(src string) string { 46 | //去除style,script,html tag 47 | re := regexp.MustCompile(`(?s)<(?:style|script)[^<>]*>.*?|]*>|`) 48 | src = re.ReplaceAllString(src, "") 49 | 50 | //trim all spaces(2+) into \n 51 | re = regexp.MustCompile(`\s{2,}`) 52 | src = re.ReplaceAllString(src, "\n") 53 | 54 | return strings.TrimSpace(src) 55 | } 56 | 57 | // change \n to
58 | func Nl2br(str string) string { 59 | return strings.Replace(str, "\n", "
", -1) 60 | } 61 | -------------------------------------------------------------------------------- /html_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func TestHtml2JS(t *testing.T) { 22 | htm := "
Click me
\n\r" 23 | js := string(Html2JS([]byte(htm))) 24 | jsR := `
Click me
\n` 25 | if js != jsR { 26 | t.Errorf("Html2JS:\n Expect => %s\n Got => %s\n", jsR, js) 27 | } 28 | } 29 | 30 | func BenchmarkHtml2JS(b *testing.B) { 31 | htm := "
Click me
\n\r" 32 | for i := 0; i < b.N; i++ { 33 | Html2JS([]byte(htm)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "bytes" 19 | "encoding/json" 20 | "fmt" 21 | "io" 22 | "io/ioutil" 23 | "net/http" 24 | "os" 25 | "path" 26 | ) 27 | 28 | type NotFoundError struct { 29 | Message string 30 | } 31 | 32 | func (e NotFoundError) Error() string { 33 | return e.Message 34 | } 35 | 36 | type RemoteError struct { 37 | Host string 38 | Err error 39 | } 40 | 41 | func (e *RemoteError) Error() string { 42 | return e.Err.Error() 43 | } 44 | 45 | var UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36" 46 | 47 | // HttpCall makes HTTP method call. 48 | func HttpCall(client *http.Client, method, url string, header http.Header, body io.Reader) (io.ReadCloser, error) { 49 | req, err := http.NewRequest(method, url, body) 50 | if err != nil { 51 | return nil, err 52 | } 53 | req.Header.Set("User-Agent", UserAgent) 54 | for k, vs := range header { 55 | req.Header[k] = vs 56 | } 57 | resp, err := client.Do(req) 58 | if err != nil { 59 | return nil, err 60 | } 61 | if resp.StatusCode == 200 { 62 | return resp.Body, nil 63 | } 64 | resp.Body.Close() 65 | if resp.StatusCode == 404 { // 403 can be rate limit error. || resp.StatusCode == 403 { 66 | err = fmt.Errorf("resource not found: %s", url) 67 | } else { 68 | err = fmt.Errorf("%s %s -> %d", method, url, resp.StatusCode) 69 | } 70 | return nil, err 71 | } 72 | 73 | // HttpGet gets the specified resource. 74 | // ErrNotFound is returned if the server responds with status 404. 75 | func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) { 76 | return HttpCall(client, "GET", url, header, nil) 77 | } 78 | 79 | // HttpPost posts the specified resource. 80 | // ErrNotFound is returned if the server responds with status 404. 81 | func HttpPost(client *http.Client, url string, header http.Header, body []byte) (io.ReadCloser, error) { 82 | return HttpCall(client, "POST", url, header, bytes.NewBuffer(body)) 83 | } 84 | 85 | // HttpGetToFile gets the specified resource and writes to file. 86 | // ErrNotFound is returned if the server responds with status 404. 87 | func HttpGetToFile(client *http.Client, url string, header http.Header, fileName string) error { 88 | rc, err := HttpGet(client, url, header) 89 | if err != nil { 90 | return err 91 | } 92 | defer rc.Close() 93 | 94 | os.MkdirAll(path.Dir(fileName), os.ModePerm) 95 | f, err := os.Create(fileName) 96 | if err != nil { 97 | return err 98 | } 99 | defer f.Close() 100 | _, err = io.Copy(f, rc) 101 | return err 102 | } 103 | 104 | // HttpGetBytes gets the specified resource. ErrNotFound is returned if the server 105 | // responds with status 404. 106 | func HttpGetBytes(client *http.Client, url string, header http.Header) ([]byte, error) { 107 | rc, err := HttpGet(client, url, header) 108 | if err != nil { 109 | return nil, err 110 | } 111 | defer rc.Close() 112 | return ioutil.ReadAll(rc) 113 | } 114 | 115 | // HttpGetJSON gets the specified resource and mapping to struct. 116 | // ErrNotFound is returned if the server responds with status 404. 117 | func HttpGetJSON(client *http.Client, url string, v interface{}) error { 118 | rc, err := HttpGet(client, url, nil) 119 | if err != nil { 120 | return err 121 | } 122 | defer rc.Close() 123 | err = json.NewDecoder(rc).Decode(v) 124 | if _, ok := err.(*json.SyntaxError); ok { 125 | return fmt.Errorf("JSON syntax error at %s", url) 126 | } 127 | return nil 128 | } 129 | 130 | // HttpPostJSON posts the specified resource with struct values, 131 | // and maps results to struct. 132 | // ErrNotFound is returned if the server responds with status 404. 133 | func HttpPostJSON(client *http.Client, url string, body, v interface{}) error { 134 | data, err := json.Marshal(body) 135 | if err != nil { 136 | return err 137 | } 138 | rc, err := HttpPost(client, url, http.Header{"content-type": []string{"application/json"}}, data) 139 | if err != nil { 140 | return err 141 | } 142 | defer rc.Close() 143 | err = json.NewDecoder(rc).Decode(v) 144 | if _, ok := err.(*json.SyntaxError); ok { 145 | return fmt.Errorf("JSON syntax error at %s", url) 146 | } 147 | return nil 148 | } 149 | 150 | // A RawFile describes a file that can be downloaded. 151 | type RawFile interface { 152 | Name() string 153 | RawUrl() string 154 | Data() []byte 155 | SetData([]byte) 156 | } 157 | 158 | // FetchFiles fetches files specified by the rawURL field in parallel. 159 | func FetchFiles(client *http.Client, files []RawFile, header http.Header) error { 160 | ch := make(chan error, len(files)) 161 | for i := range files { 162 | go func(i int) { 163 | p, err := HttpGetBytes(client, files[i].RawUrl(), nil) 164 | if err != nil { 165 | ch <- err 166 | return 167 | } 168 | files[i].SetData(p) 169 | ch <- nil 170 | }(i) 171 | } 172 | for _ = range files { 173 | if err := <-ch; err != nil { 174 | return err 175 | } 176 | } 177 | return nil 178 | } 179 | 180 | // FetchFilesCurl uses command `curl` to fetch files specified by the rawURL field in parallel. 181 | func FetchFilesCurl(files []RawFile, curlOptions ...string) error { 182 | ch := make(chan error, len(files)) 183 | for i := range files { 184 | go func(i int) { 185 | stdout, _, err := ExecCmd("curl", append(curlOptions, files[i].RawUrl())...) 186 | if err != nil { 187 | ch <- err 188 | return 189 | } 190 | 191 | files[i].SetData([]byte(stdout)) 192 | ch <- nil 193 | }(i) 194 | } 195 | for _ = range files { 196 | if err := <-ch; err != nil { 197 | return err 198 | } 199 | } 200 | return nil 201 | } 202 | -------------------------------------------------------------------------------- /http_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "io/ioutil" 19 | "net/http" 20 | "strings" 21 | "testing" 22 | ) 23 | 24 | var examplePrefix = ` 25 | 26 | 27 | Example Domain 28 | ` 29 | 30 | func TestHttpGet(t *testing.T) { 31 | // 200. 32 | rc, err := HttpGet(&http.Client{}, "http://example.com", nil) 33 | if err != nil { 34 | t.Fatalf("HttpGet:\n Expect => %v\n Got => %s\n", nil, err) 35 | } 36 | p, err := ioutil.ReadAll(rc) 37 | if err != nil { 38 | t.Errorf("HttpGet:\n Expect => %v\n Got => %s\n", nil, err) 39 | } 40 | s := string(p) 41 | if !strings.HasPrefix(s, examplePrefix) { 42 | t.Errorf("HttpGet:\n Expect => %s\n Got => %s\n", examplePrefix, s) 43 | } 44 | } 45 | 46 | func TestHttpGetBytes(t *testing.T) { 47 | p, err := HttpGetBytes(&http.Client{}, "http://example.com", nil) 48 | if err != nil { 49 | t.Errorf("HttpGetBytes:\n Expect => %v\n Got => %s\n", nil, err) 50 | } 51 | s := string(p) 52 | if !strings.HasPrefix(s, examplePrefix) { 53 | t.Errorf("HttpGet:\n Expect => %s\n Got => %s\n", examplePrefix, s) 54 | } 55 | } 56 | 57 | func TestHttpGetJSON(t *testing.T) { 58 | 59 | } 60 | 61 | type rawFile struct { 62 | name string 63 | rawURL string 64 | data []byte 65 | } 66 | 67 | func (rf *rawFile) Name() string { 68 | return rf.name 69 | } 70 | 71 | func (rf *rawFile) RawUrl() string { 72 | return rf.rawURL 73 | } 74 | 75 | func (rf *rawFile) Data() []byte { 76 | return rf.data 77 | } 78 | 79 | func (rf *rawFile) SetData(p []byte) { 80 | rf.data = p 81 | } 82 | 83 | func TestFetchFiles(t *testing.T) { 84 | files := []RawFile{ 85 | &rawFile{rawURL: "http://example.com"}, 86 | &rawFile{rawURL: "http://example.com"}, 87 | } 88 | err := FetchFiles(&http.Client{}, files, nil) 89 | if err != nil { 90 | t.Errorf("FetchFiles:\n Expect => %v\n Got => %s\n", nil, err) 91 | } else if len(files[0].Data()) != 1270 { 92 | t.Errorf("FetchFiles:\n Expect => %d\n Got => %d\n", 1270, len(files[0].Data())) 93 | } else if len(files[1].Data()) != 1270 { 94 | t.Errorf("FetchFiles:\n Expect => %d\n Got => %d\n", 1270, len(files[1].Data())) 95 | } 96 | } 97 | 98 | func TestFetchFilesCurl(t *testing.T) { 99 | files := []RawFile{ 100 | &rawFile{rawURL: "http://example.com"}, 101 | &rawFile{rawURL: "http://example.com"}, 102 | } 103 | err := FetchFilesCurl(files) 104 | if err != nil { 105 | t.Errorf("FetchFilesCurl:\n Expect => %v\n Got => %s\n", nil, err) 106 | } else if len(files[0].Data()) != 1270 { 107 | t.Errorf("FetchFilesCurl:\n Expect => %d\n Got => %d\n", 1270, len(files[0].Data())) 108 | } else if len(files[1].Data()) != 1270 { 109 | t.Errorf("FetchFilesCurl:\n Expect => %d\n Got => %d\n", 1270, len(files[1].Data())) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /math.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | // PowInt is int type of math.Pow function. 18 | func PowInt(x int, y int) int { 19 | if y <= 0 { 20 | return 1 21 | } else { 22 | if y%2 == 0 { 23 | sqrt := PowInt(x, y/2) 24 | return sqrt * sqrt 25 | } else { 26 | return PowInt(x, y-1) * x 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /math_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "math" 19 | "math/rand" 20 | "testing" 21 | 22 | . "github.com/smartystreets/goconvey/convey" 23 | ) 24 | 25 | func Test_Pow(t *testing.T) { 26 | Convey("Power int", t, func() { 27 | for x := 0; x < 10; x++ { 28 | for y := 0; y < 8; y++ { 29 | result := PowInt(x, y) 30 | result_float := math.Pow(float64(x), float64(y)) 31 | So(result, ShouldEqual, int(result_float)) 32 | } 33 | } 34 | }) 35 | } 36 | 37 | func BenchmarkPow(b *testing.B) { 38 | x := rand.Intn(100) 39 | y := rand.Intn(6) 40 | b.ResetTimer() 41 | for n := 0; n < b.N; n++ { 42 | PowInt(x, y) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /path.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "errors" 19 | "os" 20 | "path/filepath" 21 | "runtime" 22 | "strings" 23 | ) 24 | 25 | // GetGOPATHs returns all paths in GOPATH variable. 26 | func GetGOPATHs() []string { 27 | gopath := os.Getenv("GOPATH") 28 | var paths []string 29 | if runtime.GOOS == "windows" { 30 | gopath = strings.Replace(gopath, "\\", "/", -1) 31 | paths = strings.Split(gopath, ";") 32 | } else { 33 | paths = strings.Split(gopath, ":") 34 | } 35 | return paths 36 | } 37 | 38 | // GetSrcPath returns app. source code path. 39 | // It only works when you have src. folder in GOPATH, 40 | // it returns error not able to locate source folder path. 41 | func GetSrcPath(importPath string) (appPath string, err error) { 42 | paths := GetGOPATHs() 43 | for _, p := range paths { 44 | if IsExist(p + "/src/" + importPath + "/") { 45 | appPath = p + "/src/" + importPath + "/" 46 | break 47 | } 48 | } 49 | 50 | if len(appPath) == 0 { 51 | return "", errors.New("Unable to locate source folder path") 52 | } 53 | 54 | appPath = filepath.Dir(appPath) + "/" 55 | if runtime.GOOS == "windows" { 56 | // Replace all '\' to '/'. 57 | appPath = strings.Replace(appPath, "\\", "/", -1) 58 | } 59 | 60 | return appPath, nil 61 | } 62 | 63 | // HomeDir returns path of '~'(in Linux) on Windows, 64 | // it returns error when the variable does not exist. 65 | func HomeDir() (home string, err error) { 66 | if runtime.GOOS == "windows" { 67 | home = os.Getenv("USERPROFILE") 68 | if len(home) == 0 { 69 | home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 70 | } 71 | } else { 72 | home = os.Getenv("HOME") 73 | } 74 | 75 | if len(home) == 0 { 76 | return "", errors.New("Cannot specify home directory because it's empty") 77 | } 78 | 79 | return home, nil 80 | } 81 | -------------------------------------------------------------------------------- /path_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "os" 19 | "runtime" 20 | "testing" 21 | ) 22 | 23 | func TestGetGOPATHs(t *testing.T) { 24 | var gpsR []string 25 | 26 | if runtime.GOOS != "windows" { 27 | gpsR = []string{"path/to/gopath1", "path/to/gopath2", "path/to/gopath3"} 28 | os.Setenv("GOPATH", "path/to/gopath1:path/to/gopath2:path/to/gopath3") 29 | } else { 30 | gpsR = []string{"path/to/gopath1", "path/to/gopath2", "path/to/gopath3"} 31 | os.Setenv("GOPATH", "path\\to\\gopath1;path\\to\\gopath2;path\\to\\gopath3") 32 | } 33 | 34 | gps := GetGOPATHs() 35 | if !CompareSliceStr(gps, gpsR) { 36 | t.Errorf("GetGOPATHs:\n Expect => %s\n Got => %s\n", gpsR, gps) 37 | } 38 | } 39 | 40 | func TestGetSrcPath(t *testing.T) { 41 | 42 | } 43 | 44 | func TestHomeDir(t *testing.T) { 45 | _, err := HomeDir() 46 | if err != nil { 47 | t.Errorf("HomeDir:\n Expect => %v\n Got => %s\n", nil, err) 48 | } 49 | } 50 | 51 | func BenchmarkGetGOPATHs(b *testing.B) { 52 | for i := 0; i < b.N; i++ { 53 | GetGOPATHs() 54 | } 55 | } 56 | 57 | func BenchmarkGetSrcPath(b *testing.B) { 58 | for i := 0; i < b.N; i++ { 59 | GetSrcPath("github.com/unknwon/com") 60 | } 61 | } 62 | 63 | func BenchmarkHomeDir(b *testing.B) { 64 | for i := 0; i < b.N; i++ { 65 | HomeDir() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /regex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import "regexp" 18 | 19 | const ( 20 | regex_email_pattern = `(?i)[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}` 21 | regex_strict_email_pattern = `(?i)[A-Z0-9!#$%&'*+/=?^_{|}~-]+` + 22 | `(?:\.[A-Z0-9!#$%&'*+/=?^_{|}~-]+)*` + 23 | `@(?:[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?\.)+` + 24 | `[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?` 25 | regex_url_pattern = `(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?` 26 | ) 27 | 28 | var ( 29 | regex_email *regexp.Regexp 30 | regex_strict_email *regexp.Regexp 31 | regex_url *regexp.Regexp 32 | ) 33 | 34 | func init() { 35 | regex_email = regexp.MustCompile(regex_email_pattern) 36 | regex_strict_email = regexp.MustCompile(regex_strict_email_pattern) 37 | regex_url = regexp.MustCompile(regex_url_pattern) 38 | } 39 | 40 | // IsEmail validates string is an email address, if not return false 41 | // basically validation can match 99% cases 42 | func IsEmail(email string) bool { 43 | return regex_email.MatchString(email) 44 | } 45 | 46 | // IsEmailRFC validates string is an email address, if not return false 47 | // this validation omits RFC 2822 48 | func IsEmailRFC(email string) bool { 49 | return regex_strict_email.MatchString(email) 50 | } 51 | 52 | // IsUrl validates string is a url link, if not return false 53 | // simple validation can match 99% cases 54 | func IsUrl(url string) bool { 55 | return regex_url.MatchString(url) 56 | } 57 | -------------------------------------------------------------------------------- /regex_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func TestIsEmail(t *testing.T) { 22 | emails := map[string]bool{ 23 | `test@example.com`: true, 24 | `single-character@b.org`: true, 25 | `uncommon_address@test.museum`: true, 26 | `local@sld.UPPER`: true, 27 | `@missing.org`: false, 28 | `missing@.com`: false, 29 | `missing@qq.`: false, 30 | `wrong-ip@127.1.1.1.26`: false, 31 | } 32 | for e, r := range emails { 33 | b := IsEmail(e) 34 | if b != r { 35 | t.Errorf("IsEmail:\n Expect => %v\n Got => %v\n", r, b) 36 | } 37 | } 38 | } 39 | 40 | func TestIsUrl(t *testing.T) { 41 | urls := map[string]bool{ 42 | "http://www.example.com": true, 43 | "http://example.com": true, 44 | "http://example.com?user=test&password=test": true, 45 | "http://example.com?user=test#login": true, 46 | "ftp://example.com": true, 47 | "https://example.com": true, 48 | "htp://example.com": false, 49 | "http//example.com": false, 50 | "http://example": true, 51 | } 52 | for u, r := range urls { 53 | b := IsUrl(u) 54 | if b != r { 55 | t.Errorf("IsUrl:\n Expect => %v\n Got => %v\n", r, b) 56 | } 57 | } 58 | } 59 | 60 | func BenchmarkIsEmail(b *testing.B) { 61 | for i := 0; i < b.N; i++ { 62 | IsEmail("test@example.com") 63 | } 64 | } 65 | 66 | func BenchmarkIsUrl(b *testing.B) { 67 | for i := 0; i < b.N; i++ { 68 | IsEmail("http://example.com") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /slice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | // AppendStr appends string to slice with no duplicates. 22 | func AppendStr(strs []string, str string) []string { 23 | for _, s := range strs { 24 | if s == str { 25 | return strs 26 | } 27 | } 28 | return append(strs, str) 29 | } 30 | 31 | // CompareSliceStr compares two 'string' type slices. 32 | // It returns true if elements and order are both the same. 33 | func CompareSliceStr(s1, s2 []string) bool { 34 | if len(s1) != len(s2) { 35 | return false 36 | } 37 | 38 | for i := range s1 { 39 | if s1[i] != s2[i] { 40 | return false 41 | } 42 | } 43 | 44 | return true 45 | } 46 | 47 | // CompareSliceStrU compares two 'string' type slices. 48 | // It returns true if elements are the same, and ignores the order. 49 | func CompareSliceStrU(s1, s2 []string) bool { 50 | if len(s1) != len(s2) { 51 | return false 52 | } 53 | 54 | for i := range s1 { 55 | for j := len(s2) - 1; j >= 0; j-- { 56 | if s1[i] == s2[j] { 57 | s2 = append(s2[:j], s2[j+1:]...) 58 | break 59 | } 60 | } 61 | } 62 | if len(s2) > 0 { 63 | return false 64 | } 65 | return true 66 | } 67 | 68 | // IsSliceContainsStr returns true if the string exists in given slice, ignore case. 69 | func IsSliceContainsStr(sl []string, str string) bool { 70 | str = strings.ToLower(str) 71 | for _, s := range sl { 72 | if strings.ToLower(s) == str { 73 | return true 74 | } 75 | } 76 | return false 77 | } 78 | 79 | // IsSliceContainsInt64 returns true if the int64 exists in given slice. 80 | func IsSliceContainsInt64(sl []int64, i int64) bool { 81 | for _, s := range sl { 82 | if s == i { 83 | return true 84 | } 85 | } 86 | return false 87 | } 88 | -------------------------------------------------------------------------------- /slice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | . "github.com/smartystreets/goconvey/convey" 22 | ) 23 | 24 | func TestAppendStr(t *testing.T) { 25 | Convey("Append a string to a slice with no duplicates", t, func() { 26 | s := []string{"a"} 27 | 28 | Convey("Append a string that does not exist in slice", func() { 29 | s = AppendStr(s, "b") 30 | So(len(s), ShouldEqual, 2) 31 | }) 32 | 33 | Convey("Append a string that does exist in slice", func() { 34 | s = AppendStr(s, "b") 35 | So(len(s), ShouldEqual, 2) 36 | }) 37 | }) 38 | } 39 | 40 | func TestCompareSliceStr(t *testing.T) { 41 | Convey("Compares two 'string' type slices with elements and order", t, func() { 42 | Convey("Compare two slices that do have same elements and order", func() { 43 | So(CompareSliceStr( 44 | []string{"1", "2", "3"}, []string{"1", "2", "3"}), ShouldBeTrue) 45 | }) 46 | 47 | Convey("Compare two slices that do have same elements but does not have same order", func() { 48 | So(!CompareSliceStr( 49 | []string{"2", "1", "3"}, []string{"1", "2", "3"}), ShouldBeTrue) 50 | }) 51 | 52 | Convey("Compare two slices that have different number of elements", func() { 53 | So(!CompareSliceStr( 54 | []string{"2", "1"}, []string{"1", "2", "3"}), ShouldBeTrue) 55 | }) 56 | }) 57 | } 58 | 59 | func TestCompareSliceStrU(t *testing.T) { 60 | Convey("Compare two 'string' type slices with elements and ignore the order", t, func() { 61 | Convey("Compare two slices that do have same elements and order", func() { 62 | So(CompareSliceStrU( 63 | []string{"1", "2", "3"}, []string{"1", "2", "3"}), ShouldBeTrue) 64 | }) 65 | 66 | Convey("Compare two slices that do have same elements but does not have same order", func() { 67 | So(CompareSliceStrU( 68 | []string{"2", "1", "3"}, []string{"1", "2", "3"}), ShouldBeTrue) 69 | }) 70 | 71 | Convey("Compare two slices that have different number of elements", func() { 72 | So(!CompareSliceStrU( 73 | []string{"2", "1"}, []string{"1", "2", "3"}), ShouldBeTrue) 74 | }) 75 | }) 76 | } 77 | 78 | func BenchmarkAppendStr(b *testing.B) { 79 | s := []string{"a"} 80 | for i := 0; i < b.N; i++ { 81 | s = AppendStr(s, fmt.Sprint(b.N%3)) 82 | } 83 | } 84 | 85 | func BenchmarkCompareSliceStr(b *testing.B) { 86 | s1 := []string{"1", "2", "3"} 87 | s2 := []string{"1", "2", "3"} 88 | for i := 0; i < b.N; i++ { 89 | CompareSliceStr(s1, s2) 90 | } 91 | } 92 | 93 | func BenchmarkCompareSliceStrU(b *testing.B) { 94 | s1 := []string{"1", "4", "2", "3"} 95 | s2 := []string{"1", "2", "3", "4"} 96 | for i := 0; i < b.N; i++ { 97 | CompareSliceStrU(s1, s2) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "bytes" 19 | "crypto/aes" 20 | "crypto/cipher" 21 | "crypto/rand" 22 | "errors" 23 | r "math/rand" 24 | "strconv" 25 | "strings" 26 | "time" 27 | "unicode" 28 | "unicode/utf8" 29 | ) 30 | 31 | // AESGCMEncrypt encrypts plaintext with the given key using AES in GCM mode. 32 | func AESGCMEncrypt(key, plaintext []byte) ([]byte, error) { 33 | block, err := aes.NewCipher(key) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | gcm, err := cipher.NewGCM(block) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | nonce := make([]byte, gcm.NonceSize()) 44 | if _, err := rand.Read(nonce); err != nil { 45 | return nil, err 46 | } 47 | 48 | ciphertext := gcm.Seal(nil, nonce, plaintext, nil) 49 | return append(nonce, ciphertext...), nil 50 | } 51 | 52 | // AESGCMDecrypt decrypts ciphertext with the given key using AES in GCM mode. 53 | func AESGCMDecrypt(key, ciphertext []byte) ([]byte, error) { 54 | block, err := aes.NewCipher(key) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | gcm, err := cipher.NewGCM(block) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | size := gcm.NonceSize() 65 | if len(ciphertext)-size <= 0 { 66 | return nil, errors.New("Ciphertext is empty") 67 | } 68 | 69 | nonce := ciphertext[:size] 70 | ciphertext = ciphertext[size:] 71 | 72 | plainText, err := gcm.Open(nil, nonce, ciphertext, nil) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | return plainText, nil 78 | } 79 | 80 | // IsLetter returns true if the 'l' is an English letter. 81 | func IsLetter(l uint8) bool { 82 | n := (l | 0x20) - 'a' 83 | if n >= 0 && n < 26 { 84 | return true 85 | } 86 | return false 87 | } 88 | 89 | // Expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match. 90 | func Expand(template string, match map[string]string, subs ...string) string { 91 | var p []byte 92 | var i int 93 | for { 94 | i = strings.Index(template, "{") 95 | if i < 0 { 96 | break 97 | } 98 | p = append(p, template[:i]...) 99 | template = template[i+1:] 100 | i = strings.Index(template, "}") 101 | if s, ok := match[template[:i]]; ok { 102 | p = append(p, s...) 103 | } else { 104 | j, _ := strconv.Atoi(template[:i]) 105 | if j >= len(subs) { 106 | p = append(p, []byte("Missing")...) 107 | } else { 108 | p = append(p, subs[j]...) 109 | } 110 | } 111 | template = template[i+1:] 112 | } 113 | p = append(p, template...) 114 | return string(p) 115 | } 116 | 117 | // Reverse s string, support unicode 118 | func Reverse(s string) string { 119 | n := len(s) 120 | runes := make([]rune, n) 121 | for _, rune := range s { 122 | n-- 123 | runes[n] = rune 124 | } 125 | return string(runes[n:]) 126 | } 127 | 128 | // RandomCreateBytes generate random []byte by specify chars. 129 | func RandomCreateBytes(n int, alphabets ...byte) []byte { 130 | const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 131 | var bytes = make([]byte, n) 132 | var randby bool 133 | if num, err := rand.Read(bytes); num != n || err != nil { 134 | r.Seed(time.Now().UnixNano()) 135 | randby = true 136 | } 137 | for i, b := range bytes { 138 | if len(alphabets) == 0 { 139 | if randby { 140 | bytes[i] = alphanum[r.Intn(len(alphanum))] 141 | } else { 142 | bytes[i] = alphanum[b%byte(len(alphanum))] 143 | } 144 | } else { 145 | if randby { 146 | bytes[i] = alphabets[r.Intn(len(alphabets))] 147 | } else { 148 | bytes[i] = alphabets[b%byte(len(alphabets))] 149 | } 150 | } 151 | } 152 | return bytes 153 | } 154 | 155 | // ToSnakeCase can convert all upper case characters in a string to 156 | // underscore format. 157 | // 158 | // Some samples. 159 | // "FirstName" => "first_name" 160 | // "HTTPServer" => "http_server" 161 | // "NoHTTPS" => "no_https" 162 | // "GO_PATH" => "go_path" 163 | // "GO PATH" => "go_path" // space is converted to underscore. 164 | // "GO-PATH" => "go_path" // hyphen is converted to underscore. 165 | // 166 | // From https://github.com/huandu/xstrings 167 | func ToSnakeCase(str string) string { 168 | if len(str) == 0 { 169 | return "" 170 | } 171 | 172 | buf := &bytes.Buffer{} 173 | var prev, r0, r1 rune 174 | var size int 175 | 176 | r0 = '_' 177 | 178 | for len(str) > 0 { 179 | prev = r0 180 | r0, size = utf8.DecodeRuneInString(str) 181 | str = str[size:] 182 | 183 | switch { 184 | case r0 == utf8.RuneError: 185 | buf.WriteByte(byte(str[0])) 186 | 187 | case unicode.IsUpper(r0): 188 | if prev != '_' { 189 | buf.WriteRune('_') 190 | } 191 | 192 | buf.WriteRune(unicode.ToLower(r0)) 193 | 194 | if len(str) == 0 { 195 | break 196 | } 197 | 198 | r0, size = utf8.DecodeRuneInString(str) 199 | str = str[size:] 200 | 201 | if !unicode.IsUpper(r0) { 202 | buf.WriteRune(r0) 203 | break 204 | } 205 | 206 | // find next non-upper-case character and insert `_` properly. 207 | // it's designed to convert `HTTPServer` to `http_server`. 208 | // if there are more than 2 adjacent upper case characters in a word, 209 | // treat them as an abbreviation plus a normal word. 210 | for len(str) > 0 { 211 | r1 = r0 212 | r0, size = utf8.DecodeRuneInString(str) 213 | str = str[size:] 214 | 215 | if r0 == utf8.RuneError { 216 | buf.WriteRune(unicode.ToLower(r1)) 217 | buf.WriteByte(byte(str[0])) 218 | break 219 | } 220 | 221 | if !unicode.IsUpper(r0) { 222 | if r0 == '_' || r0 == ' ' || r0 == '-' { 223 | r0 = '_' 224 | 225 | buf.WriteRune(unicode.ToLower(r1)) 226 | } else { 227 | buf.WriteRune('_') 228 | buf.WriteRune(unicode.ToLower(r1)) 229 | buf.WriteRune(r0) 230 | } 231 | 232 | break 233 | } 234 | 235 | buf.WriteRune(unicode.ToLower(r1)) 236 | } 237 | 238 | if len(str) == 0 || r0 == '_' { 239 | buf.WriteRune(unicode.ToLower(r0)) 240 | break 241 | } 242 | 243 | default: 244 | if r0 == ' ' || r0 == '-' { 245 | r0 = '_' 246 | } 247 | 248 | buf.WriteRune(r0) 249 | } 250 | } 251 | 252 | return buf.String() 253 | } 254 | -------------------------------------------------------------------------------- /string_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "bytes" 19 | "crypto/rand" 20 | "testing" 21 | 22 | . "github.com/smartystreets/goconvey/convey" 23 | ) 24 | 25 | func TestAESEncrypt(t *testing.T) { 26 | t.Parallel() 27 | 28 | key := make([]byte, 16) // AES-128 29 | _, err := rand.Read(key) 30 | if err != nil { 31 | t.Fatal("Failed to create 128 bit AES key: " + err.Error()) 32 | } 33 | 34 | plaintext := []byte("this will be encrypted") 35 | 36 | _, err = AESGCMEncrypt(key, plaintext) 37 | if err != nil { 38 | t.Fatal("Failed to encrypt plaintext: " + err.Error()) 39 | } 40 | } 41 | 42 | func TestAESDecrypt(t *testing.T) { 43 | t.Parallel() 44 | 45 | key := make([]byte, 16) // AES-128 46 | _, err := rand.Read(key) 47 | if err != nil { 48 | t.Fatal("Failed to create 128 bit AES key: " + err.Error()) 49 | } 50 | 51 | plaintext := []byte("this will be encrypted") 52 | 53 | ciphertext, err := AESGCMEncrypt(key, plaintext) 54 | if err != nil { 55 | t.Fatal("Failed to encrypt plaintext: " + err.Error()) 56 | } 57 | 58 | decrypted, err := AESGCMDecrypt(key, ciphertext) 59 | if err != nil { 60 | t.Fatal("Failed to decrypt ciphertext: " + err.Error()) 61 | } 62 | 63 | if bytes.Compare(decrypted, plaintext) != 0 { 64 | t.Fatal("Decryption was not performed correctly") 65 | } 66 | } 67 | 68 | func TestIsLetter(t *testing.T) { 69 | if IsLetter('1') { 70 | t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", false, true) 71 | } 72 | 73 | if IsLetter('[') { 74 | t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", false, true) 75 | } 76 | 77 | if !IsLetter('a') { 78 | t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", true, false) 79 | } 80 | 81 | if !IsLetter('Z') { 82 | t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", true, false) 83 | } 84 | } 85 | 86 | func TestExpand(t *testing.T) { 87 | match := map[string]string{ 88 | "domain": "gowalker.org", 89 | "subdomain": "github.com", 90 | } 91 | s := "http://{domain}/{subdomain}/{0}/{1}" 92 | sR := "http://gowalker.org/github.com/unknwon/gowalker" 93 | if Expand(s, match, "unknwon", "gowalker") != sR { 94 | t.Errorf("Expand:\n Expect => %s\n Got => %s\n", sR, s) 95 | } 96 | } 97 | 98 | func TestReverse(t *testing.T) { 99 | if Reverse("abcdefg") != "gfedcba" { 100 | t.Errorf("Reverse:\n Except => %s\n Got =>%s\n", "gfedcba", Reverse("abcdefg")) 101 | } 102 | if Reverse("上善若水厚德载物") != "物载德厚水若善上" { 103 | t.Errorf("Reverse:\n Except => %s\n Got =>%s\n", "物载德厚水若善上", Reverse("上善若水厚德载物")) 104 | } 105 | } 106 | 107 | func Test_ToSnakeCase(t *testing.T) { 108 | cases := map[string]string{ 109 | "HTTPServer": "http_server", 110 | "_camelCase": "_camel_case", 111 | "NoHTTPS": "no_https", 112 | "Wi_thF": "wi_th_f", 113 | "_AnotherTES_TCaseP": "_another_tes_t_case_p", 114 | "ALL": "all", 115 | "_HELLO_WORLD_": "_hello_world_", 116 | "HELLO_WORLD": "hello_world", 117 | "HELLO____WORLD": "hello____world", 118 | "TW": "tw", 119 | "_C": "_c", 120 | 121 | " sentence case ": "__sentence_case__", 122 | " Mixed-hyphen case _and SENTENCE_case and UPPER-case": "_mixed_hyphen_case__and_sentence_case_and_upper_case", 123 | } 124 | Convey("Convert string into snake case", t, func() { 125 | for old, new := range cases { 126 | So(ToSnakeCase(old), ShouldEqual, new) 127 | } 128 | }) 129 | } 130 | 131 | func BenchmarkIsLetter(b *testing.B) { 132 | for i := 0; i < b.N; i++ { 133 | IsLetter('a') 134 | } 135 | } 136 | 137 | func BenchmarkExpand(b *testing.B) { 138 | match := map[string]string{ 139 | "domain": "gowalker.org", 140 | "subdomain": "github.com", 141 | } 142 | s := "http://{domain}/{subdomain}/{0}/{1}" 143 | for i := 0; i < b.N; i++ { 144 | Expand(s, match, "Unknwon", "gowalker") 145 | } 146 | } 147 | 148 | func BenchmarkReverse(b *testing.B) { 149 | s := "abscef中文" 150 | for i := 0; i < b.N; i++ { 151 | Reverse(s) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /testdata/SaveFile.txt: -------------------------------------------------------------------------------- 1 | TestSaveFile -------------------------------------------------------------------------------- /testdata/SaveFileS.txt: -------------------------------------------------------------------------------- 1 | TestSaveFileS -------------------------------------------------------------------------------- /testdata/sample_file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/com/b41c64acd94be7e673c9c8301344d31cce99e06c/testdata/sample_file.txt -------------------------------------------------------------------------------- /testdata/statDir/SaveFile.txt: -------------------------------------------------------------------------------- 1 | TestSaveFile -------------------------------------------------------------------------------- /testdata/statDir/SaveFileS.txt: -------------------------------------------------------------------------------- 1 | TestSaveFileS -------------------------------------------------------------------------------- /testdata/statDir/sample_file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/com/b41c64acd94be7e673c9c8301344d31cce99e06c/testdata/statDir/sample_file.txt -------------------------------------------------------------------------------- /testdata/statDir/secondLevel/SaveFile.txt: -------------------------------------------------------------------------------- 1 | TestSaveFile -------------------------------------------------------------------------------- /testdata/statDir/secondLevel/SaveFileS.txt: -------------------------------------------------------------------------------- 1 | TestSaveFileS -------------------------------------------------------------------------------- /testdata/statDir/secondLevel/sample_file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/com/b41c64acd94be7e673c9c8301344d31cce99e06c/testdata/statDir/secondLevel/sample_file.txt -------------------------------------------------------------------------------- /time.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "fmt" 19 | "strconv" 20 | "strings" 21 | "time" 22 | ) 23 | 24 | // Format unix time int64 to string 25 | func Date(ti int64, format string) string { 26 | t := time.Unix(int64(ti), 0) 27 | return DateT(t, format) 28 | } 29 | 30 | // Format unix time string to string 31 | func DateS(ts string, format string) string { 32 | i, _ := strconv.ParseInt(ts, 10, 64) 33 | return Date(i, format) 34 | } 35 | 36 | // Format time.Time struct to string 37 | // MM - month - 01 38 | // M - month - 1, single bit 39 | // DD - day - 02 40 | // D - day 2 41 | // YYYY - year - 2006 42 | // YY - year - 06 43 | // HH - 24 hours - 03 44 | // H - 24 hours - 3 45 | // hh - 12 hours - 03 46 | // h - 12 hours - 3 47 | // mm - minute - 04 48 | // m - minute - 4 49 | // ss - second - 05 50 | // s - second = 5 51 | func DateT(t time.Time, format string) string { 52 | res := strings.Replace(format, "MM", t.Format("01"), -1) 53 | res = strings.Replace(res, "M", t.Format("1"), -1) 54 | res = strings.Replace(res, "DD", t.Format("02"), -1) 55 | res = strings.Replace(res, "D", t.Format("2"), -1) 56 | res = strings.Replace(res, "YYYY", t.Format("2006"), -1) 57 | res = strings.Replace(res, "YY", t.Format("06"), -1) 58 | res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1) 59 | res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1) 60 | res = strings.Replace(res, "hh", t.Format("03"), -1) 61 | res = strings.Replace(res, "h", t.Format("3"), -1) 62 | res = strings.Replace(res, "mm", t.Format("04"), -1) 63 | res = strings.Replace(res, "m", t.Format("4"), -1) 64 | res = strings.Replace(res, "ss", t.Format("05"), -1) 65 | res = strings.Replace(res, "s", t.Format("5"), -1) 66 | return res 67 | } 68 | 69 | // DateFormat pattern rules. 70 | var datePatterns = []string{ 71 | // year 72 | "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 73 | "y", "06", //A two digit representation of a year Examples: 99 or 03 74 | 75 | // month 76 | "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 77 | "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 78 | "M", "Jan", // A short textual representation of a month, three letters Jan through Dec 79 | "F", "January", // A full textual representation of a month, such as January or March January through December 80 | 81 | // day 82 | "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 83 | "j", "2", // Day of the month without leading zeros 1 to 31 84 | 85 | // week 86 | "D", "Mon", // A textual representation of a day, three letters Mon through Sun 87 | "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday 88 | 89 | // time 90 | "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 91 | "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 92 | "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 93 | "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 94 | 95 | "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm 96 | "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM 97 | 98 | "i", "04", // Minutes with leading zeros 00 to 59 99 | "s", "05", // Seconds, with leading zeros 00 through 59 100 | 101 | // time zone 102 | "T", "MST", 103 | "P", "-07:00", 104 | "O", "-0700", 105 | 106 | // RFC 2822 107 | "r", time.RFC1123Z, 108 | } 109 | 110 | // Parse Date use PHP time format. 111 | func DateParse(dateString, format string) (time.Time, error) { 112 | replacer := strings.NewReplacer(datePatterns...) 113 | format = replacer.Replace(format) 114 | return time.ParseInLocation(format, dateString, time.Local) 115 | } 116 | -------------------------------------------------------------------------------- /url.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 com authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package com 16 | 17 | import ( 18 | "encoding/base64" 19 | "net/url" 20 | ) 21 | 22 | // url encode string, is + not %20 23 | func UrlEncode(str string) string { 24 | return url.QueryEscape(str) 25 | } 26 | 27 | // url decode string 28 | func UrlDecode(str string) (string, error) { 29 | return url.QueryUnescape(str) 30 | } 31 | 32 | // base64 encode 33 | func Base64Encode(str string) string { 34 | return base64.StdEncoding.EncodeToString([]byte(str)) 35 | } 36 | 37 | // base64 decode 38 | func Base64Decode(str string) (string, error) { 39 | s, e := base64.StdEncoding.DecodeString(str) 40 | return string(s), e 41 | } 42 | --------------------------------------------------------------------------------