github.com/viant/toolbox@v0.34.5/README.md (about) 1 # Toolbox - go utility library 2 3 [](https://goreportcard.com/report/github.com/viant/toolbox) 4 [](https://godoc.org/github.com/viant/toolbox) 5 6 This library is compatible with Go 1.8+ 7 8 Please refer to [`CHANGELOG.md`](CHANGELOG.md) if you encounter breaking changes. 9 10 - [Motivation](#Motivation) 11 - [Collection Utilities](#Collection-Utilities) 12 - [Converter && Conversion Utilities](#Conversion-Utilities) 13 - [Struct Utilities](#Struct-Utilities) 14 - [Function Utilities](#Function-Utilities) 15 - [Time Utilities](#TimeUtilities) 16 - [Storage API](#storage) 17 - [Data substitution](data/) 18 - [Text Utilities](text/) 19 - [ServiceRouter](#ServiceRouter) 20 - [Decoder and Encoder](#DecoderandEncoder) 21 - [Logger](#Logger) 22 - [BatchLimiter](#BatchLimiter) 23 - [AST Based FileSetInfo](#ast-based-filesetinfo) 24 - [License](#License) 25 - [Credits and Acknowledgements](#Credits-and-Acknowledgements) 26 27 28 29 ## Motivation 30 31 This library was developed as part of [Datastore Connectivity](https://github.com/viant/dsc/) and Testibility libraries: ([Assertly](https://github.com/viant/assertly), [Datastore testing](https://github.com/viant/dsunit/), [End to end testing](https://github.com/viant/endly/)) 32 as a way to share utilities, and other abstractions that may be useful in other projects. 33 34 35 <a name="Collection-Utilities"></a> 36 37 ### Collection Utilities 38 39 40 #### Iterator 41 42 Example 43 ```go 44 slice := []string{"a", "z", "c"} 45 iterator := toolbox.NewSliceIterator(slice) 46 value := "" 47 for iterator.HasNext() { 48 iterator.Next(&value) 49 ... 50 } 51 ``` 52 53 #### Slice utilities 54 55 The following methods work on **any slice type.** 56 57 **ProcessSlice** 58 59 Example 60 ```go 61 var aSlice interface{} 62 63 toolbox.ProcessSlice(aSlice, func(item interface{}) bool { 64 ... 65 return true //to continue to next element return true 66 }) 67 68 ``` 69 70 71 **ProcessSliceWithIndex** 72 73 Example: 74 ```go 75 var aSlice interface{} 76 77 toolbox.ProcessSlice(aSlice, func(index int, item interface{}) bool { 78 ... 79 return true //to continue to next element return true 80 }) 81 82 ``` 83 84 85 **IndexSlice** 86 87 Example: 88 ```go 89 type Foo struct{ 90 id int 91 name string 92 } 93 94 var aSlice = []Foo{ Foo{1, "A"}, Foo{2, "B"} } 95 var indexedMap = make(map[int]Foo) 96 97 toolbox.IndexSlice(aSlice, indexedMap, func(foo Foo) int { 98 return foo.id 99 }) 100 101 102 ``` 103 104 105 **CopySliceElements** 106 107 Example: 108 ```go 109 source := []interface{}{ 110 "abc", "def", "cyz", 111 } 112 var target = make([]string, 0) 113 toolbox.CopySliceElements(source, &target) 114 115 116 ``` 117 118 119 **FilterSliceElements** 120 121 Example: 122 ```go 123 source := []interface{}{ 124 "abc", "def", "cyz","adc", 125 } 126 var target = make([]string, 0) 127 128 toolbox.FilterSliceElements(source, func(item string) bool { 129 return strings.HasPrefix(item, "a") //this matches any elements starting with a 130 }, &target) 131 ``` 132 133 134 **HasSliceAnyElements** 135 136 Example: 137 ```go 138 source := []interface{}{ 139 "abc", "def", "cyz","adc", 140 } 141 toolbox.HasSliceAnyElements(source, "cyz") 142 ``` 143 144 145 **SliceToMap** 146 147 Example: 148 ```go 149 var source = []Foo{ Foo{1, "A"}, Foo{2, "B"} } 150 var target = make(map[int]string) 151 toolbox.SliceToMap(source, target, func(foo Foo) int { 152 return foo.id 153 }, 154 func(foo Foo) string { 155 return foo.name 156 }) 157 158 159 ``` 160 161 162 **TransformSlice** 163 164 Example: 165 ```go 166 type Product struct{ vendor, name string } 167 products := []Product{ 168 Product{"Vendor1", "Product1"}, 169 Product{"Vendor2", "Product2"}, 170 Product{"Vendor1", "Product3"}, 171 Product{"Vendor1", "Product4"}, 172 } 173 var vendors=make([]string, 0) 174 toolbox.TransformSlice(products, &vendors, func(product Product) string { 175 return product.vendor 176 }) 177 ``` 178 179 180 #### Map utilities 181 182 **ProcessMap** 183 184 The following methods work on **any map type.** 185 186 Example: 187 ```go 188 var aMap interface{} 189 toolbox.ProcessMap(aMap, func(key, value interface{}) bool { 190 ... 191 return true //to continue to next element return true 192 }) 193 194 ``` 195 196 197 **CopyMapEntries** 198 199 Example: 200 ```go 201 type Foo struct{id int;name string} 202 203 source := map[interface{}]interface{} { 204 1: Foo{1, "A"}, 205 2: Foo{2, "B"}, 206 } 207 var target = make (map[int]Foo) 208 209 toolbox.CopyMapEntries(source, target) 210 ``` 211 212 213 **MapKeysToSlice** 214 215 Example: 216 ```go 217 aMap := map[string]int { 218 "abc":1, 219 "efg":2, 220 } 221 var keys = make([]string, 0) 222 toolbox.MapKeysToSlice(aMap, &keys) 223 ``` 224 225 226 **GroupSliceElements** 227 228 Example: 229 ```go 230 type Product struct{vendor,name string} 231 products := []Product{ 232 Product{"Vendor1", "Product1"}, 233 Product{"Vendor2", "Product2"}, 234 Product{"Vendor1", "Product3"}, 235 Product{"Vendor1", "Product4"}, 236 } 237 238 productsByVendor := make(map[string][]Product) 239 toolbox.GroupSliceElements(products, productsByVendor, func(product Product) string { 240 return product.vendor 241 }) 242 ``` 243 244 245 **SliceToMultimap** 246 247 ```go 248 type Product struct { 249 vendor, name string 250 productId int 251 } 252 253 products := []Product{ 254 Product{"Vendor1", "Product1", 1}, 255 Product{"Vendor2", "Product2", 2}, 256 Product{"Vendor1", "Product3", 3}, 257 Product{"Vendor1", "Product4", 4}, 258 } 259 260 productsByVendor := make(map[string][]int) 261 toolbox.SliceToMultimap(products, productsByVendor, func(product Product) string { 262 return product.vendor 263 }, 264 func(product Product) int { 265 return product.productId 266 }) 267 ``` 268 269 <a name="Conversion-Utilities"></a> 270 ### Converter && Conversion Utilities 271 272 Converter transforms, data between any compatible or incompatible data type including struct/basicType/map/slice/interface{} 273 On top of that it supports custom tag to map field to target data type (i.e map) 274 275 276 ```go 277 myStruct := //some struct ... 278 myMap := make(map[string]interface{}) 279 converter := NewConverter(dateLayout, keyTag) 280 err = converter.AssignConverted(&myMap, myStruct) 281 err = converter.AssignConverted(myStruct, myMap) 282 ``` 283 284 285 <a name="Struct-Utilities"></a> 286 ### Struct Utilities 287 288 289 **ScanStructMethods** 290 291 Scan struct methods 292 293 ```go 294 service := New() 295 err = toolbox.ScanStructMethods(service, 1, func(method reflect.Method) error { 296 fmt.Printf("%v\n", method.Name) 297 return nil 298 }) 299 300 ``` 301 302 **ProcessStruct** 303 304 Scan struct fields 305 306 ```go 307 service := New() 308 err = toolbox.ProcessStruct(service, 309 func(field reflect.StructField, value reflect.Value) error { 310 fmt.Print(field.Type.Name) 311 return nil 312 }) 313 314 ``` 315 316 317 <a name="Function-Utilities"></a> 318 ### Function Utilities 319 320 321 322 323 <a name="TimeUtilities"></a> 324 ### Time Utilities 325 326 **DateFormatToLayout** 327 328 Java date format style to go date layout conversion. 329 330 331 ```go 332 dateLaout := toolbox.DateFormatToLayout("yyyy-MM-dd hh:mm:ss z") 333 timeValue, err := time.Parse(dateLaout, "2016-02-22 12:32:01 UTC") 334 ``` 335 336 **TimeAt** 337 338 Provide dynamic semantic for creating time object 339 340 ```go 341 342 tomorrow, err = TimeAt("tomorrow")//tomorrow in local timezone 343 timeInUTC, err := TimeAt("2 days ago in UTC") //or 2DayAgoInUTC 344 yesterdayUTC, err := TimeAt("yesterdayInUTC")//yesterday in UTC 345 hoursAhead, err := TimeAt("50 hours ahead") 346 347 ``` 348 349 **TimeDiff** 350 351 Provide dynamic semantic for creating time object based on time dif 352 353 ```go 354 355 lastyear, _ := time.Parse(DateFormatToLayout("yyyy-MM-dd"), "2017-01-01") 356 ts1, e := TimeDiff(lastyear, "50 hours earlier") 357 ts2, e := TimeDiff(lastyear, "3 days before in Poland") 358 359 ``` 360 361 **DayElapsed** 362 ```go 363 364 t0, _ := time.Parse(DateFormatToLayout("yyyy-MM-dd hh:mm:ss"), "2017-01-01 12:00:00") 365 dayElapsedInT0, err := ElapsedDay(t0) //0.5 366 367 ``` 368 369 **ElapsedToday** 370 ```go 371 372 elapscedInLocalTz, err := ElapsedTodayInPct("") 373 elapscedInUTC, err := ElapsedToday("UTC") 374 375 ``` 376 377 **RemainingToday** 378 ```go 379 380 elapscedInLocalTz, err := RemainingTodayInPct("") 381 elapscedInUTC, err := RemainingToday("UTC") 382 383 ``` 384 385 **AtTime** 386 ```go 387 atTime := &AtTime{ 388 WeekDay: "*", 389 Hour: "*", 390 Minute: "10,30", 391 } 392 393 //returns the nearest future time for xx:10 or xx:30 394 nextScheduleTime := atTime.Next(time.Now) 395 ``` 396 <a name="storage"></a> 397 398 ## Storage 399 400 401 [Storage API](storage/README.md) provides unified way of accessing local or remote storage system. 402 403 This API has been deprecated, please consider using [Abstract Storage](https://github.com/viant/afs) 404 405 **Example** 406 407 ```go 408 import ( 409 "github.com/viant/toolbox/storage" 410 _ "github.com/viant/toolbox/storage/gs" 411 ) 412 413 414 destinationURL := "gs://myBucket/set1/content.gz" 415 destinationCredentialFile = "gs-secret.json" 416 storageService, err := storage.NewServiceForURL(destinationURL, destinationCredentialFile) 417 418 ``` 419 420 421 422 <a name="tet"></a> 423 ### Text utilities 424 425 426 **Text Case Format** 427 428 You can format with [format.Case](format/case.go).Format(text, destCase) 429 430 ```go 431 formatted := format.CaseLowerUnderscore.Format(text, format.CaseLowerCamel) 432 ``` 433 434 435 <a name="Tokenizer"></a> 436 ### Tokenizer 437 438 <a name="ServiceRouter"></a> 439 ### ServiceRouter 440 441 This ServiceRouter provides simple WebService Endpoint abstractin and RESET Client utilities. 442 443 444 Take as example of a ReverseService defined as follow 445 446 ```go 447 448 type ReverseService interface { 449 Reverse(values []int) []int 450 } 451 452 type reverseService struct{} 453 454 func (r *reverseService) Reverse(values []int) []int { 455 var result = make([]int, 0) 456 for i := len(values) - 1; i >= 0; i-- { 457 result = append(result, values[i]) 458 } 459 460 return result 461 } 462 463 ``` 464 465 In order to define Endpoint for this service, define a server, a router with the service routes; 466 467 468 469 ```qo 470 471 472 type Server struct { 473 service ReverseService 474 port string 475 } 476 477 func (s *Server) Start() { 478 479 router := toolbox.NewServiceRouter( 480 toolbox.ServiceRouting{ 481 HTTPMethod: "GET", 482 URI: "/v1/reverse/{ids}", 483 Handler: s.service.Reverse, 484 Parameters: []string{"ids"}, 485 }, 486 toolbox.ServiceRouting{ 487 HTTPMethod: "POST", 488 URI: "/v1/reverse/", 489 Handler: s.service.Reverse, 490 Parameters: []string{"ids"}, 491 }) 492 493 http.HandleFunc("/v1/", func(writer http.ResponseWriter, reader *http.Request) { 494 err := router.Route(writer, reader) 495 if err != nil { 496 response.WriteHeader(http.StatusInternalServerError) 497 } 498 }) 499 500 fmt.Printf("Started test server on port %v\n", port) 501 log.Fatal(http.ListenAndServe(":"+port, nil)) 502 } 503 504 ``` 505 **ServiceRouting** parameters define handler parameters that can be extracted from URI, QueryString, or from Post Body (json payload) 506 In addition two special parameter names are supported: @httpRequest, @httpResponseWriter to pass in request, and response object respectively. 507 508 509 The REST client utility invoking our reverse service may look as follow 510 511 512 ```go 513 514 var result = make([]int, 0) 515 err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/reverse/1,7,3", nil, &result) 516 //... 517 err := toolbox.RouteToService("post", "http://127.0.0.1:8082/v1/reverse/", []int{1, 7, 3}, &result) 518 519 ``` 520 521 522 By default a service router uses reflection to call the matched routes handler, it is possible to avoid reflection overhead by providing the custom handler invoker. 523 524 ```go 525 526 527 var ReverseInvoker = func(serviceRouting *toolbox.ServiceRouting, request *http.Request, response http.ResponseWriter, uriParameters map[string]interface{}) error { 528 var function = serviceRouting.Handler.(func(values []int) []int) 529 idsParam := uriParameters["ids"] 530 ids := idsParam.([]string) 531 values := make([]int, 0) 532 for _, item := range ids { 533 values = append(values, toolbox.AsInt(item)) 534 } 535 var result = function(values) 536 err := toolbox.WriteServiceRoutingResponse(response, request, serviceRouting, result) 537 if err != nil { 538 return err 539 } 540 return nil 541 } 542 543 //... 544 545 546 router := toolbox.NewServiceRouter( 547 toolbox.ServiceRouting{ 548 HTTPMethod: "GET", 549 URI: "/v1/reverse/{ids}", 550 Handler: s.service.Reverse, 551 Parameters: []string{"ids"}, 552 HandlerInvoker: ReverseInvoker, 553 }) 554 //... 555 556 557 ``` 558 559 560 561 562 <a name="DecoderandEncoder"></a> 563 ### Decoder and Encoder 564 565 #### Decoder 566 567 This library defines DecoderFactory interface to delegate decoder creation, 568 This library comes with standard JSON and UnMarshaler (protobuf) factory implementation. 569 570 Example 571 572 ```go 573 factory :=toolbox.NewJsonDecoderFactory() 574 .... 575 576 decoder := factory.Create(reader) 577 foo := &Foo{} 578 err = decoder.Decode(foo) 579 580 581 582 marshalerFactory := toolbox.NewUnMarshalerDecoderFactory() 583 decoder := marshalerFactory.Create(reader) 584 foo := &Foo{} 585 err = decoder.Decode(foo) 586 ``` 587 588 589 #### Encoder 590 591 This library defines EncoderFactory interface to delegate encoder creation, 592 This library comes with standard JSON and Marshaler (protobuf) factory implementation. 593 594 Example 595 596 ```go 597 factory :=toolbox.NewJsonEncoderFactory() 598 .... 599 buffer := new(bytes.Buffer) 600 601 602 decoder := factory.Create(buffer) 603 err = decoder.Encode(foo) 604 605 606 607 marshalerFactory := toolbox.NewMarshalerEncoderFactory() 608 decoder := marshalerFactory.Create(buffer) 609 err = decoder.Encode(foo) 610 ``` 611 612 613 614 <a name="Logger"></a> 615 ### Logger 616 617 618 This library provides a file logger implementation that optimizes writes. 619 Log messages are queues until max queue size or flush frequency are met. 620 On top of that Ctrl-C also forces immediate log messages flush to disk. 621 622 File template support java style time format to manage rotation on the file name level. 623 624 ```go 625 logger, err := toolbox.NewFileLogger(toolbox.FileLoggerConfig{ 626 LogType: "test", 627 FileTemplate: "/tmp/test[yyyyMMdd-hhmm].log", 628 QueueFlashCount: 250, 629 MaxQueueSize: 500, 630 FlushFrequencyInMs: 2000, 631 MaxIddleTimeInSec: 1, 632 }, toolbox.FileLoggerConfig{ 633 LogType: "transaction", 634 FileTemplate: "/tmp/transaction[yyyyMMdd-hhmm].log", 635 QueueFlashCount: 250, 636 MaxQueueSize: 500, 637 FlushFrequencyInMs:2000, 638 MaxIddleTimeInSec: 1, 639 }, 640 ) 641 642 logger.Log(&toolbox.LogMessage{ 643 MessageType: "test", 644 Message: message 645 }) 646 647 logger.Log(&toolbox.LogMessage{ 648 MessageType: "transaction", 649 Message: message 650 }) 651 ``` 652 653 <a name="BatchLimiter"></a> 654 ### BatchLimiter 655 656 657 This library provides a batch limiter, that enables controling number of active go routines. 658 659 660 ```go 661 662 var tasks []*Task 663 var batchSize = 4 664 limiter:= toolbox.NewBatchLimiter(batchSize, len(tasks)) 665 for i, _ := range tasks { 666 go func(task *Task) { 667 limiter.Acquire() 668 defer limiter.Done() 669 task.Run(); 670 }(tasks[i]) 671 } 672 limiter.Wait() 673 674 ``` 675 676 ### AST Based FileSetInfo 677 678 679 ```go 680 pkgPath := "" 681 source := path.Join(pkgPath) 682 filesetInfo, err :=toolbox.NewFileSetInfo(source) 683 myType := fileSetInfo.Type("MyType") 684 fields := myType.Fields() 685 method := myType.Receivers 686 ``` 687 688 689 690 691 ## GoCover 692 693 [](https://gocover.io/github.com/viant/toolbox) 694 695 696 697 <a name="License"></a> 698 ## License 699 700 The source code is made available under the terms of the Apache License, Version 2, as stated in the file `LICENSE`. 701 702 Individual files may be made available under their own specific license, 703 all compatible with Apache License, Version 2. Please see individual files for details. 704 705 706 <a name="Credits-and-Acknowledgements"></a> 707 708 ## Credits and Acknowledgements 709 710 **Library Author:** Adrian Witas 711 712 **Contributors:**