├── .gitignore ├── B17194.png ├── README.md ├── appA ├── allocate.go ├── gColl.go ├── mapNoStar.go ├── mapSplit.go ├── mapStar.go └── sliceGC.go ├── ch01 ├── cla.go ├── control.go ├── curly.go ├── customLog.go ├── customLogLineNumber.go ├── forLoops.go ├── generics.go ├── goRoutines.go ├── hw.go ├── input.go ├── logs.go ├── phoneBook.go ├── process.go ├── systemLog.go ├── variables.go └── which.go ├── ch02 ├── byteSlices.go ├── capLen.go ├── constants.go ├── convertTimes.go ├── copySlice.go ├── cryptoRand.go ├── dates.go ├── deleteSlice.go ├── error.go ├── genPass.go ├── goSlices.go ├── intString.go ├── numbers.go ├── partSlice.go ├── phoneBook.go ├── pointers.go ├── randomNumbers.go ├── sliceArrays.go ├── sortSlice.go ├── text.go ├── unicode.go └── useStrings.go ├── ch03 ├── csvData.go ├── fieldsRE.go ├── forMaps.go ├── intRE.go ├── nameSurRE.go ├── nilMap.go ├── phoneBook.go ├── sliceStruct.go └── structures.go ├── ch04 ├── Shape2D.go ├── assertions.go ├── empty.go ├── errorInt.go ├── mapEmpty.go ├── methods.go ├── objO.go ├── phoneBook.go ├── reflection.go ├── setValues.go ├── sort.go ├── sortCSV.go ├── sortShapes.go └── typeSwitch.go ├── ch05 ├── defer.go ├── docker-compose.yml ├── document.go ├── functions.go ├── getSchema.go ├── gitVersion.go ├── logDefer.go ├── namedReturn.go ├── postGo.go ├── returnFunction.go ├── sorting.go └── variadic.go ├── ch06 ├── FScycles.go ├── JSON2XML.go ├── JSONstreams.go ├── README.md ├── ReadDirEntry.go ├── byCharacter.go ├── byCharacter_noNewLine.go ├── byLine.go ├── byLine_noNewLine.go ├── byWord.go ├── byWord_noNewLine.go ├── devRandom.go ├── embedFiles.go ├── encodeDecode.go ├── ioFS.go ├── ioInterface.go ├── jsonViper.go ├── myConfig.json ├── prettyPrint.go ├── printSource.go ├── readSize.go ├── signals.go ├── static │ ├── file.txt │ ├── image.png │ └── textfile ├── tagsJSON.go ├── useViper.go ├── writeFile.go ├── xml.go └── yaml.go ├── ch07 ├── addDone.go ├── atomic.go ├── bufChannel.go ├── chRace.go ├── channelFunc.go ├── channels.go ├── closeNil.go ├── create.go ├── defineOrder.go ├── forgetMutex.go ├── goClosure.go ├── goClosureCorrect.go ├── keyVal.go ├── maxprocs.go ├── monitor.go ├── multiple.go ├── mutex.go ├── nilChannel.go ├── randomFiles.go ├── readCloseCh.go ├── rwMutex.go ├── select.go ├── semaphore.go ├── shareMem.go ├── timeOut1.go ├── timeOut2.go ├── useContext.go ├── varGoroutines.go └── wPools.go ├── ch08 ├── Dockerfile ├── dFilev1 ├── dFilev2 ├── docker-compose.yml ├── fileServer.go ├── getEntries.go ├── metrics.go ├── prometheus.go ├── prometheus │ └── prometheus.yml ├── samplePro.go ├── simpleClient.go ├── timeoutClient.go ├── timeoutServer.go ├── withDeadline.go ├── wwwClient.go └── wwwServer.go ├── ch09 ├── concTCP.go ├── otherTCPclient.go ├── otherTCPserver.go ├── socketClient.go ├── socketServer.go ├── tcpC.go ├── tcpS.go ├── test.html ├── udpC.go └── udpS.go ├── ch10 ├── rClient.go └── rServer.go ├── ch11 ├── cannotReach.go ├── cleanup │ ├── cleanup.go │ └── cleanup_test.go ├── coverage │ ├── coverage.go │ └── coverage_test.go ├── crossCompile.go ├── exampleFunctions │ ├── exampleFunctions.go │ └── exampleFunctions_test.go ├── generate │ ├── echo.sh │ ├── goGenerate.go │ └── hello.py ├── intRE │ ├── intRE.go │ └── intRE_test.go ├── main.go ├── profileCla.go ├── profileHTTP.go ├── quickT │ ├── quickT.go │ └── quickT_test.go ├── table │ ├── r1.txt │ ├── r2.txt │ ├── table.go │ ├── table_test.go │ └── testdata │ │ ├── 10.txt │ │ ├── 1000.txt │ │ └── 5k.txt ├── traceCLA.go ├── traceHTTP.go └── walkAll.go └── ch13 ├── allowed.go ├── hw.go ├── interfaces.go ├── newDT.go ├── numeric.go ├── reflection.go └── structures.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .DS_Store 18 | 19 | # PostgreSQL Docker image 20 | /ch05/postgres/* 21 | 22 | /ch08/grafana_data/* 23 | /ch08/prometheus_data/* 24 | 25 | -------------------------------------------------------------------------------- /B17194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mactsouk/mastering-Go-3rd/1c581a41fbbd809d204e049390ee2223cc3af65d/B17194.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mastering-Go-3rd 2 | 3 | Source code for Mastering Go, 3rd edition book. 4 | 5 | Go is a modern, generic-purpose, open source programming language that was officially announced at the end of 2009. Go began as an internal Google project, which means that it was started as an experiment, and has since been inspired by many other programming languages, including *C*, *Pascal*, *Alef*, and *Oberon*. Go's spiritual fathers are the professional programmers ``Robert Griesemer``, ``Ken Thomson``, and ``Rob Pike``. 6 | 7 | Mastering Go, 3rd Edition explores the capabilities of Go *in practice*. You are going to become confident with advanced Go concepts, including concurrency and the operation of the *Go Garbage Collector*, using Go with Docker, writing powerful command line utilities, working with JSON data, and interacting with databases. You are also going to improve your understanding of Go internals to optimize Go code and use data types and data structures in new and unexpected ways. This book also covers the nuances and idioms of Go with exercises and resources to fully embed your newly acquired knowledge. Become an expert Go programmer by building Go projects, writing system utilities and implementing advanced Go techniques in your projects. 8 | 9 | Code repositories for the 1st and 2nd edition of *Mastering Go* are available at 10 | 11 | - https://github.com/PacktPublishing/Mastering-Go 12 | - https://github.com/PacktPublishing/Mastering-Go-Second-Edition 13 | 14 | **Mastering Go, 3rd Edition** 15 | 16 | Published August 2021 17 | 18 | * Paperback: around 700 pages 19 | * Publisher: Packt Publishing 20 | * Language: English 21 | 22 | - ISBN-10: 1801079315 23 | - ISBN-13: 978-1801079310 24 | - Kindle ASIN: ‎B09C8R34JM 25 | 26 | ## Links 27 | 28 | - [Amazon Page](https://www.amazon.com/Mastering-Go-professional-utilities-concurrent-dp-1801079315/dp/1801079315) 29 | - [Packt Page](https://www.packtpub.com/product/mastering-go-third-edition/9781801079310) 30 | - [Go](https://golang.org) 31 | 32 | ## Table of Contents 33 | 34 | * Chapter 1: A Quick Introduction to Go 35 | * Chapter 2: Basic Go Data Types 36 | * Chapter 3: Composite Data Types 37 | * Chapter 4: Reflection and Interfaces 38 | * Chapter 5: Go Packages and Functions 39 | * Chapter 6: Telling a UNIX System What to Do 40 | * Chapter 7: Go Concurrency 41 | * Chapter 8: Building Web Services 42 | * Chapter 9: Working with TCP/IP and WebSocket 43 | * Chapter 10: Working with REST APIs 44 | * Chapter 11: Code Testing and Profiling 45 | * Chapter 12: Working with gRPC 46 | * Chapter 13: Go Generics 47 | * Appendix A: Go Garbage Collector 48 | 49 | For a more analytical TOC, please visit [this](https://www.mtsoukalos.eu/2021/08/mastering-go-3rd-edition-toc/) blog post. 50 | 51 | [](https://www.amazon.com/Mastering-Go-professional-utilities-concurrent-dp-1801079315/dp/1801079315) 52 | -------------------------------------------------------------------------------- /appA/allocate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | const VAT = 24 6 | 7 | type Item struct { 8 | Description string 9 | Value float64 10 | } 11 | 12 | func Value(price float64) float64 { 13 | total := price + price*VAT/100 14 | return total 15 | } 16 | 17 | func main() { 18 | t := Item{Description: "Keyboard", Value: 100} 19 | t.Value = Value(t.Value) 20 | fmt.Println(t) 21 | 22 | tP := &Item{} 23 | *&tP.Description = "Mouse" 24 | *&tP.Value = 100 25 | fmt.Println(tP) 26 | } 27 | -------------------------------------------------------------------------------- /appA/gColl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "time" 7 | ) 8 | 9 | func printStats(mem runtime.MemStats) { 10 | runtime.ReadMemStats(&mem) 11 | fmt.Println("mem.Alloc:", mem.Alloc) 12 | fmt.Println("mem.TotalAlloc:", mem.TotalAlloc) 13 | fmt.Println("mem.HeapAlloc:", mem.HeapAlloc) 14 | fmt.Println("mem.NumGC:", mem.NumGC, "\n") 15 | } 16 | 17 | func main() { 18 | var mem runtime.MemStats 19 | printStats(mem) 20 | 21 | for i := 0; i < 10; i++ { 22 | // Allocating 50,000,000 bytes 23 | s := make([]byte, 50000000) 24 | if s == nil { 25 | fmt.Println("Operation failed!") 26 | } 27 | } 28 | printStats(mem) 29 | 30 | for i := 0; i < 10; i++ { 31 | // Allocating 100,000,000 bytes 32 | s := make([]byte, 100000000) 33 | if s == nil { 34 | fmt.Println("Operation failed!") 35 | } 36 | time.Sleep(5 * time.Second) 37 | } 38 | printStats(mem) 39 | } 40 | -------------------------------------------------------------------------------- /appA/mapNoStar.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | ) 6 | 7 | func main() { 8 | var N = 80000000 9 | myMap := make(map[int]int) 10 | for i := 0; i < N; i++ { 11 | value := int(i) 12 | myMap[value] = value 13 | } 14 | runtime.GC() 15 | _ = myMap[0] 16 | } 17 | -------------------------------------------------------------------------------- /appA/mapSplit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | ) 6 | 7 | func main() { 8 | var N = 80000000 9 | split := make([]map[int]int, 2000) 10 | for i := range split { 11 | split[i] = make(map[int]int) 12 | } 13 | for i := 0; i < N; i++ { 14 | value := int(i) 15 | split[i%2000][value] = value 16 | } 17 | runtime.GC() 18 | _ = split[0][0] 19 | } 20 | -------------------------------------------------------------------------------- /appA/mapStar.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | ) 6 | 7 | func main() { 8 | var N = 80000000 9 | myMap := make(map[int]*int) 10 | for i := 0; i < N; i++ { 11 | value := int(i) 12 | myMap[value] = &value 13 | } 14 | runtime.GC() 15 | _ = myMap[0] 16 | } 17 | -------------------------------------------------------------------------------- /appA/sliceGC.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | ) 6 | 7 | type data struct { 8 | i, j int 9 | } 10 | 11 | func main() { 12 | var N = 80000000 13 | var structure []data 14 | for i := 0; i < N; i++ { 15 | value := int(i) 16 | structure = append(structure, data{value, value}) 17 | } 18 | 19 | runtime.GC() 20 | _ = structure[0] 21 | } 22 | -------------------------------------------------------------------------------- /ch01/cla.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func main() { 10 | arguments := os.Args 11 | if len(arguments) == 1 { 12 | fmt.Println("Need one or more arguments!") 13 | return 14 | } 15 | 16 | var min, max float64 17 | for i := 1; i < len(arguments); i++ { 18 | n, err := strconv.ParseFloat(arguments[i], 64) 19 | if err != nil { 20 | continue 21 | } 22 | 23 | // As pointed out by a reader of the book: 24 | // 25 | // When the first argument is non-parseable, the `min/max` variables are not initialized 26 | // to the first parseable argument (but to `0` by default). 27 | // This would lead to bug when all the parseable arguments share the same sign. 28 | if i == 1 { 29 | min = n 30 | max = n 31 | continue 32 | } 33 | 34 | if n < min { 35 | min = n 36 | } 37 | if n > max { 38 | max = n 39 | } 40 | } 41 | 42 | fmt.Println("Min:", min) 43 | fmt.Println("Max:", max) 44 | } 45 | -------------------------------------------------------------------------------- /ch01/control.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func main() { 10 | if len(os.Args) != 2 { 11 | fmt.Println("Please provide a command line argument") 12 | return 13 | } 14 | argument := os.Args[1] 15 | 16 | // With expression after switch 17 | switch argument { 18 | case "0": 19 | fmt.Println("Zero!") 20 | case "1": 21 | fmt.Println("One!") 22 | case "2", "3", "4": 23 | fmt.Println("2 or 3 or 4") 24 | fallthrough 25 | default: 26 | fmt.Println("Value:", argument) 27 | } 28 | 29 | value, err := strconv.Atoi(argument) 30 | if err != nil { 31 | fmt.Println("Cannot convert to int:", argument) 32 | return 33 | } 34 | 35 | // No expression after switch 36 | switch { 37 | case value == 0: 38 | fmt.Println("Zero!") 39 | case value > 0: 40 | fmt.Println("Positive integer") 41 | case value < 0: 42 | fmt.Println("Negative integer") 43 | default: 44 | fmt.Println("This should not happen:", value) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ch01/curly.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() 8 | { 9 | fmt.Println("Go has strict rules for curly braces!") 10 | } 11 | 12 | -------------------------------------------------------------------------------- /ch01/customLog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path" 8 | ) 9 | 10 | func main() { 11 | LOGFILE := path.Join(os.TempDir(), "mGo.log") 12 | f, err := os.OpenFile(LOGFILE, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 13 | 14 | if err != nil { 15 | fmt.Println(err) 16 | return 17 | } 18 | defer f.Close() 19 | 20 | iLog := log.New(f, "iLog ", log.LstdFlags) 21 | iLog.Println("Hello there!") 22 | iLog.Println("Mastering Go 3rd edition!") 23 | } 24 | -------------------------------------------------------------------------------- /ch01/customLogLineNumber.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path" 8 | ) 9 | 10 | func main() { 11 | LOGFILE := path.Join(os.TempDir(), "mGo.log") 12 | f, err := os.OpenFile(LOGFILE, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 13 | 14 | if err != nil { 15 | fmt.Println(err) 16 | return 17 | } 18 | defer f.Close() 19 | 20 | // initial values for the standard logger 21 | // log.Lshortfile does the job of printing the line number 22 | LstdFlags := log.Ldate | log.Lshortfile 23 | iLog := log.New(f, "LNum ", LstdFlags) 24 | iLog.Println("Mastering Go, 3rd edition!") 25 | 26 | iLog.SetFlags(log.Lshortfile | log.LstdFlags) 27 | iLog.Println("Another log entry!") 28 | } 29 | -------------------------------------------------------------------------------- /ch01/forLoops.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Traditional for loop 7 | for i := 0; i < 10; i++ { 8 | fmt.Print(i*i, " ") 9 | } 10 | fmt.Println() 11 | 12 | // For loop used as do-while loop 13 | i := 0 14 | for ok := true; ok; ok = (i != 10) { 15 | fmt.Print(i*i, " ") 16 | i++ 17 | } 18 | fmt.Println() 19 | 20 | i = 0 21 | for { 22 | if i == 10 { 23 | break 24 | } 25 | fmt.Print(i*i, " ") 26 | i++ 27 | } 28 | fmt.Println() 29 | 30 | // This is a slice but range also works with arrays 31 | aSlice := []int{-1, 2, 1, -1, 2, -2} 32 | for i, v := range aSlice { 33 | fmt.Println("index:", i, "value: ", v) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch01/generics.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Print[T any](s []T) { 8 | for _, v := range s { 9 | fmt.Print(v, " ") 10 | } 11 | fmt.Println() 12 | } 13 | 14 | func main() { 15 | Ints := []int{1, 2, 3} 16 | Strings := []string{"One", "Two", "Three"} 17 | Print(Ints) 18 | Print(Strings) 19 | } 20 | -------------------------------------------------------------------------------- /ch01/goRoutines.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func myPrint(start, finish int) { 9 | for i := start; i <= finish; i++ { 10 | fmt.Print(i, " ") 11 | } 12 | fmt.Println() 13 | time.Sleep(100 * time.Microsecond) 14 | } 15 | 16 | func main() { 17 | for i := 0; i < 4; i++ { 18 | go myPrint(i, 5) 19 | } 20 | time.Sleep(time.Second) 21 | } 22 | -------------------------------------------------------------------------------- /ch01/hw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | fmt.Println("Hello World!") 9 | } 10 | -------------------------------------------------------------------------------- /ch01/input.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // Get User Input 9 | fmt.Printf("Please give me your name: ") 10 | var name string 11 | fmt.Scanln(&name) 12 | fmt.Println("Your name is", name) 13 | } 14 | -------------------------------------------------------------------------------- /ch01/logs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | if len(os.Args) != 1 { 10 | log.Fatal("Fatal: Hello World!") 11 | } 12 | log.Panic("Panic: Hello World!") 13 | } 14 | -------------------------------------------------------------------------------- /ch01/phoneBook.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | type Entry struct { 9 | Name string 10 | Surname string 11 | Tel string 12 | } 13 | 14 | var data = []Entry{} 15 | 16 | func search(key string) *Entry { 17 | for i, v := range data { 18 | if v.Surname == key { 19 | return &data[i] 20 | } 21 | } 22 | return nil 23 | } 24 | 25 | func list() { 26 | for _, v := range data { 27 | fmt.Println(v) 28 | } 29 | } 30 | 31 | func main() { 32 | arguments := os.Args 33 | if len(arguments) == 1 { 34 | exe := arguments[0] 35 | fmt.Printf("Usage: %s search|list \n", exe) 36 | return 37 | } 38 | 39 | data = append(data, Entry{"Mihalis", "Tsoukalos", "2109416471"}) 40 | data = append(data, Entry{"Mary", "Doe", "2109416871"}) 41 | data = append(data, Entry{"John", "Black", "2109416123"}) 42 | 43 | // Differentiate between the commands 44 | switch arguments[1] { 45 | // The search command 46 | case "search": 47 | if len(arguments) != 3 { 48 | fmt.Println("Usage: search Surname") 49 | return 50 | } 51 | result := search(arguments[2]) 52 | if result == nil { 53 | fmt.Println("Entry not found:", arguments[2]) 54 | return 55 | } 56 | fmt.Println(*result) 57 | // The list command 58 | case "list": 59 | list() 60 | // Anything that is not a match 61 | default: 62 | fmt.Println("Not a valid option") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ch01/process.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func main() { 10 | arguments := os.Args 11 | if len(arguments) == 1 { 12 | fmt.Println("Not enough arguments") 13 | return 14 | } 15 | 16 | var total, nInts, nFloats int 17 | invalid := make([]string, 0) 18 | for _, k := range arguments[1:] { 19 | // Is it an integer? 20 | _, err := strconv.Atoi(k) 21 | if err == nil { 22 | total++ 23 | nInts++ 24 | continue 25 | } 26 | // Is it a float 27 | _, err = strconv.ParseFloat(k, 64) 28 | if err == nil { 29 | total++ 30 | nFloats++ 31 | continue 32 | } 33 | // Then it is invalid 34 | invalid = append(invalid, k) 35 | } 36 | 37 | fmt.Println("#read:", total, "#ints:", nInts, "#floats:", nFloats) 38 | if len(invalid) > total { 39 | fmt.Println("Too much invalid input:", len(invalid)) 40 | for _, s := range invalid { 41 | fmt.Println(s) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ch01/systemLog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "log/syslog" 6 | ) 7 | 8 | func main() { 9 | sysLog, err := syslog.New(syslog.LOG_SYSLOG, "systemLog.go") 10 | 11 | if err != nil { 12 | log.Println(err) 13 | return 14 | } else { 15 | log.SetOutput(sysLog) 16 | log.Print("Everything is fine!") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ch01/variables.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | var Global int = 1234 9 | var AnotherGlobal = -5678 10 | 11 | func main() { 12 | var j int 13 | i := Global + AnotherGlobal 14 | fmt.Println("Initial j value:", j) 15 | j = Global 16 | // math.Abs() requires a float64 parameter 17 | // so we type cast it appropriately 18 | k := math.Abs(float64(AnotherGlobal)) 19 | fmt.Printf("Global=%d, i=%d, j=%d k=%.2f.\n", Global, i, j, k) 20 | } 21 | -------------------------------------------------------------------------------- /ch01/which.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func main() { 10 | arguments := os.Args 11 | if len(arguments) == 1 { 12 | fmt.Println("Please provide an argument!") 13 | return 14 | } 15 | file := arguments[1] 16 | 17 | path := os.Getenv("PATH") 18 | pathSplit := filepath.SplitList(path) 19 | for _, directory := range pathSplit { 20 | fullPath := filepath.Join(directory, file) 21 | // Does it exist? 22 | fileInfo, err := os.Stat(fullPath) 23 | if err == nil { 24 | mode := fileInfo.Mode() 25 | // Is it a regular file? 26 | if mode.IsRegular() { 27 | // Is it executable? 28 | if mode&0111 != 0 { 29 | fmt.Println(fullPath) 30 | return 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch02/byteSlices.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Byte slice 7 | b := make([]byte, 12) 8 | fmt.Println("Byte slice:", b) 9 | b = []byte("Byte slice €") 10 | fmt.Println("Byte slice:", b) 11 | 12 | // Print byte slice contents as text 13 | fmt.Printf("Byte slice as text: %s\n", b) 14 | fmt.Println("Byte slice as text:", string(b)) 15 | 16 | // Length of b 17 | fmt.Println("Length of b:", len(b)) 18 | } 19 | -------------------------------------------------------------------------------- /ch02/capLen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Only length is defined. Capacity = length 7 | a := make([]int, 4) 8 | fmt.Println("L:", len(a), "C:", cap(a)) 9 | // Initialize slice. Capacity = length 10 | b := []int{0, 1, 2, 3, 4} 11 | fmt.Println("L:", len(b), "C:", cap(b)) 12 | // Same length and capacity 13 | aSlice := make([]int, 4, 4) 14 | fmt.Println(aSlice) 15 | // Add an element 16 | aSlice = append(aSlice, 5) 17 | fmt.Println(aSlice) 18 | // The capacity is doubled 19 | fmt.Println("L:", len(aSlice), "C:", cap(aSlice)) 20 | // Now add four elements 21 | aSlice = append(aSlice, []int{-1, -2, -3, -4}...) 22 | fmt.Println(aSlice) 23 | // The capacity is doubled 24 | fmt.Println("L:", len(aSlice), "C:", cap(aSlice)) 25 | } 26 | -------------------------------------------------------------------------------- /ch02/constants.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Digit int 8 | type Power2 int 9 | 10 | const PI = 3.1415926 11 | 12 | const ( 13 | C1 = "C1C1C1" 14 | C2 = "C2C2C2" 15 | C3 = "C3C3C3" 16 | ) 17 | 18 | func main() { 19 | const s1 = 123 20 | var v1 float32 = s1 * 12 21 | fmt.Println(v1) 22 | fmt.Println(PI) 23 | 24 | const ( 25 | Zero Digit = iota 26 | One 27 | Two 28 | Three 29 | Four 30 | ) 31 | 32 | fmt.Println(One) 33 | fmt.Println(Two) 34 | 35 | const ( 36 | p2_0 Power2 = 1 << iota 37 | _ 38 | p2_2 39 | _ 40 | p2_4 41 | _ 42 | p2_6 43 | ) 44 | 45 | fmt.Println("2^0:", p2_0) 46 | fmt.Println("2^2:", p2_2) 47 | fmt.Println("2^4:", p2_4) 48 | fmt.Println("2^6:", p2_6) 49 | } 50 | -------------------------------------------------------------------------------- /ch02/convertTimes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | 11 | if len(os.Args) == 1 { 12 | fmt.Println("Need input in __02 January 2006 15:04 MST__ format!") 13 | return 14 | } 15 | 16 | input := os.Args[1] 17 | now, err := time.Parse("02 January 2006 15:04 MST", input) 18 | if err != nil { 19 | fmt.Println(err) 20 | return 21 | } 22 | 23 | // Local time 24 | loc, _ := time.LoadLocation("Local") 25 | fmt.Printf("Current Location: %s\n", now.In(loc)) 26 | 27 | // NY 28 | loc, _ = time.LoadLocation("America/New_York") 29 | fmt.Printf("New York Time: %s\n", now.In(loc)) 30 | 31 | // London 32 | loc, _ = time.LoadLocation("Europe/London") 33 | fmt.Printf("London Time: %s\n", now.In(loc)) 34 | 35 | // Tokyo 36 | loc, _ = time.LoadLocation("Asia/Tokyo") 37 | fmt.Printf("Tokyo Time: %s\n", now.In(loc)) 38 | } 39 | -------------------------------------------------------------------------------- /ch02/copySlice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | a1 := []int{1} 7 | a2 := []int{-1, -2} 8 | a5 := []int{10, 11, 12, 13, 14} 9 | fmt.Println("a1", a1) 10 | fmt.Println("a2", a2) 11 | fmt.Println("a5", a5) 12 | 13 | // copy(destination, input) 14 | // len(a2) > len(a1) 15 | copy(a1, a2) 16 | fmt.Println("a1", a1) 17 | fmt.Println("a2", a2) 18 | 19 | // len(a5) > len(a1) 20 | copy(a1, a5) 21 | fmt.Println("a1", a1) 22 | fmt.Println("a5", a5) 23 | 24 | // len(a2) < len(a5) -> OK 25 | copy(a5, a2) 26 | fmt.Println("a2", a2) 27 | fmt.Println("a5", a5) 28 | } 29 | -------------------------------------------------------------------------------- /ch02/cryptoRand.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/base64" 6 | "fmt" 7 | "os" 8 | "strconv" 9 | ) 10 | 11 | // This function returns (secure) random bytes 12 | func generateBytes(n int64) ([]byte, error) { 13 | b := make([]byte, n) 14 | _, err := rand.Read(b) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return b, nil 19 | } 20 | 21 | func generatePass(s int64) (string, error) { 22 | b, err := generateBytes(s) 23 | return base64.URLEncoding.EncodeToString(b), err 24 | } 25 | 26 | func main() { 27 | var LENGTH int64 = 8 28 | arguments := os.Args 29 | switch len(arguments) { 30 | case 2: 31 | t, err := strconv.ParseInt(os.Args[1], 10, 64) 32 | if err == nil { 33 | LENGTH = t 34 | } 35 | if LENGTH <= 0 { 36 | LENGTH = 8 37 | } 38 | default: 39 | fmt.Println("Using default values!") 40 | } 41 | 42 | myPass, err := generatePass(LENGTH) 43 | if err != nil { 44 | fmt.Println(err) 45 | return 46 | } 47 | fmt.Println(myPass[0:LENGTH]) 48 | } 49 | -------------------------------------------------------------------------------- /ch02/dates.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | start := time.Now() 11 | 12 | if len(os.Args) != 2 { 13 | fmt.Println("Usage: dates parse_string") 14 | return 15 | } 16 | dateString := os.Args[1] 17 | 18 | // Is this a date only? 19 | d, err := time.Parse("02 January 2006", dateString) 20 | if err == nil { 21 | fmt.Println("Full:", d) 22 | fmt.Println("Time:", d.Day(), d.Month(), d.Year()) 23 | } 24 | 25 | // Is this a date + time? 26 | d, err = time.Parse("02 January 2006 15:04", dateString) 27 | if err == nil { 28 | fmt.Println("Full:", d) 29 | fmt.Println("Date:", d.Day(), d.Month(), d.Year()) 30 | fmt.Println("Time:", d.Hour(), d.Minute()) 31 | } 32 | 33 | // Is this a date + time with month represented as a number? 34 | d, err = time.Parse("02-01-2006 15:04", dateString) 35 | if err == nil { 36 | fmt.Println("Full:", d) 37 | fmt.Println("Date:", d.Day(), d.Month(), d.Year()) 38 | fmt.Println("Time:", d.Hour(), d.Minute()) 39 | } 40 | 41 | // Is it time only? 42 | d, err = time.Parse("15:04", dateString) 43 | if err == nil { 44 | fmt.Println("Full:", d) 45 | fmt.Println("Time:", d.Hour(), d.Minute()) 46 | } 47 | 48 | t := time.Now().Unix() 49 | fmt.Println("Epoch time:", t) 50 | // Convert Epoch time to time.Time 51 | d = time.Unix(t, 0) 52 | fmt.Println("Date:", d.Day(), d.Month(), d.Year()) 53 | fmt.Printf("Time: %d:%d\n", d.Hour(), d.Minute()) 54 | 55 | duration := time.Since(start) 56 | fmt.Println("Execution time:", duration) 57 | } 58 | -------------------------------------------------------------------------------- /ch02/deleteSlice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func main() { 10 | arguments := os.Args 11 | if len(arguments) == 1 { 12 | fmt.Println("Need an integer value.") 13 | return 14 | } 15 | 16 | index := arguments[1] 17 | i, err := strconv.Atoi(index) 18 | if err != nil { 19 | fmt.Println(err) 20 | return 21 | } 22 | fmt.Println("Using index", i) 23 | 24 | aSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8} 25 | fmt.Println("Original slice:", aSlice) 26 | 27 | // Delete element at index i 28 | if i > len(aSlice)-1 { 29 | fmt.Println("Cannot delete element", i) 30 | return 31 | } 32 | 33 | // The ... operator auto expands aSlice[i+1:] so that 34 | // its elements can be appended to aSlice[:i] one by one 35 | aSlice = append(aSlice[:i], aSlice[i+1:]...) 36 | fmt.Println("After 1st deletion:", aSlice) 37 | 38 | // Delete element at index i 39 | if i > len(aSlice)-1 { 40 | fmt.Println("Cannot delete element", i) 41 | return 42 | } 43 | 44 | // Replace element at index i with last element 45 | aSlice[i] = aSlice[len(aSlice)-1] 46 | // Remove last element 47 | aSlice = aSlice[:len(aSlice)-1] 48 | fmt.Println("After 2nd deletion:", aSlice) 49 | } 50 | -------------------------------------------------------------------------------- /ch02/error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | ) 9 | 10 | // Custom error message with errors.New() 11 | func check(a, b int) error { 12 | if a == 0 && b == 0 { 13 | return errors.New("this is a custom error message") 14 | } 15 | return nil 16 | } 17 | 18 | // Custom error message with fmt.Errorf() 19 | func formattedError(a, b int) error { 20 | if a == 0 && b == 0 { 21 | return fmt.Errorf("a %d and b %d. UserID: %d", a, b, os.Getuid()) 22 | } 23 | return nil 24 | } 25 | 26 | func main() { 27 | err := check(0, 10) 28 | if err == nil { 29 | fmt.Println("check() ended normally!") 30 | } else { 31 | fmt.Println(err) 32 | } 33 | 34 | err = check(0, 0) 35 | if err.Error() == "this is a custom error message" { 36 | fmt.Println("Custom error detected!") 37 | } 38 | 39 | err = formattedError(0, 0) 40 | if err != nil { 41 | fmt.Println(err) 42 | } 43 | 44 | i, err := strconv.Atoi("-123") 45 | if err == nil { 46 | fmt.Println("Int value is", i) 47 | } 48 | 49 | i, err = strconv.Atoi("Y123") 50 | if err != nil { 51 | fmt.Println(err) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ch02/genPass.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | var MIN = 0 12 | var MAX = 94 13 | 14 | func random(min, max int) int { 15 | return rand.Intn(max-min) + min 16 | } 17 | 18 | func getString(len int64) string { 19 | temp := "" 20 | startChar := "!" 21 | var i int64 = 1 22 | for { 23 | myRand := random(MIN, MAX) 24 | newChar := string(startChar[0] + byte(myRand)) 25 | temp = temp + newChar 26 | if i == len { 27 | break 28 | } 29 | i++ 30 | } 31 | return temp 32 | } 33 | 34 | func main() { 35 | var LENGTH int64 = 8 36 | 37 | arguments := os.Args 38 | switch len(arguments) { 39 | case 2: 40 | t, err := strconv.ParseInt(os.Args[1], 10, 64) 41 | if err == nil { 42 | LENGTH = t 43 | } 44 | if LENGTH <= 0 { 45 | LENGTH = 8 46 | } 47 | default: 48 | fmt.Println("Using default values...") 49 | } 50 | 51 | SEED := time.Now().Unix() 52 | rand.Seed(SEED) 53 | fmt.Println(getString(LENGTH)) 54 | } 55 | -------------------------------------------------------------------------------- /ch02/goSlices.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Create an empty slice 7 | aSlice := []float64{} 8 | // Both length and capacity are 0 because aSlice is empty 9 | fmt.Println(aSlice, len(aSlice), cap(aSlice)) 10 | 11 | // Add elements to a slice 12 | aSlice = append(aSlice, 1234.56) 13 | aSlice = append(aSlice, -34.0) 14 | fmt.Println(aSlice, "with length", len(aSlice)) 15 | 16 | // A slice with length 4 17 | t := make([]int, 4) 18 | t[0] = -1 19 | t[1] = -2 20 | t[2] = -3 21 | t[3] = -4 22 | // Now you will need to use append 23 | t = append(t, -5) 24 | fmt.Println(t) 25 | 26 | // A 2D slice 27 | // You can have as many dimensions as needed 28 | twoD := [][]int{{1, 2, 3}, {4, 5, 6}} 29 | 30 | // Visiting all elements of a 2D slice 31 | // with a double for loop 32 | for _, i := range twoD { 33 | for _, k := range i { 34 | fmt.Print(k, " ") 35 | } 36 | fmt.Println() 37 | } 38 | 39 | make2D := make([][]int, 2) 40 | fmt.Println(make2D) 41 | make2D[0] = []int{1, 2, 3, 4} 42 | make2D[1] = []int{-1, -2, -3, -4} 43 | fmt.Println(make2D) 44 | } 45 | 46 | -------------------------------------------------------------------------------- /ch02/intString.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func main() { 10 | if len(os.Args) == 1 { 11 | fmt.Println("Print provide an integer.") 12 | return 13 | } 14 | 15 | n, err := strconv.Atoi(os.Args[1]) 16 | if err != nil { 17 | fmt.Println(err) 18 | return 19 | } 20 | 21 | // Using strconv.Itoa() 22 | input := strconv.Itoa(n) 23 | fmt.Printf("strconv.Itoa() %s of type %T\n", input, input) 24 | 25 | // Using strconv.FormatInt 26 | input = strconv.FormatInt(int64(n), 10) 27 | fmt.Printf("strconv.FormatInt() %s of type %T\n", input, input) 28 | 29 | // Using string() 30 | input = string(n) 31 | fmt.Printf("string() %s of type %T\n", input, input) 32 | } 33 | -------------------------------------------------------------------------------- /ch02/numbers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | c1 := 12 + 1i 9 | c2 := complex(5, 7) 10 | fmt.Printf("Type of c1: %T\n", c1) 11 | fmt.Printf("Type of c2: %T\n", c2) 12 | 13 | var c3 complex64 = complex64(c1 + c2) 14 | fmt.Println("c3:", c3) 15 | fmt.Printf("Type of c3: %T\n", c3) 16 | 17 | cZero := c3 - c3 18 | fmt.Println("cZero:", cZero) 19 | 20 | x := 12 21 | k := 5 22 | fmt.Println("x:", x) 23 | fmt.Printf("Type of x: %T\n", x) 24 | 25 | div := x / k 26 | fmt.Println("div:", div) 27 | 28 | var m, n float64 29 | m = 1.223 30 | fmt.Println("m, n:", m, n) 31 | 32 | y := 4 / 2.3 33 | fmt.Println("y:", y) 34 | 35 | divFloat := float64(x) / float64(k) 36 | fmt.Println("divFloat:", divFloat) 37 | fmt.Printf("Type of divFloat: %T\n", divFloat) 38 | } 39 | -------------------------------------------------------------------------------- /ch02/partSlice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | aSlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 7 | fmt.Println(aSlice) 8 | l := len(aSlice) 9 | 10 | // First 5 elements 11 | fmt.Println(aSlice[0:5]) 12 | // First 5 elements 13 | fmt.Println(aSlice[:5]) 14 | 15 | // Last 2 elements 16 | fmt.Println(aSlice[l-2 : l]) 17 | 18 | // Last 2 elements 19 | fmt.Println(aSlice[l-2:]) 20 | 21 | // First 5 elements 22 | t := aSlice[0:5:10] 23 | fmt.Println(len(t), cap(t)) 24 | 25 | // Elements at indexes 2,3,4 26 | // Capacity will be 10-2 27 | t = aSlice[2:5:10] 28 | fmt.Println(len(t), cap(t)) 29 | 30 | // Elements at indexes 0,1,2,3,4 31 | // New capacity will be 6-0 32 | t = aSlice[:5:6] 33 | fmt.Println(len(t), cap(t)) 34 | } 35 | -------------------------------------------------------------------------------- /ch02/phoneBook.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | type Entry struct { 12 | Name string 13 | Surname string 14 | Tel string 15 | } 16 | 17 | var data = []Entry{} 18 | var MIN = 0 19 | var MAX = 26 20 | 21 | func search(key string) *Entry { 22 | for i, v := range data { 23 | if v.Tel == key { 24 | return &data[i] 25 | } 26 | } 27 | return nil 28 | } 29 | 30 | func list() { 31 | for _, v := range data { 32 | fmt.Println(v) 33 | } 34 | } 35 | 36 | func random(min, max int) int { 37 | return rand.Intn(max-min) + min 38 | } 39 | 40 | func getString(l int64) string { 41 | startChar := "A" 42 | temp := "" 43 | var i int64 = 1 44 | for { 45 | myRand := random(MIN, MAX) 46 | newChar := string(startChar[0] + byte(myRand)) 47 | temp = temp + newChar 48 | if i == l { 49 | break 50 | } 51 | i++ 52 | } 53 | return temp 54 | } 55 | 56 | func populate(n int, s []Entry) { 57 | for i := 0; i < n; i++ { 58 | name := getString(4) 59 | surname := getString(5) 60 | n := strconv.Itoa(random(100, 199)) 61 | data = append(data, Entry{name, surname, n}) 62 | } 63 | } 64 | 65 | func main() { 66 | arguments := os.Args 67 | if len(arguments) == 1 { 68 | fmt.Println("Usage: search|list ") 69 | return 70 | } 71 | 72 | SEED := time.Now().Unix() 73 | rand.Seed(SEED) 74 | 75 | // How many records to insert 76 | n := 100 77 | populate(n, data) 78 | fmt.Printf("Data has %d entries.\n", len(data)) 79 | 80 | // Differentiate between the commands 81 | switch arguments[1] { 82 | case "search": 83 | if len(arguments) != 3 { 84 | fmt.Println("Usage: search Tel number") 85 | return 86 | } 87 | temp := search(arguments[2]) 88 | if temp == nil { 89 | fmt.Println("Entry not found:", arguments[2]) 90 | return 91 | } 92 | fmt.Println(*temp) 93 | case "list": 94 | list() 95 | default: 96 | fmt.Println("Not a valid option") 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ch02/pointers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type aStructure struct { 6 | field1 complex128 7 | field2 int 8 | } 9 | 10 | func processPointer(x *float64) { 11 | *x = *x * *x 12 | } 13 | 14 | func returnPointer(x float64) *float64 { 15 | temp := 2 * x 16 | return &temp 17 | } 18 | 19 | func bothPointers(x *float64) *float64 { 20 | temp := 2 * *x 21 | return &temp 22 | } 23 | 24 | func main() { 25 | var f float64 = 12.123 26 | fmt.Println("Memory address of f:", &f) 27 | // Pointer to f 28 | fP := &f 29 | fmt.Println("Memory address of f:", fP) 30 | fmt.Println("Value of f:", *fP) 31 | // The value of f changes 32 | processPointer(fP) 33 | fmt.Printf("Value of f: %.2f\n", f) 34 | // The value of f does not change 35 | x := returnPointer(f) 36 | fmt.Printf("Value of x: %.2f\n", *x) 37 | // The value of f does not change 38 | xx := bothPointers(fP) 39 | fmt.Printf("Value of xx: %.2f\n", *xx) 40 | 41 | // Check for empty structure 42 | var k *aStructure 43 | // This is nil because currently k points to nowhere 44 | fmt.Println(k) 45 | // Therefore you are allowed to do this: 46 | if k == nil { 47 | k = new(aStructure) 48 | } 49 | fmt.Printf("%+v\n", k) 50 | if k != nil { 51 | fmt.Println("k is not nil!") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ch02/randomNumbers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | func random(min, max int) int { 12 | return rand.Intn(max-min) + min 13 | } 14 | 15 | func main() { 16 | MIN := 0 17 | MAX := 100 18 | TOTAL := 100 19 | SEED := time.Now().Unix() 20 | 21 | arguments := os.Args 22 | switch len(arguments) { 23 | case 2: 24 | fmt.Println("Usage: ./randomNumbers MIN MAX TOTAL SEED") 25 | t, err := strconv.Atoi(arguments[1]) 26 | if err == nil { 27 | MIN = t 28 | MAX = MIN + 100 29 | } 30 | case 3: 31 | fmt.Println("Usage: ./randomNumbers MIN MAX TOTAL SEED") 32 | t, err := strconv.Atoi(arguments[1]) 33 | if err == nil { 34 | MIN = t 35 | } 36 | t, err = strconv.Atoi(arguments[2]) 37 | if err == nil { 38 | MAX = t 39 | } else { 40 | MAX = MIN + 100 41 | } 42 | case 4: 43 | fmt.Println("Usage: ./randomNumbers MIN MAX TOTAL SEED") 44 | t, err := strconv.Atoi(arguments[1]) 45 | if err == nil { 46 | MIN = t 47 | } 48 | t, err = strconv.Atoi(arguments[2]) 49 | if err == nil { 50 | MAX = t 51 | } else { 52 | MAX = MIN + 100 53 | } 54 | t, err = strconv.Atoi(arguments[3]) 55 | if err == nil { 56 | TOTAL = t 57 | } 58 | case 5: 59 | t, err := strconv.Atoi(arguments[1]) 60 | if err == nil { 61 | MIN = t 62 | } 63 | t, err = strconv.Atoi(arguments[2]) 64 | if err == nil { 65 | MAX = t 66 | } else { 67 | MAX = MIN + 100 68 | } 69 | t, err = strconv.Atoi(arguments[3]) 70 | if err == nil { 71 | TOTAL = t 72 | } 73 | temp, err := strconv.ParseInt(arguments[4], 10, 64) 74 | if err == nil { 75 | SEED = temp 76 | } 77 | default: 78 | fmt.Println("Using default values!") 79 | } 80 | 81 | rand.Seed(SEED) 82 | for i := 0; i < TOTAL; i++ { 83 | myrand := random(MIN, MAX) 84 | fmt.Print(myrand) 85 | fmt.Print(" ") 86 | } 87 | fmt.Println() 88 | } 89 | -------------------------------------------------------------------------------- /ch02/sliceArrays.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func change(s []string) { 8 | s[0] = "Change_function" 9 | } 10 | 11 | func main() { 12 | a := [4]string{"Zero", "One", "Two", "Three"} 13 | fmt.Println("a:", a) 14 | 15 | var S0 = a[0:1] 16 | fmt.Println(S0) 17 | S0[0] = "S0" 18 | 19 | var S12 = a[1:3] 20 | fmt.Println(S12) 21 | S12[0] = "S12_0" 22 | S12[1] = "S12_1" 23 | 24 | fmt.Println("a:", a) 25 | 26 | // Changes to slice -> changes to array 27 | change(S12) 28 | fmt.Println("a:", a) 29 | 30 | // capacity of S0 31 | fmt.Println("Capacity of S0:", cap(S0), "Length of S0:", len(S0)) 32 | 33 | // Adding 4 elements to S0 34 | S0 = append(S0, "N1") 35 | S0 = append(S0, "N2") 36 | S0 = append(S0, "N3") 37 | a[0] = "-N1" 38 | 39 | // Changing the capacity of S0 40 | // Not the same underlying array any more! 41 | S0 = append(S0, "N4") 42 | 43 | fmt.Println("Capacity of S0:", cap(S0), "Length of S0:", len(S0)) 44 | // This change does not go to S0 45 | a[0] = "-N1-" 46 | 47 | // This change does go to S12 48 | // Because slice S12 is still connected to array a. 49 | a[1] = "-N2-" 50 | 51 | fmt.Println("S0:", S0) 52 | fmt.Println("a: ", a) 53 | fmt.Println("S12:", S12) 54 | } 55 | -------------------------------------------------------------------------------- /ch02/sortSlice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | func main() { 9 | sInts := []int{1, 0, 2, -3, 4, -20} 10 | sFloats := []float64{1.0, 0.2, 0.22, -3, 4.1, -0.1} 11 | sStrings := []string{"aa", "a", "A", "Aa", "aab", "AAa"} 12 | 13 | fmt.Println("sInts original:", sInts) 14 | sort.Ints(sInts) 15 | fmt.Println("sInts:", sInts) 16 | sort.Sort(sort.Reverse(sort.IntSlice(sInts))) 17 | fmt.Println("Reverse:", sInts) 18 | 19 | fmt.Println("sFloats original:", sFloats) 20 | sort.Float64s(sFloats) 21 | fmt.Println("sFloats:", sFloats) 22 | sort.Sort(sort.Reverse(sort.Float64Slice(sFloats))) 23 | fmt.Println("Reverse:", sFloats) 24 | 25 | fmt.Println("sStrings original:", sStrings) 26 | sort.Strings(sStrings) 27 | fmt.Println("sStrings:", sStrings) 28 | sort.Sort(sort.Reverse(sort.StringSlice(sStrings))) 29 | fmt.Println("Reverse:", sStrings) 30 | } 31 | -------------------------------------------------------------------------------- /ch02/text.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | aString := "Hello World! €" 7 | fmt.Println("First character", string(aString[0])) 8 | 9 | // Runes 10 | // A rune 11 | r := '€' 12 | fmt.Println("As an int32 value:", r) 13 | // Convert Runes to text 14 | fmt.Printf("As a string: %s and as a character: %c\n", r, r) 15 | 16 | // Print an existing string as runes 17 | for _, v := range aString { 18 | fmt.Printf("%x ", v) 19 | } 20 | fmt.Println() 21 | 22 | // String to rune Array 23 | // myRune := []rune(aString) 24 | // fmt.Printf("myRune %U\n", myRune) 25 | 26 | // Rune array to string 27 | // runeArray := []rune{'1', '2', '3'} 28 | // s := string(runeArray) 29 | // fmt.Println(s) 30 | 31 | // Print an existing string as characters 32 | for _, v := range aString { 33 | fmt.Printf("%c", v) 34 | } 35 | fmt.Println() 36 | } 37 | -------------------------------------------------------------------------------- /ch02/unicode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "unicode" 6 | ) 7 | 8 | func main() { 9 | const sL = "\x99\x00ab\x50\x00\x23\x50\x29\x9c" 10 | 11 | for i := 0; i < len(sL); i++ { 12 | if unicode.IsPrint(rune(sL[i])) { 13 | fmt.Printf("%c\n", sL[i]) 14 | } else { 15 | fmt.Println("Not printable!") 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ch02/useStrings.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | s "strings" 6 | "unicode" 7 | ) 8 | 9 | var f = fmt.Printf 10 | 11 | func main() { 12 | upper := s.ToUpper("Hello there!") 13 | f("To Upper: %s\n", upper) 14 | f("To Lower: %s\n", s.ToLower("Hello THERE")) 15 | 16 | f("%s\n", s.Title("tHis wiLL be A title!")) 17 | 18 | f("EqualFold: %v\n", s.EqualFold("Mihalis", "MIHAlis")) 19 | f("EqualFold: %v\n", s.EqualFold("Mihalis", "MIHAli")) 20 | 21 | f("Prefix: %v\n", s.HasPrefix("Mihalis", "Mi")) 22 | f("Prefix: %v\n", s.HasPrefix("Mihalis", "mi")) 23 | f("Suffix: %v\n", s.HasSuffix("Mihalis", "is")) 24 | f("Suffix: %v\n", s.HasSuffix("Mihalis", "IS")) 25 | 26 | f("Index: %v\n", s.Index("Mihalis", "ha")) 27 | f("Index: %v\n", s.Index("Mihalis", "Ha")) 28 | f("Count: %v\n", s.Count("Mihalis", "i")) 29 | f("Count: %v\n", s.Count("Mihalis", "I")) 30 | f("Repeat: %s\n", s.Repeat("ab", 5)) 31 | 32 | f("TrimSpace: %s\n", s.TrimSpace(" \tThis is a line. \n")) 33 | f("TrimLeft: %s", s.TrimLeft(" \tThis is a\t line. \n", "\n\t ")) 34 | f("TrimRight: %s\n", s.TrimRight(" \tThis is a\t line. \n", "\n\t ")) 35 | 36 | f("Compare: %v\n", s.Compare("Mihalis", "MIHALIS")) 37 | f("Compare: %v\n", s.Compare("Mihalis", "Mihalis")) 38 | f("Compare: %v\n", s.Compare("MIHALIS", "MIHalis")) 39 | 40 | t := s.Fields("This is a string!") 41 | f("Fields: %v\n", len(t)) 42 | t = s.Fields("ThisIs a\tstring!") 43 | f("Fields: %v\n", len(t)) 44 | 45 | f("%s\n", s.Split("abcd efg", "")) 46 | f("%s\n", s.Replace("abcd efg", "", "_", -1)) 47 | f("%s\n", s.Replace("abcd efg", "", "_", 4)) 48 | f("%s\n", s.Replace("abcd efg", "", "_", 2)) 49 | 50 | lines := []string{"Line 1", "Line 2", "Line 3"} 51 | f("Join: %s\n", s.Join(lines, "+++")) 52 | 53 | f("SplitAfter: %s\n", s.SplitAfter("123++432++", "++")) 54 | 55 | trimFunction := func(c rune) bool { 56 | return !unicode.IsLetter(c) 57 | } 58 | f("TrimFunc: %s\n", s.TrimFunc("123 abc ABC \t .", trimFunction)) 59 | } 60 | -------------------------------------------------------------------------------- /ch03/csvData.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type Record struct { 10 | Name string 11 | Surname string 12 | Number string 13 | LastAccess string 14 | } 15 | 16 | var myData = []Record{} 17 | 18 | func readCSVFile(filepath string) ([][]string, error) { 19 | _, err := os.Stat(filepath) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | f, err := os.Open(filepath) 25 | if err != nil { 26 | return nil, err 27 | } 28 | defer f.Close() 29 | 30 | // CSV file read all at once 31 | // lines data type is [][]string 32 | lines, err := csv.NewReader(f).ReadAll() 33 | if err != nil { 34 | return [][]string{}, err 35 | } 36 | 37 | return lines, nil 38 | } 39 | 40 | func saveCSVFile(filepath string) error { 41 | csvfile, err := os.Create(filepath) 42 | if err != nil { 43 | return err 44 | } 45 | defer csvfile.Close() 46 | 47 | csvwriter := csv.NewWriter(csvfile) 48 | // Changing the default field delimiter to tab 49 | csvwriter.Comma = '\t' 50 | for _, row := range myData { 51 | temp := []string{row.Name, row.Surname, row.Number, row.LastAccess} 52 | _ = csvwriter.Write(temp) 53 | } 54 | csvwriter.Flush() 55 | return nil 56 | } 57 | 58 | func main() { 59 | if len(os.Args) != 3 { 60 | fmt.Println("csvData input output!") 61 | return 62 | } 63 | 64 | input := os.Args[1] 65 | output := os.Args[2] 66 | lines, err := readCSVFile(input) 67 | if err != nil { 68 | fmt.Println(err) 69 | return 70 | } 71 | 72 | // CSV data is read in columns - each line is a slice 73 | for _, line := range lines { 74 | temp := Record{ 75 | Name: line[0], 76 | Surname: line[1], 77 | Number: line[2], 78 | LastAccess: line[3], 79 | } 80 | myData = append(myData, temp) 81 | fmt.Println(temp) 82 | } 83 | 84 | err = saveCSVFile(output) 85 | if err != nil { 86 | fmt.Println(err) 87 | return 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ch03/fieldsRE.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | func matchNameSur(s string) bool { 11 | t := []byte(s) 12 | re := regexp.MustCompile(`^[A-Z][a-z]*$`) 13 | return re.Match(t) 14 | } 15 | 16 | func matchTel(s string) bool { 17 | t := []byte(s) 18 | re := regexp.MustCompile(`\d+$`) 19 | return re.Match(t) 20 | } 21 | 22 | func matchRecord(s string) bool { 23 | fields := strings.Split(s, ",") 24 | if len(fields) != 3 { 25 | return false 26 | } 27 | 28 | if !matchNameSur(fields[0]) { 29 | return false 30 | } 31 | 32 | if !matchNameSur(fields[1]) { 33 | return false 34 | } 35 | 36 | return matchTel(fields[2]) 37 | } 38 | 39 | func main() { 40 | arguments := os.Args 41 | if len(arguments) == 1 { 42 | fmt.Println("Usage: record.") 43 | return 44 | } 45 | 46 | s := arguments[1] 47 | err := matchRecord(s) 48 | fmt.Println(err) 49 | } 50 | -------------------------------------------------------------------------------- /ch03/forMaps.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // range works with maps as well 7 | aMap := make(map[string]string) 8 | aMap["123"] = "456" 9 | aMap["key"] = "A value" 10 | for key, v := range aMap { 11 | fmt.Println("key:", key, "value:", v) 12 | } 13 | 14 | for _, v := range aMap { 15 | fmt.Print(" # ", v) 16 | } 17 | fmt.Println() 18 | } 19 | -------------------------------------------------------------------------------- /ch03/intRE.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | ) 8 | 9 | func matchInt(s string) bool { 10 | t := []byte(s) 11 | re := regexp.MustCompile(`^[-+]?\d+$`) 12 | return re.Match(t) 13 | } 14 | 15 | func main() { 16 | arguments := os.Args 17 | if len(arguments) == 1 { 18 | fmt.Println("Usage: string.") 19 | return 20 | } 21 | 22 | s := arguments[1] 23 | ret := matchInt(s) 24 | fmt.Println(ret) 25 | } 26 | -------------------------------------------------------------------------------- /ch03/nameSurRE.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | ) 8 | 9 | func matchNameSur(s string) bool { 10 | t := []byte(s) 11 | re := regexp.MustCompile(`^[A-Z][a-z]*$`) 12 | return re.Match(t) 13 | } 14 | 15 | func main() { 16 | arguments := os.Args 17 | if len(arguments) == 1 { 18 | fmt.Println("Usage: string.") 19 | return 20 | } 21 | 22 | s := arguments[1] 23 | ret := matchNameSur(s) 24 | fmt.Println(ret) 25 | } 26 | -------------------------------------------------------------------------------- /ch03/nilMap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | aMap := map[string]int{} 9 | aMap["test"] = 1 10 | fmt.Println("aMap:", aMap) 11 | aMap = nil 12 | fmt.Println("aMap:", aMap) 13 | if aMap == nil { 14 | fmt.Println("nil map!") 15 | aMap = map[string]int{} 16 | } 17 | aMap["test"] = 1 18 | 19 | // This will crash! 20 | aMap = nil 21 | aMap["test"] = 1 22 | } 23 | -------------------------------------------------------------------------------- /ch03/phoneBook.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | type Entry struct { 14 | Name string 15 | Surname string 16 | Tel string 17 | LastAccess string 18 | } 19 | 20 | // CSVFILE resides in the home directory of the current user 21 | var CSVFILE = "/Users/mtsouk/csv.data" 22 | 23 | var data = []Entry{} 24 | var index map[string]int 25 | 26 | func readCSVFile(filepath string) error { 27 | _, err := os.Stat(filepath) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | f, err := os.Open(filepath) 33 | if err != nil { 34 | return err 35 | } 36 | defer f.Close() 37 | 38 | // CSV file read all at once 39 | lines, err := csv.NewReader(f).ReadAll() 40 | if err != nil { 41 | return err 42 | } 43 | 44 | for _, line := range lines { 45 | temp := Entry{ 46 | Name: line[0], 47 | Surname: line[1], 48 | Tel: line[2], 49 | LastAccess: line[3], 50 | } 51 | // Storing to global variable 52 | data = append(data, temp) 53 | } 54 | 55 | return nil 56 | } 57 | 58 | func saveCSVFile(filepath string) error { 59 | csvfile, err := os.Create(filepath) 60 | if err != nil { 61 | return err 62 | } 63 | defer csvfile.Close() 64 | 65 | csvwriter := csv.NewWriter(csvfile) 66 | for _, row := range data { 67 | temp := []string{row.Name, row.Surname, row.Tel, row.LastAccess} 68 | _ = csvwriter.Write(temp) 69 | } 70 | csvwriter.Flush() 71 | return nil 72 | } 73 | 74 | func createIndex() error { 75 | index = make(map[string]int) 76 | for i, k := range data { 77 | key := k.Tel 78 | index[key] = i 79 | } 80 | return nil 81 | } 82 | 83 | // Initialized by the user – returns a pointer 84 | // If it returns nil, there was an error 85 | func initS(N, S, T string) *Entry { 86 | // Both of them should have a value 87 | if T == "" || S == "" { 88 | return nil 89 | } 90 | // Give LastAccess a value 91 | LastAccess := strconv.FormatInt(time.Now().Unix(), 10) 92 | return &Entry{Name: N, Surname: S, Tel: T, LastAccess: LastAccess} 93 | } 94 | 95 | func insert(pS *Entry) error { 96 | // If it already exists, do not add it 97 | _, ok := index[(*pS).Tel] 98 | if ok { 99 | return fmt.Errorf("%s already exists", pS.Tel) 100 | } 101 | data = append(data, *pS) 102 | // Update the index 103 | _ = createIndex() 104 | 105 | err := saveCSVFile(CSVFILE) 106 | if err != nil { 107 | return err 108 | } 109 | return nil 110 | } 111 | 112 | func deleteEntry(key string) error { 113 | i, ok := index[key] 114 | if !ok { 115 | return fmt.Errorf("%s cannot be found!", key) 116 | } 117 | data = append(data[:i], data[i+1:]...) 118 | // Update the index - key does not exist any more 119 | delete(index, key) 120 | 121 | err := saveCSVFile(CSVFILE) 122 | if err != nil { 123 | return err 124 | } 125 | return nil 126 | } 127 | 128 | func search(key string) *Entry { 129 | i, ok := index[key] 130 | if !ok { 131 | return nil 132 | } 133 | data[i].LastAccess = strconv.FormatInt(time.Now().Unix(), 10) 134 | _ = saveCSVFile(CSVFILE) 135 | return &data[i] 136 | } 137 | 138 | func list() { 139 | for _, v := range data { 140 | fmt.Println(v) 141 | } 142 | } 143 | 144 | func matchTel(s string) bool { 145 | t := []byte(s) 146 | re := regexp.MustCompile(`\d+$`) 147 | return re.Match(t) 148 | } 149 | 150 | func main() { 151 | arguments := os.Args 152 | if len(arguments) == 1 { 153 | fmt.Println("Usage: insert|delete|search|list ") 154 | return 155 | } 156 | 157 | // If the CSVFILE does not exist, create an empty one 158 | _, err := os.Stat(CSVFILE) 159 | // If error is not nil, it means that the file does not exist 160 | if err != nil { 161 | fmt.Println("Creating", CSVFILE) 162 | f, err := os.Create(CSVFILE) 163 | if err != nil { 164 | fmt.Println(err) 165 | return 166 | } 167 | f.Close() 168 | } 169 | 170 | fileInfo, err := os.Stat(CSVFILE) 171 | // Is it a regular file? 172 | mode := fileInfo.Mode() 173 | if !mode.IsRegular() { 174 | fmt.Println(CSVFILE, "not a regular file!") 175 | return 176 | } 177 | 178 | err = readCSVFile(CSVFILE) 179 | if err != nil { 180 | fmt.Println(err) 181 | return 182 | } 183 | 184 | err = createIndex() 185 | if err != nil { 186 | fmt.Println("Cannot create index.") 187 | return 188 | } 189 | 190 | // Differentiating between the commands 191 | switch arguments[1] { 192 | case "insert": 193 | if len(arguments) != 5 { 194 | fmt.Println("Usage: insert Name Surname Telephone") 195 | return 196 | } 197 | t := strings.ReplaceAll(arguments[4], "-", "") 198 | if !matchTel(t) { 199 | fmt.Println("Not a valid telephone number:", t) 200 | return 201 | } 202 | temp := initS(arguments[2], arguments[3], t) 203 | // If it was nil, there was an error 204 | if temp != nil { 205 | err := insert(temp) 206 | if err != nil { 207 | fmt.Println(err) 208 | return 209 | } 210 | } 211 | case "delete": 212 | if len(arguments) != 3 { 213 | fmt.Println("Usage: delete Number") 214 | return 215 | } 216 | t := strings.ReplaceAll(arguments[2], "-", "") 217 | if !matchTel(t) { 218 | fmt.Println("Not a valid telephone number:", t) 219 | return 220 | } 221 | err := deleteEntry(t) 222 | if err != nil { 223 | fmt.Println(err) 224 | } 225 | case "search": 226 | if len(arguments) != 3 { 227 | fmt.Println("Usage: search Number") 228 | return 229 | } 230 | t := strings.ReplaceAll(arguments[2], "-", "") 231 | if !matchTel(t) { 232 | fmt.Println("Not a valid telephone number:", t) 233 | return 234 | } 235 | temp := search(t) 236 | if temp == nil { 237 | fmt.Println("Number not found:", t) 238 | return 239 | } 240 | fmt.Println(*temp) 241 | case "list": 242 | list() 243 | default: 244 | fmt.Println("Not a valid option") 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /ch03/sliceStruct.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type record struct { 9 | Field1 int 10 | Field2 string 11 | } 12 | 13 | func main() { 14 | S := []record{} 15 | for i := 0; i < 10; i++ { 16 | text := "text" + strconv.Itoa(i) 17 | temp := record{Field1: i, Field2: text} 18 | S = append(S, temp) 19 | } 20 | // Accessing the fields of the first element 21 | fmt.Println("Index 0:", S[0].Field1, S[0].Field2) 22 | fmt.Println("Number of structures:", len(S)) 23 | sum := 0 24 | for _, k := range S { 25 | sum += k.Field1 26 | } 27 | fmt.Println("Sum:", sum) 28 | } 29 | -------------------------------------------------------------------------------- /ch03/structures.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // Knowning the data structures of a program is really important 6 | // Programs = Data Structures + Algorithms 7 | type Entry struct { 8 | Name string 9 | Surname string 10 | Year int 11 | } 12 | 13 | // Initialized by Go 14 | func zeroS() Entry { 15 | return Entry{} 16 | } 17 | 18 | // Initialized by the user 19 | func initS(N, S string, Y int) Entry { 20 | if Y < 2000 { 21 | return Entry{Name: N, Surname: S, Year: 2000} 22 | } 23 | return Entry{Name: N, Surname: S, Year: Y} 24 | } 25 | 26 | // Initialized by Go - returns pointer 27 | func zeroPtoS() *Entry { 28 | t := &Entry{} 29 | return t 30 | } 31 | 32 | // Initialized by the user - returns pointer 33 | func initPtoS(N, S string, Y int) *Entry { 34 | if len(S) == 0 { 35 | return &Entry{Name: N, Surname: "Unknown", Year: Y} 36 | } 37 | return &Entry{Name: N, Surname: S, Year: Y} 38 | } 39 | 40 | func main() { 41 | s1 := zeroS() 42 | p1 := zeroPtoS() 43 | fmt.Println("s1:", s1, "p1:", *p1) 44 | 45 | s2 := initS("Mihalis", "Tsoukalos", 2020) 46 | p2 := initPtoS("Mihalis", "Tsoukalos", 2020) 47 | fmt.Println("s2:", s2, "p2:", *p2) 48 | fmt.Println("Year:", s1.Year, s2.Year, p1.Year, p2.Year) 49 | pS := new(Entry) 50 | fmt.Println("pS:", pS) 51 | } 52 | -------------------------------------------------------------------------------- /ch04/Shape2D.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type Shape2D interface { 9 | Perimeter() float64 10 | } 11 | 12 | type circle struct { 13 | R float64 14 | } 15 | 16 | func (c circle) Perimeter() float64 { 17 | return 2 * math.Pi * c.R 18 | } 19 | 20 | func main() { 21 | a := circle{R: 1.5} 22 | fmt.Printf("R %.2f -> Perimeter %.3f \n", a.R, a.Perimeter()) 23 | 24 | _, ok := interface{}(a).(Shape2D) 25 | if ok { 26 | fmt.Println("a is a Shape2D!") 27 | } 28 | 29 | i := 12 30 | _, ok = interface{}(i).(Shape2D) 31 | if ok { 32 | fmt.Println("i is a Shape2D!") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ch04/assertions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func returnNumber() interface{} { 8 | return 12 9 | } 10 | 11 | func main() { 12 | anInt := returnNumber() 13 | number := anInt.(int) 14 | number++ 15 | fmt.Println(number) 16 | 17 | // The next statement will fail because there 18 | // is no type assertion to get the value: 19 | // anInt++ 20 | 21 | // This fails but the failure is under control 22 | // because of the `ok` bool variable that tells 23 | // whether the type assertion is successful or not 24 | value, ok := anInt.(int64) 25 | if ok { 26 | fmt.Println("Type assertion successful: ", value) 27 | } else { 28 | fmt.Println("Type assertion failed!") 29 | } 30 | 31 | // This is successful but dangerous 32 | // because it does not make sure that 33 | // the type assertion is successful 34 | // It just happens to be successful 35 | i := anInt.(int) 36 | fmt.Println("i:", i) 37 | 38 | // This will PANIC because `anInt` is not `bool` 39 | _ = anInt.(bool) 40 | } 41 | -------------------------------------------------------------------------------- /ch04/empty.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type S1 struct { 6 | F1 int 7 | F2 string 8 | } 9 | 10 | type S2 struct { 11 | F1 int 12 | F2 S1 13 | } 14 | 15 | func Print(s interface{}) { 16 | fmt.Println(s) 17 | } 18 | 19 | func main() { 20 | v1 := S1{10, "Hello"} 21 | v2 := S2{F1: -1, F2: v1} 22 | Print(v1) 23 | Print(v2) 24 | // Printing an integer 25 | Print(123) 26 | // Printing a string 27 | Print("Go is the best!") 28 | } 29 | -------------------------------------------------------------------------------- /ch04/errorInt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "os" 9 | ) 10 | 11 | type emptyFile struct { 12 | Ended bool 13 | Read int 14 | } 15 | 16 | // Implement error interface 17 | func (e emptyFile) Error() string { 18 | return fmt.Sprintf("Ended with io.EOF (%t) but read (%d) bytes", e.Ended, e.Read) 19 | } 20 | 21 | // Check values 22 | func isFileEmpty(e error) bool { 23 | // Type assertion 24 | v, ok := e.(emptyFile) 25 | if ok { 26 | if v.Read == 0 && v.Ended == true { 27 | return true 28 | } 29 | } 30 | return false 31 | } 32 | 33 | func readFile(file string) error { 34 | var err error 35 | fd, err := os.Open(file) 36 | if err != nil { 37 | return err 38 | } 39 | defer fd.Close() 40 | 41 | reader := bufio.NewReader(fd) 42 | n := 0 43 | for { 44 | line, err := reader.ReadString('\n') 45 | n += len(line) 46 | if err == io.EOF { 47 | // End of File: nothing more to read 48 | if n == 0 { 49 | return emptyFile{true, n} 50 | } 51 | break 52 | } else if err != nil { 53 | return err 54 | } 55 | } 56 | return nil 57 | } 58 | 59 | func main() { 60 | flag.Parse() 61 | if len(flag.Args()) == 0 { 62 | fmt.Println("usage: errorInt [ ...]") 63 | return 64 | } 65 | 66 | for _, file := range flag.Args() { 67 | err := readFile(file) 68 | if isFileEmpty(err) { 69 | fmt.Println(file, err) 70 | } else if err != nil { 71 | fmt.Println(file, err) 72 | } else { 73 | fmt.Println(file, "is OK.") 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ch04/mapEmpty.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | var JSONrecord = `{ 10 | "Flag": true, 11 | "Array": ["a","b","c"], 12 | "Entity": { 13 | "a1": "b1", 14 | "a2": "b2", 15 | "Value": -456, 16 | "Null": null 17 | }, 18 | "Message": "Hello Go!" 19 | }` 20 | 21 | func typeSwitch(m map[string]interface{}) { 22 | for k, v := range m { 23 | switch c := v.(type) { 24 | case string: 25 | fmt.Println("Is a string!", k, c) 26 | case float64: 27 | fmt.Println("Is a float64!", k, c) 28 | case bool: 29 | fmt.Println("Is a Boolean!", k, c) 30 | case map[string]interface{}: 31 | fmt.Println("Is a map!", k, c) 32 | typeSwitch(v.(map[string]interface{})) 33 | default: 34 | fmt.Printf("...Is %v: %T!\n", k, c) 35 | } 36 | } 37 | return 38 | } 39 | 40 | func exploreMap(m map[string]interface{}) { 41 | for k, v := range m { 42 | embMap, ok := v.(map[string]interface{}) 43 | // If it is a map, explore deeper 44 | if ok { 45 | fmt.Printf("{\"%v\": \n", k) 46 | exploreMap(embMap) 47 | fmt.Printf("}\n") 48 | } else { 49 | fmt.Printf("%v: %v\n", k, v) 50 | } 51 | } 52 | } 53 | 54 | func main() { 55 | if len(os.Args) == 1 { 56 | fmt.Println("*** Using default JSON record.") 57 | } else { 58 | JSONrecord = os.Args[1] 59 | } 60 | 61 | JSONMap := make(map[string]interface{}) 62 | err := json.Unmarshal([]byte(JSONrecord), &JSONMap) 63 | if err != nil { 64 | fmt.Println(err) 65 | return 66 | } 67 | exploreMap(JSONMap) 68 | typeSwitch(JSONMap) 69 | } 70 | -------------------------------------------------------------------------------- /ch04/methods.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | type ar2x2 [2][2]int 10 | 11 | // Traditional Add() function 12 | func Add(a, b ar2x2) ar2x2 { 13 | c := ar2x2{} 14 | for i := 0; i < 2; i++ { 15 | for j := 0; j < 2; j++ { 16 | c[i][j] = a[i][j] + b[i][j] 17 | } 18 | } 19 | return c 20 | } 21 | 22 | // Type method Add() 23 | func (a *ar2x2) Add(b ar2x2) { 24 | for i := 0; i < 2; i++ { 25 | for j := 0; j < 2; j++ { 26 | a[i][j] = a[i][j] + b[i][j] 27 | } 28 | } 29 | } 30 | 31 | // Type method Subtract() 32 | func (a *ar2x2) Subtract(b ar2x2) { 33 | for i := 0; i < 2; i++ { 34 | for j := 0; j < 2; j++ { 35 | a[i][j] = a[i][j] - b[i][j] 36 | } 37 | } 38 | } 39 | 40 | // Type method Multiply() 41 | func (a *ar2x2) Multiply(b ar2x2) { 42 | c := ar2x2{} 43 | c[0][0] = a[0][0]*b[0][0] + a[0][1]*b[1][0] 44 | c[1][0] = a[1][0]*b[0][0] + a[1][1]*b[1][0] 45 | c[0][1] = a[0][0]*b[0][1] + a[0][1]*b[1][1] 46 | c[1][1] = a[1][0]*b[0][1] + a[1][1]*b[1][1] 47 | *a = c 48 | } 49 | 50 | func main() { 51 | if len(os.Args) != 9 { 52 | fmt.Println("Need 8 integers") 53 | return 54 | } 55 | 56 | k := [8]int{} 57 | for index, i := range os.Args[1:] { 58 | v, err := strconv.Atoi(i) 59 | if err != nil { 60 | fmt.Println(err) 61 | return 62 | } 63 | k[index] = v 64 | } 65 | a := ar2x2{{k[0], k[1]}, {k[2], k[3]}} 66 | b := ar2x2{{k[4], k[5]}, {k[6], k[7]}} 67 | 68 | fmt.Println("Traditional a+b:", Add(a, b)) 69 | a.Add(b) 70 | fmt.Println("a+b:", a) 71 | a.Subtract(a) 72 | fmt.Println("a-a:", a) 73 | 74 | a = ar2x2{{k[0], k[1]}, {k[2], k[3]}} 75 | a.Multiply(b) 76 | fmt.Println("a*b:", a) 77 | 78 | a = ar2x2{{k[0], k[1]}, {k[2], k[3]}} 79 | b.Multiply(a) 80 | fmt.Println("b*a:", b) 81 | } 82 | -------------------------------------------------------------------------------- /ch04/objO.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type IntA interface { 8 | foo() 9 | } 10 | 11 | type IntB interface { 12 | bar() 13 | } 14 | 15 | type IntC interface { 16 | IntA 17 | IntB 18 | } 19 | 20 | func processA(s IntA) { 21 | fmt.Printf("%T\n", s) 22 | } 23 | 24 | type a struct { 25 | XX int 26 | YY int 27 | } 28 | 29 | // Satisfying IntA 30 | func (varC c) foo() { 31 | fmt.Println("Foo Processing", varC) 32 | } 33 | 34 | // Satisfying IntB 35 | func (varC c) bar() { 36 | fmt.Println("Bar Processing", varC) 37 | } 38 | 39 | type b struct { 40 | AA string 41 | XX int 42 | } 43 | 44 | // Structure c has two fields 45 | type c struct { 46 | A a 47 | B b 48 | } 49 | 50 | // Structure compose gets the fields of structure a 51 | type compose struct { 52 | field1 int 53 | a 54 | } 55 | 56 | // Different structures can have methods with the same name 57 | func (A a) A() { 58 | fmt.Println("Function A() for A") 59 | } 60 | 61 | func (B b) A() { 62 | fmt.Println("Function A() for B") 63 | } 64 | 65 | func main() { 66 | var iC c = c{a{120, 12}, b{"-12", -12}} 67 | iC.A.A() 68 | iC.B.A() 69 | 70 | // The following two statements does not work 71 | // ./objO.go:71:33: mixture of field:value and value initializers 72 | // iComp := compose{field1: 123, a{456, 789}} 73 | // ./objO.go:75:32: cannot use promoted field a.XX in struct literal of type compose 74 | //./objO.go:75:41: cannot use promoted field a.YY in struct literal of type compose 75 | // iComp := compose{field1: 123, XX: 456, YY: 789} 76 | 77 | iComp := compose{123, a{456, 789}} 78 | // iComp := compose{field1: 123, a: a{456, 789}} 79 | fmt.Println(iComp.XX, iComp.YY, iComp.field1) 80 | 81 | iC.bar() 82 | processA(iC) 83 | } 84 | -------------------------------------------------------------------------------- /ch04/reflection.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type Secret struct { 9 | Username string 10 | Password string 11 | } 12 | 13 | type Record struct { 14 | Field1 string 15 | Field2 float64 16 | Field3 Secret 17 | } 18 | 19 | func main() { 20 | A := Record{"String value", -12.123, Secret{"Mihalis", "Tsoukalos"}} 21 | 22 | r := reflect.ValueOf(A) 23 | fmt.Println("String value:", r.String()) 24 | 25 | iType := r.Type() 26 | fmt.Printf("i Type: %s\n", iType) 27 | fmt.Printf("The %d fields of %s are\n", r.NumField(), iType) 28 | 29 | for i := 0; i < r.NumField(); i++ { 30 | fmt.Printf("\t%s ", iType.Field(i).Name) 31 | fmt.Printf("\twith type: %s ", r.Field(i).Type()) 32 | fmt.Printf("\tand value _%v_\n", r.Field(i).Interface()) 33 | 34 | // Check whether there are other structures embedded in Record 35 | k := reflect.TypeOf(r.Field(i).Interface()).Kind() 36 | // Need to convert it to string in order to compare it 37 | if k.String() == "struct" { 38 | fmt.Println(r.Field(i).Type()) 39 | } 40 | 41 | // Same as before but using the internal value 42 | if k == reflect.Struct { 43 | fmt.Println(r.Field(i).Type()) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ch04/setValues.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type T struct { 9 | F1 int 10 | F2 string 11 | F3 float64 12 | } 13 | 14 | func main() { 15 | A := T{1, "F2", 3.0} 16 | fmt.Println("A:", A) 17 | 18 | r := reflect.ValueOf(&A).Elem() 19 | fmt.Println("String value:", r.String()) 20 | typeOfA := r.Type() 21 | for i := 0; i < r.NumField(); i++ { 22 | f := r.Field(i) 23 | tOfA := typeOfA.Field(i).Name 24 | fmt.Printf("%d: %s %s = %v\n", i, tOfA, f.Type(), f.Interface()) 25 | 26 | k := reflect.TypeOf(r.Field(i).Interface()).Kind() 27 | if k == reflect.Int { 28 | r.Field(i).SetInt(-100) 29 | } else if k == reflect.String { 30 | r.Field(i).SetString("Changed!") 31 | } 32 | } 33 | 34 | fmt.Println("A:", A) 35 | } 36 | -------------------------------------------------------------------------------- /ch04/sort.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type S1 struct { 9 | F1 int 10 | F2 string 11 | F3 int 12 | } 13 | 14 | // We want to sort S2 records based on the value of F3.F1 15 | // Which is S1.F1 as F3 is an S1 structure 16 | type S2 struct { 17 | F1 int 18 | F2 string 19 | F3 S1 20 | } 21 | 22 | type S2slice []S2 23 | 24 | // Implementing sort.Interface for S2slice 25 | func (a S2slice) Len() int { 26 | return len(a) 27 | } 28 | 29 | // What field to use for comparing 30 | func (a S2slice) Less(i, j int) bool { 31 | return a[i].F3.F1 < a[j].F3.F1 32 | } 33 | 34 | func (a S2slice) Swap(i, j int) { 35 | a[i], a[j] = a[j], a[i] 36 | } 37 | 38 | func main() { 39 | data := []S2{ 40 | S2{1, "One", S1{1, "S1_1", 10}}, 41 | S2{2, "Two", S1{2, "S1_1", 20}}, 42 | S2{-1, "Two", S1{-1, "S1_1", -20}}, 43 | } 44 | fmt.Println("Before:", data) 45 | sort.Sort(S2slice(data)) 46 | fmt.Println("After:", data) 47 | 48 | // Reverse sorting works automatically 49 | sort.Sort(sort.Reverse(S2slice(data))) 50 | fmt.Println("Reverse:", data) 51 | } 52 | -------------------------------------------------------------------------------- /ch04/sortCSV.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/csv" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "sort" 9 | ) 10 | 11 | // Format 1 12 | type F1 struct { 13 | Name string 14 | Surname string 15 | Tel string 16 | LastAccess string 17 | } 18 | 19 | // Format 2 20 | type F2 struct { 21 | Name string 22 | Surname string 23 | Areacode string 24 | Tel string 25 | LastAccess string 26 | } 27 | 28 | type Book1 []F1 29 | type Book2 []F2 30 | 31 | // CSVFILE resides in the home directory of the current user 32 | var CSVFILE = "" 33 | var d1 = Book1{} 34 | var d2 = Book2{} 35 | 36 | func readCSVFile(filepath string) error { 37 | _, err := os.Stat(filepath) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | f, err := os.Open(filepath) 43 | if err != nil { 44 | return err 45 | } 46 | defer f.Close() 47 | 48 | lines, err := csv.NewReader(f).ReadAll() 49 | if err != nil { 50 | return err 51 | } 52 | 53 | var firstLine bool = true 54 | var format1 = true 55 | for _, line := range lines { 56 | if firstLine { 57 | if len(line) == 4 { 58 | format1 = true 59 | } else if len(line) == 5 { 60 | format1 = false 61 | } else { 62 | return errors.New("Unknown File Format!") 63 | } 64 | firstLine = false 65 | } 66 | 67 | if format1 { 68 | if len(line) == 4 { 69 | temp := F1{ 70 | Name: line[0], 71 | Surname: line[1], 72 | Tel: line[2], 73 | LastAccess: line[3], 74 | } 75 | d1 = append(d1, temp) 76 | } 77 | } else { 78 | if len(line) == 5 { 79 | temp := F2{ 80 | Name: line[0], 81 | Surname: line[1], 82 | Areacode: line[2], 83 | Tel: line[3], 84 | LastAccess: line[4], 85 | } 86 | d2 = append(d2, temp) 87 | } 88 | } 89 | } 90 | return nil 91 | } 92 | 93 | // Implement sort.Interface for Book1 94 | func (a Book1) Len() int { 95 | return len(a) 96 | } 97 | 98 | // First based on surname. If they have the same 99 | // surname take into account the name. 100 | func (a Book1) Less(i, j int) bool { 101 | if a[i].Surname == a[j].Surname { 102 | return a[i].Name < a[j].Name 103 | } 104 | return a[i].Surname < a[j].Surname 105 | } 106 | 107 | func (a Book1) Swap(i, j int) { 108 | a[i], a[j] = a[j], a[i] 109 | } 110 | 111 | // Implement sort.Interface for Book2 112 | func (a Book2) Len() int { 113 | return len(a) 114 | } 115 | 116 | // First based on areacode. If they have the same 117 | // areacode take into account the surname. 118 | func (a Book2) Less(i, j int) bool { 119 | if a[i].Areacode == a[j].Areacode { 120 | return a[i].Surname < a[j].Surname 121 | } 122 | return a[i].Areacode < a[j].Areacode 123 | } 124 | 125 | func (a Book2) Swap(i, j int) { 126 | a[i], a[j] = a[j], a[i] 127 | } 128 | 129 | func list(d interface{}) { 130 | switch T := d.(type) { 131 | case Book1: 132 | data := d.(Book1) 133 | for _, v := range data { 134 | fmt.Println(v) 135 | } 136 | case Book2: 137 | data := d.(Book2) 138 | for _, v := range data { 139 | fmt.Println(v) 140 | } 141 | default: 142 | fmt.Printf("Not supported type: %T\n", T) 143 | } 144 | } 145 | 146 | func sortData(data interface{}) { 147 | // type switch 148 | switch T := data.(type) { 149 | case Book1: 150 | d := data.(Book1) 151 | sort.Sort(Book1(d)) 152 | list(d) 153 | case Book2: 154 | d := data.(Book2) 155 | sort.Sort(Book2(d)) 156 | list(d) 157 | default: 158 | fmt.Printf("Not supported type: %T\n", T) 159 | } 160 | } 161 | 162 | func main() { 163 | if len(os.Args) != 1 { 164 | CSVFILE = os.Args[1] 165 | } else { 166 | fmt.Println("No data file!") 167 | return 168 | } 169 | 170 | _, err := os.Stat(CSVFILE) 171 | // If the CSVFILE does not exist, terminate the program 172 | if err != nil { 173 | fmt.Println(CSVFILE, "does not exist!") 174 | return 175 | } 176 | 177 | fileInfo, err := os.Stat(CSVFILE) 178 | // Is it a regular file? 179 | mode := fileInfo.Mode() 180 | if !mode.IsRegular() { 181 | fmt.Println(CSVFILE, "not a regular file!") 182 | return 183 | } 184 | 185 | err = readCSVFile(CSVFILE) 186 | if err != nil { 187 | fmt.Println(err) 188 | return 189 | } 190 | 191 | if len(d1) != 0 { 192 | sortData(d1) 193 | } else { 194 | sortData(d2) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /ch04/sortShapes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "sort" 8 | "time" 9 | ) 10 | 11 | const min = 1 12 | const max = 5 13 | 14 | func rF64(min, max float64) float64 { 15 | return min + rand.Float64()*(max-min) 16 | } 17 | 18 | // Going to sort the shapes based on their volume 19 | type Shape3D interface { 20 | Vol() float64 21 | } 22 | 23 | type Cube struct { 24 | x float64 25 | } 26 | 27 | type Cuboid struct { 28 | x float64 29 | y float64 30 | z float64 31 | } 32 | 33 | type Sphere struct { 34 | r float64 35 | } 36 | 37 | func (c Cube) Vol() float64 { 38 | return c.x * c.x * c.x 39 | } 40 | 41 | func (c Cuboid) Vol() float64 { 42 | return c.x * c.y * c.z 43 | } 44 | 45 | func (c Sphere) Vol() float64 { 46 | return 4 / 3 * math.Pi * c.r * c.r * c.r 47 | } 48 | 49 | // Slice of Shape3D 50 | type shapes []Shape3D 51 | 52 | // Implementing sort.Interface 53 | func (a shapes) Len() int { 54 | return len(a) 55 | } 56 | 57 | func (a shapes) Less(i, j int) bool { 58 | return a[i].Vol() < a[j].Vol() 59 | } 60 | 61 | func (a shapes) Swap(i, j int) { 62 | a[i], a[j] = a[j], a[i] 63 | } 64 | 65 | func PrintShapes(a shapes) { 66 | for _, v := range a { 67 | // fmt.Printf("%.2f ", v) 68 | switch v.(type) { 69 | case Cube: 70 | fmt.Printf("Cube: volume %.2f\n", v.Vol()) 71 | case Cuboid: 72 | fmt.Printf("Cuboid: volume %.2f\n", v.Vol()) 73 | case Sphere: 74 | fmt.Printf("Sphere: volume %.2f\n", v.Vol()) 75 | default: 76 | fmt.Println("Unknown data type!") 77 | } 78 | } 79 | fmt.Println() 80 | } 81 | 82 | func main() { 83 | data := shapes{} 84 | rand.Seed(time.Now().Unix()) 85 | 86 | for i := 0; i < 3; i++ { 87 | cube := Cube{rF64(min, max)} 88 | cuboid := Cuboid{rF64(min, max), rF64(min, max), rF64(min, max)} 89 | sphere := Sphere{rF64(min, max)} 90 | 91 | data = append(data, cube) 92 | data = append(data, cuboid) 93 | data = append(data, sphere) 94 | } 95 | PrintShapes(data) 96 | 97 | // Sorting 98 | sort.Sort(shapes(data)) 99 | PrintShapes(data) 100 | 101 | // Reverse sorting 102 | sort.Sort(sort.Reverse(shapes(data))) 103 | PrintShapes(data) 104 | } 105 | -------------------------------------------------------------------------------- /ch04/typeSwitch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Secret struct { 6 | SecretValue string 7 | } 8 | 9 | type Entry struct { 10 | F1 int 11 | F2 string 12 | F3 Secret 13 | } 14 | 15 | func Teststruct(x interface{}) { 16 | // type switch 17 | switch T := x.(type) { 18 | case Secret: 19 | fmt.Println("Secret type") 20 | case Entry: 21 | fmt.Println("Entry type") 22 | default: 23 | fmt.Printf("Not supported type: %T\n", T) 24 | } 25 | } 26 | 27 | func Learn(x interface{}) { 28 | switch T := x.(type) { 29 | default: 30 | fmt.Printf("Data type: %T\n", T) 31 | } 32 | } 33 | 34 | func main() { 35 | A := Entry{100, "F2", Secret{"myPassword"}} 36 | Teststruct(A) 37 | Teststruct(A.F3) 38 | Teststruct("A string") 39 | 40 | Learn(12.23) 41 | Learn('€') 42 | } 43 | -------------------------------------------------------------------------------- /ch05/defer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func d1() { 8 | for i := 3; i > 0; i-- { 9 | defer fmt.Print(i, " ") 10 | } 11 | } 12 | 13 | func d2() { 14 | for i := 3; i > 0; i-- { 15 | defer func() { 16 | fmt.Print(i, " ") 17 | }() 18 | } 19 | fmt.Println() 20 | } 21 | 22 | func d3() { 23 | for i := 3; i > 0; i-- { 24 | defer func(n int) { 25 | fmt.Print(n, " ") 26 | }(i) 27 | } 28 | } 29 | 30 | func main() { 31 | d1() 32 | d2() 33 | fmt.Println() 34 | d3() 35 | fmt.Println() 36 | } 37 | -------------------------------------------------------------------------------- /ch05/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | postgres: 5 | image: postgres 6 | container_name: postgres 7 | environment: 8 | - POSTGRES_USER=mtsouk 9 | - POSTGRES_PASSWORD=pass 10 | - POSTGRES_DB=master 11 | volumes: 12 | - ./postgres:/var/lib/postgresql/data/ 13 | networks: 14 | - psql 15 | ports: 16 | - "5432:5432" 17 | 18 | volumes: 19 | postgres: 20 | 21 | networks: 22 | psql: 23 | driver: bridge 24 | -------------------------------------------------------------------------------- /ch05/document.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The package works on 2 tables on a PostgreSQL data base server. 4 | 5 | The names of the tables are: 6 | 7 | * Users 8 | * Userdata 9 | 10 | The definitions of the tables in the PostgreSQL server are: 11 | 12 | CREATE TABLE Users ( 13 | ID SERIAL, 14 | Username VARCHAR(100) PRIMARY KEY 15 | ); 16 | 17 | CREATE TABLE Userdata ( 18 | UserID Int NOT NULL, 19 | Name VARCHAR(100), 20 | Surname VARCHAR(100), 21 | Description VARCHAR(200) 22 | ); 23 | 24 | This is rendered as code 25 | 26 | This is not rendered as code 27 | 28 | */ 29 | package document 30 | 31 | // BUG(1): Function ListUsers() not working as expected 32 | // BUG(2): Function AddUser() is too slow 33 | 34 | import ( 35 | "database/sql" 36 | "fmt" 37 | "strings" 38 | ) 39 | 40 | /* 41 | This block of global variables holds the connection details to the Postgres server 42 | Hostname: is the IP or the hostname of the server 43 | Port: is the TCP port the DB server listens to 44 | Username: is the username of the database user 45 | Password: is the password of the database user 46 | Database: is the name of the Database in PostgreSQL 47 | */ 48 | var ( 49 | Hostname = "" 50 | Port = 2345 51 | Username = "" 52 | Password = "" 53 | Database = "" 54 | ) 55 | 56 | // The Userdata structure is for holding full user data 57 | // from the Userdata table and the Username from the 58 | // Users table 59 | type Userdata struct { 60 | ID int 61 | Username string 62 | Name string 63 | Surname string 64 | Description string 65 | } 66 | 67 | // openConnection() is for opening the Postgres connection 68 | // in order to be used by the other functions of the package. 69 | func openConnection() (*sql.DB, error) { 70 | var db *sql.DB 71 | return db, nil 72 | } 73 | 74 | // The function returns the User ID of the username 75 | // -1 if the user does not exist 76 | func exists(username string) int { 77 | fmt.Println("Searching user", username) 78 | return 0 79 | } 80 | 81 | // AddUser adds a new user to the database 82 | // 83 | // Returns new User ID 84 | // -1 if there was an error 85 | func AddUser(d Userdata) int { 86 | d.Username = strings.ToLower(d.Username) 87 | return -1 88 | } 89 | 90 | /* 91 | DeleteUser deletes an existing user if the user exists. 92 | 93 | It requires the User ID of the user to be deleted. 94 | */ 95 | func DeleteUser(id int) error { 96 | fmt.Println(id) 97 | return nil 98 | } 99 | 100 | // ListUsers lists all users in the database 101 | // and returns a slice of Userdata. 102 | func ListUsers() ([]Userdata, error) { 103 | // Data holds the records returned by the SQL query 104 | Data := []Userdata{} 105 | return Data, nil 106 | } 107 | 108 | // UpdateUser is for updating an existing user 109 | // given a Userdata structure. 110 | // The user ID of the user to be updated is found 111 | // inside the function. 112 | func UpdateUser(d Userdata) error { 113 | fmt.Println(d) 114 | return nil 115 | } 116 | -------------------------------------------------------------------------------- /ch05/functions.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func doubleSquare(x int) (int, int) { 6 | return x * 2, x * x 7 | } 8 | 9 | // Sorting from smaller to bigger value 10 | func sortTwo(x, y int) (int, int) { 11 | if x > y { 12 | return y, x 13 | } 14 | return x, y 15 | } 16 | 17 | func main() { 18 | n := 10 19 | d, s := doubleSquare(n) 20 | fmt.Println("Double of", n, "is", d) 21 | fmt.Println("Square of", n, "is", s) 22 | 23 | // An anonymous function 24 | anF := func(param int) int { 25 | return param * param 26 | } 27 | fmt.Println("anF of", n, "is", anF(n)) 28 | 29 | fmt.Println(sortTwo(1, -3)) 30 | fmt.Println(sortTwo(-1, 0)) 31 | } 32 | -------------------------------------------------------------------------------- /ch05/getSchema.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | 9 | _ "github.com/lib/pq" 10 | ) 11 | 12 | func main() { 13 | arguments := os.Args 14 | if len(arguments) != 6 { 15 | fmt.Println("Please provide: hostname port username password db") 16 | return 17 | } 18 | 19 | host := arguments[1] 20 | p := arguments[2] 21 | user := arguments[3] 22 | pass := arguments[4] 23 | database := arguments[5] 24 | 25 | // Port number SHOULD BE an integer 26 | port, err := strconv.Atoi(p) 27 | if err != nil { 28 | fmt.Println("Not a valid port number:", err) 29 | return 30 | } 31 | 32 | // connection string 33 | conn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", 34 | host, port, user, pass, database) 35 | 36 | // open PostgreSQL database 37 | db, err := sql.Open("postgres", conn) 38 | if err != nil { 39 | fmt.Println("Open():", err) 40 | return 41 | } 42 | defer db.Close() 43 | 44 | // Get all databases 45 | rows, err := db.Query(`SELECT "datname" FROM "pg_database" 46 | WHERE datistemplate = false`) 47 | if err != nil { 48 | fmt.Println("Query", err) 49 | return 50 | } 51 | 52 | for rows.Next() { 53 | var name string 54 | err = rows.Scan(&name) 55 | defer rows.Close() 56 | if err != nil { 57 | fmt.Println("Scan", err) 58 | return 59 | } 60 | fmt.Println("*", name) 61 | } 62 | 63 | // Get all tables from __current__ database 64 | query := `SELECT table_name FROM information_schema.tables WHERE 65 | table_schema = 'public' ORDER BY table_name` 66 | rows, err = db.Query(query) 67 | if err != nil { 68 | fmt.Println("Query", err) 69 | return 70 | } 71 | 72 | // This is how you process the rows that are returned from SELECT 73 | for rows.Next() { 74 | var name string 75 | err = rows.Scan(&name) 76 | if err != nil { 77 | fmt.Println("Scan", err) 78 | return 79 | } 80 | fmt.Println("+T", name) 81 | } 82 | defer rows.Close() 83 | } 84 | -------------------------------------------------------------------------------- /ch05/gitVersion.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | var VERSION string 9 | 10 | func main() { 11 | if len(os.Args) == 2 { 12 | if os.Args[1] == "version" { 13 | fmt.Println("Version:", VERSION) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ch05/logDefer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | ) 8 | 9 | var LOG = "/tmp/mGo.log" 10 | 11 | func one(aLog *log.Logger) { 12 | aLog.Println("-- FUNCTION one ------") 13 | defer aLog.Println("-- FUNCTION one ------") 14 | 15 | for i := 0; i < 10; i++ { 16 | aLog.Println(i) 17 | } 18 | } 19 | 20 | func two(aLog *log.Logger) { 21 | aLog.Println("---- FUNCTION two") 22 | defer aLog.Println("FUNCTION two ------") 23 | 24 | for i := 10; i > 0; i-- { 25 | aLog.Println(i) 26 | } 27 | } 28 | 29 | func main() { 30 | perms := os.O_APPEND | os.O_CREATE | os.O_WRONLY 31 | f, err := os.OpenFile(LOG, perms, 0644) 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | defer f.Close() 37 | 38 | iLog := log.New(f, "logDefer ", log.LstdFlags) 39 | iLog.Println("Hello there!") 40 | iLog.Println("Another log entry!") 41 | one(iLog) 42 | two(iLog) 43 | } 44 | -------------------------------------------------------------------------------- /ch05/namedReturn.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func minMax(x, y int) (min, max int) { 10 | if x > y { 11 | min = y 12 | max = x 13 | return min, max 14 | } 15 | 16 | min = x 17 | max = y 18 | return 19 | } 20 | 21 | func main() { 22 | arguments := os.Args 23 | if len(arguments) < 3 { 24 | fmt.Println("The program needs at least 2 arguments!") 25 | return 26 | } 27 | 28 | // No checking here - we trust the user!! 29 | a1, _ := strconv.Atoi(arguments[1]) 30 | a2, _ := strconv.Atoi(arguments[2]) 31 | 32 | fmt.Println(minMax(a1, a2)) 33 | mi, ma := minMax(a1, a2) 34 | fmt.Println(mi, ma) 35 | } 36 | -------------------------------------------------------------------------------- /ch05/postGo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/mactsouk/post05" 9 | ) 10 | 11 | var MIN = 0 12 | var MAX = 26 13 | 14 | func random(min, max int) int { 15 | return rand.Intn(max-min) + min 16 | } 17 | 18 | func getString(length int64) string { 19 | startChar := "A" 20 | temp := "" 21 | var i int64 = 1 22 | for { 23 | myRand := random(MIN, MAX) 24 | newChar := string(startChar[0] + byte(myRand)) 25 | temp = temp + newChar 26 | if i == length { 27 | break 28 | } 29 | i++ 30 | } 31 | return temp 32 | } 33 | 34 | func main() { 35 | post05.Hostname = "localhost" 36 | post05.Port = 5432 37 | post05.Username = "mtsouk" 38 | post05.Password = "pass" 39 | post05.Database = "go" 40 | 41 | data, err := post05.ListUsers() 42 | if err != nil { 43 | fmt.Println(err) 44 | return 45 | } 46 | for _, v := range data { 47 | fmt.Println(v) 48 | } 49 | 50 | SEED := time.Now().Unix() 51 | rand.Seed(SEED) 52 | random_username := getString(5) 53 | 54 | t := post05.Userdata{ 55 | Username: random_username, 56 | Name: "Mihalis", 57 | Surname: "Tsoukalos", 58 | Description: "This is me!"} 59 | 60 | id := post05.AddUser(t) 61 | if id == -1 { 62 | fmt.Println("There was an error adding user", t.Username) 63 | } 64 | 65 | err = post05.DeleteUser(id) 66 | if err != nil { 67 | fmt.Println(err) 68 | } 69 | 70 | // Trying to delete it again! 71 | err = post05.DeleteUser(id) 72 | if err != nil { 73 | fmt.Println(err) 74 | } 75 | 76 | id = post05.AddUser(t) 77 | if id == -1 { 78 | fmt.Println("There was an error adding user", t.Username) 79 | } 80 | 81 | t = post05.Userdata{ 82 | Username: random_username, 83 | Name: "Mihalis", 84 | Surname: "Tsoukalos", 85 | Description: "This might not be me!"} 86 | 87 | err = post05.UpdateUser(t) 88 | if err != nil { 89 | fmt.Println(err) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ch05/returnFunction.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func funRet(i int) func(int) int { 6 | if i < 0 { 7 | return func(k int) int { 8 | k = -k 9 | return k + k 10 | } 11 | } 12 | 13 | return func(k int) int { 14 | return k * k 15 | } 16 | } 17 | 18 | func main() { 19 | n := 10 20 | i := funRet(n) 21 | // The -4 parameter is used for determining 22 | // the anonymous function that will be returned 23 | j := funRet(-4) 24 | 25 | fmt.Printf("%T\n", i) 26 | fmt.Printf("%T %v\n", j, j) 27 | fmt.Println("j", j, j(-5)) 28 | 29 | // Same input parameter but DIFFERENT 30 | // anonymous functions assigned to i and j 31 | fmt.Println(i(10)) 32 | fmt.Println(j(10)) 33 | } 34 | -------------------------------------------------------------------------------- /ch05/sorting.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type Grades struct { 9 | Name string 10 | Surname string 11 | Grade int 12 | } 13 | 14 | func main() { 15 | data := []Grades{{"J.", "Lewis", 10}, {"M.", "Tsoukalos", 7}, 16 | {"D.", "Tsoukalos", 8}, {"J.", "Lewis", 9}} 17 | 18 | isSorted := sort.SliceIsSorted(data, func(i, j int) bool { 19 | return data[i].Grade < data[j].Grade 20 | }) 21 | 22 | if isSorted { 23 | fmt.Println("It is sorted!") 24 | } else { 25 | fmt.Println("It is NOT sorted!") 26 | } 27 | 28 | sort.Slice(data, 29 | func(i, j int) bool { return data[i].Grade < data[j].Grade }) 30 | fmt.Println("By Grade:", data) 31 | } 32 | -------------------------------------------------------------------------------- /ch05/variadic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func addFloats(message string, s ...float64) float64 { 9 | fmt.Println(message) 10 | sum := float64(0) 11 | for _, a := range s { 12 | sum = sum + a 13 | } 14 | s[0] = -1000 15 | return sum 16 | } 17 | 18 | func everything(input ...interface{}) { 19 | fmt.Println(input) 20 | } 21 | 22 | func main() { 23 | sum := addFloats("Adding numbers...", 1.1, 2.12, 3.14, 4, 5, -1, 10) 24 | fmt.Println("Sum:", sum) 25 | s := []float64{1.1, 2.12, 3.14} 26 | sum = addFloats("Adding numbers...", s...) 27 | fmt.Println("Sum:", sum) 28 | everything(s) 29 | 30 | // Cannot directly pass []string as []interface{} 31 | // You have to convert it first! 32 | empty := make([]interface{}, len(os.Args[1:])) 33 | for i, v := range os.Args[1:] { 34 | empty[i] = v 35 | } 36 | everything(empty...) 37 | 38 | // There is a slightly different way to do the conversion 39 | arguments := os.Args[1:] 40 | empty = make([]interface{}, len(arguments)) 41 | for i := range arguments { 42 | empty[i] = arguments[i] 43 | } 44 | everything(empty...) 45 | 46 | // This will work! 47 | str := []string{"One", "Two", "Three"} 48 | everything(str, str, str) 49 | } 50 | -------------------------------------------------------------------------------- /ch06/FScycles.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | var visited = map[string]int{} 10 | 11 | func walkFunction(path string, info os.FileInfo, err error) error { 12 | fileInfo, err := os.Stat(path) 13 | if err != nil { 14 | return err 15 | } 16 | 17 | fileInfo, _ = os.Lstat(path) 18 | mode := fileInfo.Mode() 19 | 20 | // Find regular directories first 21 | if mode.IsDir() { 22 | abs, _ := filepath.Abs(path) 23 | _, ok := visited[abs] 24 | if ok { 25 | fmt.Println("Found cycle:", abs) 26 | return nil 27 | } 28 | visited[abs]++ 29 | return nil 30 | } 31 | 32 | // Find symbolic links to directories 33 | if mode&os.ModeSymlink != 0 { 34 | temp, err := os.Readlink(path) 35 | if err != nil { 36 | fmt.Println("os.Readlink():", err) 37 | return err 38 | } 39 | 40 | newPath, err := filepath.EvalSymlinks(temp) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | linkFileInfo, err := os.Stat(newPath) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | linkMode := linkFileInfo.Mode() 51 | if linkMode.IsDir() { 52 | fmt.Println("Following...", path, "-->", newPath) 53 | abs, _ := filepath.Abs(newPath) 54 | _, ok := visited[abs] 55 | if ok { 56 | fmt.Println("Found cycle!", abs) 57 | return nil 58 | } 59 | visited[abs]++ 60 | 61 | return filepath.Walk(newPath, walkFunction) 62 | } 63 | } 64 | return nil 65 | } 66 | 67 | func main() { 68 | arguments := os.Args 69 | if len(arguments) == 1 { 70 | fmt.Println("Not enough arguments!") 71 | return 72 | } 73 | 74 | Path := arguments[1] 75 | err := filepath.Walk(Path, walkFunction) 76 | if err != nil { 77 | fmt.Println(err) 78 | } 79 | 80 | for k, v := range visited { 81 | if v > 1 { 82 | fmt.Println(k, v) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /ch06/JSON2XML.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | type XMLrec struct { 11 | Name string `xml:"username"` 12 | Surname string `xml:"surname,omitempty"` 13 | Year int `xml:"creationyear,omitempty"` 14 | } 15 | 16 | type JSONrec struct { 17 | Name string `json:"username"` 18 | Surname string `json:"surname,omitempty"` 19 | Year int `json:"creationyear,omitempty"` 20 | } 21 | 22 | func main() { 23 | arguments := os.Args 24 | if len(arguments) == 1 { 25 | fmt.Println("Need XML or JSON input") 26 | return 27 | } 28 | // This can be a JSON or an XML record 29 | input := []byte(arguments[1]) 30 | fmt.Println(string(input)) 31 | 32 | // Check if it is XML 33 | checkJSON := false 34 | tempXML := XMLrec{} 35 | err := xml.Unmarshal(input, &tempXML) 36 | if err != nil { 37 | checkJSON = true 38 | } else { 39 | tempJSON := JSONrec{Name: tempXML.Name} 40 | if tempXML.Surname != "" { 41 | tempJSON.Surname = tempXML.Surname 42 | } 43 | if tempXML.Year != 0 { 44 | tempJSON.Year = tempXML.Year 45 | } 46 | s, err := json.Marshal(&tempJSON) 47 | if err != nil { 48 | fmt.Println(err) 49 | return 50 | } 51 | fmt.Println(string(s)) 52 | return 53 | } 54 | 55 | if !checkJSON { 56 | return 57 | } 58 | 59 | // ELSE Check if it is JSON 60 | tempXML = XMLrec{} 61 | tempJSON := JSONrec{} 62 | err = json.Unmarshal(input, &tempJSON) 63 | if err != nil { 64 | fmt.Println("Not valid input") 65 | return 66 | } else { 67 | tempXML = XMLrec{Name: tempJSON.Name} 68 | if tempJSON.Surname != "" { 69 | tempXML.Surname = tempJSON.Surname 70 | } 71 | if tempJSON.Year != 0 { 72 | tempXML.Year = tempJSON.Year 73 | } 74 | s, err := xml.Marshal(&tempXML) 75 | if err != nil { 76 | fmt.Println(err) 77 | return 78 | } 79 | fmt.Println(string(s)) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ch06/JSONstreams.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "math/rand" 8 | ) 9 | 10 | type Data struct { 11 | Key string `json:"key"` 12 | Val int `json:"value"` 13 | } 14 | 15 | var DataRecords []Data 16 | 17 | func random(min, max int) int { 18 | return rand.Intn(max-min) + min 19 | } 20 | 21 | var MIN = 0 22 | var MAX = 26 23 | 24 | func getString(l int64) string { 25 | startChar := "A" 26 | temp := "" 27 | var i int64 = 1 28 | for { 29 | myRand := random(MIN, MAX) 30 | newChar := string(startChar[0] + byte(myRand)) 31 | temp = temp + newChar 32 | if i == l { 33 | break 34 | } 35 | i++ 36 | } 37 | return temp 38 | } 39 | 40 | // DeSerialize decodes a serialized slice with JSON records 41 | func DeSerialize(e *json.Decoder, slice interface{}) error { 42 | return e.Decode(slice) 43 | } 44 | 45 | // Serialize serializes a slice with JSON records 46 | func Serialize(e *json.Encoder, slice interface{}) error { 47 | return e.Encode(slice) 48 | } 49 | 50 | func main() { 51 | // Create sample data 52 | var i int 53 | var t Data 54 | for i = 0; i < 2; i++ { 55 | t = Data{ 56 | Key: getString(5), 57 | Val: random(1, 100), 58 | } 59 | DataRecords = append(DataRecords, t) 60 | } 61 | 62 | // bytes.Buffer is both an io.Reader and io.Writer 63 | buf := new(bytes.Buffer) 64 | 65 | encoder := json.NewEncoder(buf) 66 | err := Serialize(encoder, DataRecords) 67 | if err != nil { 68 | fmt.Println(err) 69 | return 70 | } 71 | fmt.Print("After Serialize:", buf) 72 | 73 | decoder := json.NewDecoder(buf) 74 | var temp []Data 75 | err = DeSerialize(decoder, &temp) 76 | fmt.Println("After DeSerialize:") 77 | for index, value := range temp { 78 | fmt.Println(index, value) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ch06/README.md: -------------------------------------------------------------------------------- 1 | ## What if a textfile ends without a newline? 2 | 3 | As this is not mentioned in the *Mastering Go, 3rd edition* book, I have included separate version of `byWord.go`, `byLine.go` and `byCharacter.go` named `byWord_noNewLine.go`, `byLine_noNewLine.go` and `byCharacter_noNewLine.go`, respectively that show how to handle plain text files that do not end with a newline character. 4 | 5 | Enjoy! 6 | 7 | -------------------------------------------------------------------------------- /ch06/ReadDirEntry.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func GetSize(path string) (int64, error) { 10 | contents, err := os.ReadDir(path) 11 | if err != nil { 12 | return -1, err 13 | } 14 | 15 | var total int64 16 | for _, entry := range contents { 17 | // Visit directory entries 18 | if entry.IsDir() { 19 | temp, err := GetSize(filepath.Join(path, entry.Name())) 20 | if err != nil { 21 | return -1, err 22 | } 23 | total += temp 24 | // Get size of each non-directory entry 25 | } else { 26 | info, err := entry.Info() 27 | if err != nil { 28 | return -1, err 29 | } 30 | // Returns an int64 value 31 | total += info.Size() 32 | } 33 | } 34 | return total, nil 35 | } 36 | 37 | func main() { 38 | arguments := os.Args 39 | if len(arguments) == 1 { 40 | fmt.Println("Need a ") 41 | return 42 | } 43 | 44 | root, err := filepath.EvalSymlinks(arguments[1]) 45 | fileInfo, err := os.Stat(root) 46 | if err != nil { 47 | fmt.Println(err) 48 | return 49 | } 50 | 51 | fileInfo, _ = os.Lstat(root) 52 | mode := fileInfo.Mode() 53 | if !mode.IsDir() { 54 | fmt.Println(root, "not a directory!") 55 | return 56 | } 57 | 58 | i, err := GetSize(root) 59 | if err != nil { 60 | fmt.Println(err) 61 | return 62 | } 63 | fmt.Println("Total Size:", i) 64 | } 65 | -------------------------------------------------------------------------------- /ch06/byCharacter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func charByChar(file string) error { 11 | f, err := os.Open(file) 12 | if err != nil { 13 | return err 14 | } 15 | defer f.Close() 16 | 17 | r := bufio.NewReader(f) 18 | for { 19 | line, err := r.ReadString('\n') 20 | if err == io.EOF { 21 | break 22 | } else if err != nil { 23 | fmt.Printf("error reading file %s", err) 24 | return err 25 | } 26 | 27 | for _, x := range line { 28 | fmt.Println(string(x)) 29 | } 30 | } 31 | return nil 32 | } 33 | 34 | func main() { 35 | args := os.Args 36 | if len(args) == 1 { 37 | fmt.Printf("usage: byCharacter [ ...]\n") 38 | return 39 | } 40 | 41 | for _, file := range args[1:] { 42 | err := charByChar(file) 43 | if err != nil { 44 | fmt.Println(err) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ch06/byCharacter_noNewLine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func charByChar(file string) error { 11 | f, err := os.Open(file) 12 | if err != nil { 13 | return err 14 | } 15 | defer f.Close() 16 | 17 | r := bufio.NewReader(f) 18 | for { 19 | line, err := r.ReadString('\n') 20 | if err == io.EOF { 21 | if len(line) != 0 { 22 | for _, x := range line { 23 | fmt.Println(string(x)) 24 | } 25 | } 26 | break 27 | } else if err != nil { 28 | fmt.Printf("error reading file %s", err) 29 | return err 30 | } 31 | 32 | for _, x := range line { 33 | fmt.Println(string(x)) 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | func main() { 40 | args := os.Args 41 | if len(args) == 1 { 42 | fmt.Printf("usage: byCharacter [ ...]\n") 43 | return 44 | } 45 | 46 | for _, file := range args[1:] { 47 | err := charByChar(file) 48 | if err != nil { 49 | fmt.Println(err) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch06/byLine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func lineByLine(file string) error { 11 | f, err := os.Open(file) 12 | if err != nil { 13 | return err 14 | } 15 | defer f.Close() 16 | 17 | r := bufio.NewReader(f) 18 | for { 19 | line, err := r.ReadString('\n') 20 | if err == io.EOF { 21 | break 22 | } else if err != nil { 23 | fmt.Printf("error reading file %s", err) 24 | return err 25 | } 26 | fmt.Print(line) 27 | } 28 | return nil 29 | } 30 | 31 | func main() { 32 | args := os.Args 33 | if len(args) == 1 { 34 | fmt.Printf("usage: byLine [ ...]\n") 35 | return 36 | } 37 | 38 | for _, file := range args[1:] { 39 | err := lineByLine(file) 40 | if err != nil { 41 | fmt.Println(err) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ch06/byLine_noNewLine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func lineByLine(file string) error { 11 | f, err := os.Open(file) 12 | if err != nil { 13 | return err 14 | } 15 | defer f.Close() 16 | 17 | r := bufio.NewReader(f) 18 | for { 19 | line, err := r.ReadString('\n') 20 | if err == io.EOF { 21 | if len(line) != 0 { 22 | fmt.Println(line) 23 | } 24 | break 25 | } else if err != nil { 26 | fmt.Printf("error reading file %s", err) 27 | return err 28 | } 29 | fmt.Print(line) 30 | } 31 | return nil 32 | } 33 | 34 | func main() { 35 | args := os.Args 36 | if len(args) == 1 { 37 | fmt.Printf("usage: byLine [ ...]\n") 38 | return 39 | } 40 | 41 | for _, file := range args[1:] { 42 | err := lineByLine(file) 43 | if err != nil { 44 | fmt.Println(err) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ch06/byWord.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | "regexp" 9 | ) 10 | 11 | func wordByWord(file string) error { 12 | f, err := os.Open(file) 13 | if err != nil { 14 | return err 15 | } 16 | defer f.Close() 17 | 18 | r := bufio.NewReader(f) 19 | for { 20 | line, err := r.ReadString('\n') 21 | if err == io.EOF { 22 | break 23 | } else if err != nil { 24 | fmt.Printf("error reading file %s", err) 25 | return err 26 | } 27 | 28 | re := regexp.MustCompile("[^\\s]+") 29 | words := re.FindAllString(line, -1) 30 | for i := 0; i < len(words); i++ { 31 | fmt.Println(words[i]) 32 | } 33 | } 34 | return nil 35 | } 36 | 37 | func main() { 38 | args := os.Args 39 | if len(args) == 1 { 40 | fmt.Printf("usage: byWord [ ...]\n") 41 | return 42 | } 43 | 44 | for _, file := range args[1:] { 45 | err := wordByWord(file) 46 | if err != nil { 47 | fmt.Println(err) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ch06/byWord_noNewLine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | "regexp" 9 | ) 10 | 11 | func wordByWord(file string) error { 12 | f, err := os.Open(file) 13 | if err != nil { 14 | return err 15 | } 16 | defer f.Close() 17 | 18 | r := bufio.NewReader(f) 19 | for { 20 | line, err := r.ReadString('\n') 21 | if err == io.EOF { 22 | if len(line) != 0 { 23 | re := regexp.MustCompile("[^\\s]+") 24 | words := re.FindAllString(line, -1) 25 | for i := 0; i < len(words); i++ { 26 | fmt.Println(words[i]) 27 | } 28 | } 29 | break 30 | } else if err != nil { 31 | fmt.Printf("error reading file %s", err) 32 | return err 33 | } 34 | 35 | re := regexp.MustCompile("[^\\s]+") 36 | words := re.FindAllString(line, -1) 37 | for i := 0; i < len(words); i++ { 38 | fmt.Println(words[i]) 39 | } 40 | } 41 | return nil 42 | } 43 | 44 | func main() { 45 | args := os.Args 46 | if len(args) == 1 { 47 | fmt.Printf("usage: byWord [ ...]\n") 48 | return 49 | } 50 | 51 | for _, file := range args[1:] { 52 | err := wordByWord(file) 53 | if err != nil { 54 | fmt.Println(err) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ch06/devRandom.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | f, err := os.Open("/dev/random") 11 | defer f.Close() 12 | 13 | if err != nil { 14 | fmt.Println(err) 15 | return 16 | } 17 | 18 | var seed int64 19 | binary.Read(f, binary.LittleEndian, &seed) 20 | fmt.Println("Seed:", seed) 21 | } 22 | -------------------------------------------------------------------------------- /ch06/embedFiles.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | //go:embed static/image.png 10 | var f1 []byte 11 | 12 | //go:embed static/textfile 13 | var f2 string 14 | 15 | func writeToFile(s []byte, path string) error { 16 | fd, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) 17 | if err != nil { 18 | return err 19 | } 20 | defer fd.Close() 21 | 22 | n, err := fd.Write(s) 23 | if err != nil { 24 | return err 25 | } 26 | fmt.Printf("wrote %d bytes\n", n) 27 | return nil 28 | } 29 | 30 | func main() { 31 | arguments := os.Args 32 | if len(arguments) == 1 { 33 | fmt.Println("Print select 1|2") 34 | return 35 | } 36 | 37 | fmt.Println("f1:", len(f1), "f2:", len(f2)) 38 | 39 | switch arguments[1] { 40 | case "1": 41 | filename := "/tmp/temporary.png" 42 | err := writeToFile(f1, filename) 43 | if err != nil { 44 | fmt.Println(err) 45 | return 46 | } 47 | case "2": 48 | fmt.Print(f2) 49 | default: 50 | fmt.Println("Not a valid option!") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch06/encodeDecode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type UseAll struct { 9 | Name string `json:"username"` 10 | Surname string `json:"surname"` 11 | Year int `json:"created"` 12 | } 13 | 14 | func main() { 15 | useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021} 16 | 17 | // Regular Structure 18 | // Encoding JSON data -> Convert Go Structure to JSON record with fields 19 | t, err := json.Marshal(&useall) 20 | if err != nil { 21 | fmt.Println(err) 22 | } else { 23 | fmt.Printf("Value %s\n", t) 24 | } 25 | 26 | // Decoding JSON data given as a string 27 | str := `{"username": "M.", "surname": "Ts", "created":2020}` 28 | // Convert string into a byte slice 29 | jsonRecord := []byte(str) 30 | // Create a structure variable to store the result 31 | temp := UseAll{} 32 | err = json.Unmarshal(jsonRecord, &temp) 33 | if err != nil { 34 | fmt.Println(err) 35 | } else { 36 | fmt.Printf("Data type: %T with value %v\n", temp, temp) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ch06/ioFS.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "io/fs" 7 | "os" 8 | ) 9 | 10 | //go:embed static 11 | var f embed.FS 12 | 13 | var searchString string 14 | 15 | func walkFunction(path string, d fs.DirEntry, err error) error { 16 | if err != nil { 17 | return err 18 | } 19 | fmt.Printf("Path=%q, isDir=%v\n", path, d.IsDir()) 20 | return nil 21 | } 22 | 23 | func walkSearch(path string, d fs.DirEntry, err error) error { 24 | if err != nil { 25 | return err 26 | } 27 | if d.Name() == searchString { 28 | fileInfo, err := fs.Stat(f, path) 29 | if err != nil { 30 | return err 31 | } 32 | fmt.Println("Found", path, "with size", fileInfo.Size()) 33 | return nil 34 | } 35 | return nil 36 | } 37 | 38 | func list(f embed.FS) error { 39 | return fs.WalkDir(f, ".", walkFunction) 40 | } 41 | 42 | func search(f embed.FS) error { 43 | return fs.WalkDir(f, ".", walkSearch) 44 | } 45 | 46 | func extract(f embed.FS, filepath string) ([]byte, error) { 47 | return fs.ReadFile(f, filepath) 48 | } 49 | 50 | func writeToFile(s []byte, path string) error { 51 | fd, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) 52 | if err != nil { 53 | return err 54 | } 55 | defer fd.Close() 56 | 57 | n, err := fd.Write(s) 58 | if err != nil { 59 | return err 60 | } 61 | fmt.Printf("wrote %d bytes\n", n) 62 | return nil 63 | } 64 | 65 | func main() { 66 | // At this point we do not know what is included in ./static 67 | 68 | // List all files 69 | err := list(f) 70 | if err != nil { 71 | fmt.Println(err) 72 | return 73 | } 74 | 75 | // Search 76 | searchString = "file.txt" 77 | err = search(f) 78 | if err != nil { 79 | fmt.Println(err) 80 | return 81 | } 82 | 83 | // Extract into a byte slice 84 | buffer, err := extract(f, "static/file.txt") 85 | if err != nil { 86 | fmt.Println(err) 87 | return 88 | } 89 | 90 | // Save it to an actual file 91 | err = writeToFile(buffer, "/tmp/IOFS.txt") 92 | if err != nil { 93 | fmt.Println(err) 94 | return 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ch06/ioInterface.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | type S1 struct { 10 | F1 int 11 | F2 string 12 | } 13 | 14 | type S2 struct { 15 | F1 S1 16 | text []byte 17 | } 18 | 19 | // Using pointer to S1 for changes to be persistent when the method exits 20 | func (s *S1) Read(p []byte) (n int, err error) { 21 | fmt.Print("Give me your name: ") 22 | fmt.Scanln(&p) 23 | s.F2 = string(p) 24 | return len(p), nil 25 | } 26 | 27 | func (s *S1) Write(p []byte) (n int, err error) { 28 | if s.F1 < 0 { 29 | return -1, nil 30 | } 31 | 32 | for i := 0; i < s.F1; i++ { 33 | fmt.Printf("%s ", p) 34 | } 35 | fmt.Println() 36 | return s.F1, nil 37 | } 38 | 39 | func (s S2) eof() bool { 40 | return len(s.text) == 0 41 | } 42 | 43 | func (s *S2) readByte() byte { 44 | // this function assumes that eof() check was done before 45 | temp := s.text[0] 46 | s.text = s.text[1:] 47 | return temp 48 | } 49 | 50 | func (s *S2) Read(p []byte) (n int, err error) { 51 | if s.eof() { 52 | err = io.EOF 53 | return 54 | } 55 | 56 | l := len(p) 57 | if l > 0 { 58 | for n < l { 59 | p[n] = s.readByte() 60 | n++ 61 | if s.eof() { 62 | s.text = s.text[0:0] 63 | break 64 | } 65 | } 66 | } 67 | return 68 | } 69 | 70 | func main() { 71 | s1var := S1{4, "Hello"} 72 | fmt.Println(s1var) 73 | 74 | buf := make([]byte, 2) 75 | _, err := s1var.Read(buf) 76 | if err != nil { 77 | fmt.Println(err) 78 | return 79 | } 80 | fmt.Println("Read:", s1var.F2) 81 | _, _ = s1var.Write([]byte("Hello There!")) 82 | 83 | s2var := S2{F1: s1var, text: []byte("Hello world!!")} 84 | // Read s2var.text 85 | r := bufio.NewReader(&s2var) 86 | 87 | for { 88 | n, err := r.Read(buf) 89 | if err == io.EOF { 90 | break 91 | } else if err != nil { 92 | fmt.Println("*", err) 93 | break 94 | } 95 | fmt.Println("**", n, string(buf[:n])) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ch06/jsonViper.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | type ConfigStructure struct { 12 | MacPass string `mapstructure:"macos"` 13 | LinuxPass string `mapstructure:"linux"` 14 | WindowsPass string `mapstructure:"windows"` 15 | PostHost string `mapstructure:"postgres"` 16 | MySQLHost string `mapstructure:"mysql"` 17 | MongoHost string `mapstructure:"mongodb"` 18 | } 19 | 20 | func PrettyPrint(v interface{}) (err error) { 21 | b, err := json.MarshalIndent(v, "", " ") 22 | if err == nil { 23 | fmt.Println(string(b)) 24 | } 25 | return 26 | } 27 | 28 | var CONFIG = ".config.json" 29 | 30 | func main() { 31 | 32 | if len(os.Args) == 1 { 33 | fmt.Println("Using default file", CONFIG) 34 | } else { 35 | CONFIG = os.Args[1] 36 | } 37 | 38 | viper.SetConfigType("json") 39 | viper.SetConfigFile(CONFIG) 40 | fmt.Printf("Using config: %s\n", viper.ConfigFileUsed()) 41 | viper.ReadInConfig() 42 | 43 | if viper.IsSet("macos") { 44 | fmt.Println("macos:", viper.Get("macos")) 45 | } else { 46 | fmt.Println("macos not set!") 47 | } 48 | 49 | if viper.IsSet("active") { 50 | value := viper.GetBool("active") 51 | if value { 52 | postgres := viper.Get("postgres") 53 | mysql := viper.Get("mysql") 54 | mongo := viper.Get("mongodb") 55 | fmt.Println("P:", postgres, "My:", mysql, "Mo:", mongo) 56 | } 57 | } else { 58 | fmt.Println("active is not set!") 59 | } 60 | 61 | if !viper.IsSet("DoesNotExist") { 62 | fmt.Println("DoesNotExist is not set!") 63 | } 64 | 65 | var t ConfigStructure 66 | err := viper.Unmarshal(&t) 67 | if err != nil { 68 | fmt.Println(err) 69 | return 70 | } 71 | PrettyPrint(t) 72 | } 73 | -------------------------------------------------------------------------------- /ch06/myConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "macos": "pass_macos", 3 | "linux": "pass_linux", 4 | "windows": "pass_windows", 5 | 6 | "active": true, 7 | "postgres": "machine1", 8 | "mysql": "machine2", 9 | "mongodb": "machine3" 10 | } 11 | -------------------------------------------------------------------------------- /ch06/prettyPrint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "math/rand" 8 | ) 9 | 10 | type Data struct { 11 | Key string `json:"key"` 12 | Val int `json:"value"` 13 | } 14 | 15 | var DataRecords []Data 16 | 17 | func random(min, max int) int { 18 | return rand.Intn(max-min) + min 19 | } 20 | 21 | var MIN = 0 22 | var MAX = 26 23 | 24 | func getString(l int64) string { 25 | startChar := "A" 26 | temp := "" 27 | var i int64 = 1 28 | for { 29 | myRand := random(MIN, MAX) 30 | newChar := string(startChar[0] + byte(myRand)) 31 | temp = temp + newChar 32 | if i == l { 33 | break 34 | } 35 | i++ 36 | } 37 | return temp 38 | } 39 | 40 | func PrettyPrint(v interface{}) (err error) { 41 | b, err := json.MarshalIndent(v, "", "\t") 42 | if err == nil { 43 | fmt.Println(string(b)) 44 | } 45 | return err 46 | } 47 | 48 | func JSONstream(data interface{}) (string, error) { 49 | buffer := new(bytes.Buffer) 50 | encoder := json.NewEncoder(buffer) 51 | encoder.SetIndent("", "\t") 52 | 53 | err := encoder.Encode(data) 54 | if err != nil { 55 | return "", err 56 | } 57 | return buffer.String(), nil 58 | } 59 | 60 | func main() { 61 | // Create random records 62 | var i int 63 | var t Data 64 | for i = 0; i < 2; i++ { 65 | t = Data{ 66 | Key: getString(5), 67 | Val: random(1, 100), 68 | } 69 | DataRecords = append(DataRecords, t) 70 | } 71 | 72 | fmt.Println("Last record:", t) 73 | _ = PrettyPrint(t) 74 | 75 | val, _ := JSONstream(DataRecords) 76 | fmt.Println(val) 77 | } 78 | -------------------------------------------------------------------------------- /ch06/printSource.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | ) 7 | 8 | //go:embed printSource.go 9 | var src string 10 | 11 | func main() { 12 | fmt.Print(src) 13 | } 14 | -------------------------------------------------------------------------------- /ch06/readSize.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strconv" 8 | ) 9 | 10 | func readSize(f *os.File, size int) []byte { 11 | buffer := make([]byte, size) 12 | n, err := f.Read(buffer) 13 | 14 | // io.EOF is a special case and is treated as such 15 | if err == io.EOF { 16 | return nil 17 | } 18 | 19 | if err != nil { 20 | fmt.Println(err) 21 | return nil 22 | } 23 | return buffer[0:n] 24 | } 25 | 26 | func main() { 27 | arguments := os.Args 28 | if len(arguments) != 3 { 29 | fmt.Println(" ") 30 | return 31 | } 32 | 33 | bufferSize, err := strconv.Atoi(os.Args[1]) 34 | if err != nil { 35 | fmt.Println(err) 36 | return 37 | } 38 | 39 | file := os.Args[2] 40 | f, err := os.Open(file) 41 | if err != nil { 42 | fmt.Println(err) 43 | return 44 | } 45 | defer f.Close() 46 | 47 | readData := readSize(f, bufferSize) 48 | if readData != nil { 49 | fmt.Print(string(readData)) 50 | } else { 51 | return 52 | } 53 | fmt.Println() 54 | } 55 | -------------------------------------------------------------------------------- /ch06/signals.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | "time" 9 | ) 10 | 11 | func handleSignal(sig os.Signal) { 12 | fmt.Println("handleSignal() Caught:", sig) 13 | } 14 | 15 | func main() { 16 | fmt.Printf("Process ID: %d\n", os.Getpid()) 17 | sigs := make(chan os.Signal, 1) 18 | signal.Notify(sigs) 19 | start := time.Now() 20 | go func() { 21 | for { 22 | sig := <-sigs 23 | switch sig { 24 | case syscall.SIGINT: 25 | duration := time.Since(start) 26 | fmt.Println("Execution time:", duration) 27 | case syscall.SIGINFO: 28 | handleSignal(sig) 29 | // do not use return here because the goroutine will exit 30 | // but the time.Sleep() will continue to work! 31 | os.Exit(0) 32 | default: 33 | fmt.Println("Caught:", sig) 34 | } 35 | } 36 | }() 37 | 38 | for { 39 | fmt.Print("+") 40 | time.Sleep(10 * time.Second) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ch06/static/file.txt: -------------------------------------------------------------------------------- 1 | Data to write 2 | -------------------------------------------------------------------------------- /ch06/static/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mactsouk/mastering-Go-3rd/1c581a41fbbd809d204e049390ee2223cc3af65d/ch06/static/image.png -------------------------------------------------------------------------------- /ch06/static/textfile: -------------------------------------------------------------------------------- 1 | Data to write 2 | -------------------------------------------------------------------------------- /ch06/tagsJSON.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // Ignoring empty fields in JSON 9 | type NoEmpty struct { 10 | Name string `json:"username"` 11 | Surname string `json:"surname"` 12 | Year int `json:"creationyear,omitempty"` 13 | } 14 | 15 | // Removing private fields and ignoring empty fields 16 | type Password struct { 17 | Name string `json:"username"` 18 | Surname string `json:"surname,omitempty"` 19 | Year int `json:"creationyear,omitempty"` 20 | Pass string `json:"-"` 21 | } 22 | 23 | func main() { 24 | noempty := NoEmpty{Name: "Mihalis"} 25 | password := Password{Name: "Mihalis", Pass: "myPassword"} 26 | 27 | // Ignoring empty fields in JSON 28 | noEmptyVar, err := json.Marshal(&noempty) 29 | if err != nil { 30 | fmt.Println(err) 31 | } else { 32 | fmt.Printf("noEmptyVar decoded with value %s\n", noEmptyVar) 33 | } 34 | 35 | // Removing private fields 36 | passwordVar, err := json.Marshal(&password) 37 | if err != nil { 38 | fmt.Println(err) 39 | } else { 40 | fmt.Printf("password decoded with value %s\n", passwordVar) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ch06/useViper.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/pflag" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func aliasNormalizeFunc(f *pflag.FlagSet, n string) pflag.NormalizedName { 11 | switch n { 12 | case "pass": 13 | n = "password" 14 | break 15 | case "ps": 16 | n = "password" 17 | break 18 | } 19 | return pflag.NormalizedName(n) 20 | } 21 | 22 | func main() { 23 | pflag.StringP("name", "n", "Mike", "Name parameter") 24 | pflag.StringP("password", "p", "hardToGuess", "Password") 25 | pflag.CommandLine.SetNormalizeFunc(aliasNormalizeFunc) 26 | 27 | pflag.Parse() 28 | viper.BindPFlags(pflag.CommandLine) 29 | 30 | name := viper.GetString("name") 31 | password := viper.GetString("password") 32 | 33 | fmt.Println(name, password) 34 | 35 | // Reading an Environment variable 36 | viper.BindEnv("GOMAXPROCS") 37 | val := viper.Get("GOMAXPROCS") 38 | if val != nil { 39 | fmt.Println("GOMAXPROCS:", val) 40 | } 41 | 42 | // Setting an Environment variable 43 | viper.Set("GOMAXPROCS", 16) 44 | val = viper.Get("GOMAXPROCS") 45 | fmt.Println("GOMAXPROCS:", val) 46 | } 47 | -------------------------------------------------------------------------------- /ch06/writeFile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | buffer := []byte("Data to write\n") 12 | 13 | f1, err := os.Create("/tmp/f1.txt") 14 | if err != nil { 15 | fmt.Println("Cannot create file", err) 16 | return 17 | } 18 | defer f1.Close() 19 | fmt.Fprintf(f1, string(buffer)) 20 | 21 | f2, err := os.Create("/tmp/f2.txt") 22 | if err != nil { 23 | fmt.Println("Cannot create file", err) 24 | return 25 | } 26 | defer f2.Close() 27 | n, err := f2.WriteString(string(buffer)) 28 | fmt.Printf("wrote %d bytes\n", n) 29 | 30 | f3, err := os.Create("/tmp/f3.txt") 31 | if err != nil { 32 | fmt.Println(err) 33 | return 34 | } 35 | w := bufio.NewWriter(f3) 36 | n, err = w.WriteString(string(buffer)) 37 | fmt.Printf("wrote %d bytes\n", n) 38 | w.Flush() 39 | 40 | f := "/tmp/f4.txt" 41 | f4, err := os.Create(f) 42 | if err != nil { 43 | fmt.Println(err) 44 | return 45 | } 46 | defer f4.Close() 47 | 48 | for i := 0; i < 5; i++ { 49 | n, err = io.WriteString(f4, string(buffer)) 50 | if err != nil { 51 | fmt.Println(err) 52 | return 53 | } 54 | fmt.Printf("wrote %d bytes\n", n) 55 | } 56 | 57 | // Append to a file 58 | f4, err = os.OpenFile(f, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 59 | if err != nil { 60 | fmt.Println(err) 61 | return 62 | } 63 | defer f4.Close() 64 | 65 | // Write() needs a byte slice 66 | n, err = f4.Write([]byte("Put some more data at the end.\n")) 67 | if err != nil { 68 | fmt.Println(err) 69 | return 70 | } 71 | fmt.Printf("wrote %d bytes\n", n) 72 | } 73 | -------------------------------------------------------------------------------- /ch06/xml.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/xml" 5 | "fmt" 6 | ) 7 | 8 | type Employee struct { 9 | XMLName xml.Name `xml:"employee"` 10 | ID int `xml:"id,attr"` 11 | FirstName string `xml:"name>first"` 12 | LastName string `xml:"name>last"` 13 | Height float32 `xml:"height,omitempty"` 14 | Address 15 | Comment string `xml:",comment"` 16 | } 17 | 18 | type Address struct { 19 | City, Country string 20 | } 21 | 22 | func main() { 23 | r := Employee{ID: 7, FirstName: "Mihalis", LastName: "Tsoukalos"} 24 | r.Comment = "Technical Writer + DevOps" 25 | r.Address = Address{"SomeWhere 12", "12312, Greece"} 26 | 27 | output, err := xml.MarshalIndent(&r, " ", " ") 28 | if err != nil { 29 | fmt.Println("Error:", err) 30 | } 31 | output = []byte(xml.Header + string(output)) 32 | fmt.Printf("%s\n", output) 33 | } 34 | -------------------------------------------------------------------------------- /ch06/yaml.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gopkg.in/yaml.v2" 7 | ) 8 | 9 | var yamlfile = ` 10 | image: Golang 11 | matrix: 12 | docker: python 13 | version: [2.7, 3.9] 14 | ` 15 | 16 | type Mat struct { 17 | DockerImage string `yaml:"docker"` 18 | Version []float32 `yaml:",flow"` 19 | } 20 | 21 | type YAML struct { 22 | Image string 23 | Matrix Mat 24 | } 25 | 26 | func main() { 27 | data := YAML{} 28 | 29 | err := yaml.Unmarshal([]byte(yamlfile), &data) 30 | if err != nil { 31 | fmt.Println(err) 32 | return 33 | } 34 | fmt.Printf("After Unmarshal (Structure):\n%v\n\n", data) 35 | 36 | d, err := yaml.Marshal(&data) 37 | if err != nil { 38 | fmt.Println(err) 39 | return 40 | } 41 | fmt.Printf("After Marshal (YAML code):\n%s\n", string(d)) 42 | } 43 | -------------------------------------------------------------------------------- /ch07/addDone.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | count := 20 11 | fmt.Printf("Going to create %d goroutines.\n", count) 12 | 13 | flag := true 14 | if len(os.Args) == 1 { 15 | flag = false 16 | } 17 | 18 | var waitGroup sync.WaitGroup 19 | 20 | fmt.Printf("%#v\n", waitGroup) 21 | for i := 0; i < count; i++ { 22 | waitGroup.Add(1) 23 | go func(x int) { 24 | defer waitGroup.Done() 25 | fmt.Printf("%d ", x) 26 | }(i) 27 | } 28 | 29 | // More Add() calls 30 | if flag { 31 | waitGroup.Add(1) 32 | } else { 33 | // More Done() calls 34 | waitGroup.Done() 35 | } 36 | 37 | fmt.Printf("%#v\n", waitGroup) 38 | waitGroup.Wait() 39 | fmt.Println("\nExiting...") 40 | } 41 | -------------------------------------------------------------------------------- /ch07/atomic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | type atomCounter struct { 10 | val int64 11 | } 12 | 13 | func (c *atomCounter) Value() int64 { 14 | return atomic.LoadInt64(&c.val) 15 | } 16 | 17 | func main() { 18 | X := 100 19 | Y := 4 20 | var waitGroup sync.WaitGroup 21 | counter := atomCounter{} 22 | for i := 0; i < X; i++ { 23 | waitGroup.Add(1) 24 | go func(no int) { 25 | defer waitGroup.Done() 26 | for i := 0; i < Y; i++ { 27 | atomic.AddInt64(&counter.val, 1) 28 | } 29 | }(i) 30 | } 31 | 32 | waitGroup.Wait() 33 | fmt.Println(counter.Value()) 34 | } 35 | -------------------------------------------------------------------------------- /ch07/bufChannel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | // numbers cannot store more than 5 integers 9 | numbers := make(chan int, 5) 10 | counter := 10 11 | 12 | for i := 0; i < counter; i++ { 13 | select { 14 | // This is where the processing takes place 15 | case numbers <- i * i: 16 | fmt.Println("About to process", i) 17 | default: 18 | fmt.Print("No space for ", i, " ") 19 | } 20 | } 21 | fmt.Println() 22 | 23 | for { 24 | select { 25 | case num := <-numbers: 26 | fmt.Print("*", num, " ") 27 | default: 28 | fmt.Println("Nothing left to read!") 29 | return 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ch07/chRace.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func printer(ch chan<- bool, times int) { 8 | for i := 0; i < times; i++ { 9 | ch <- true 10 | } 11 | close(ch) 12 | } 13 | 14 | func main() { 15 | // This is an unbuffered channel 16 | var ch chan bool = make(chan bool) 17 | 18 | // Write 5 values to channel with a single goroutine 19 | go printer(ch, 5) 20 | 21 | // IMPORTANT: As the channel c is closed, 22 | // the range loop is going to exit on its own. 23 | for val := range ch { 24 | fmt.Print(val, " ") 25 | } 26 | fmt.Println() 27 | 28 | for i := 0; i < 15; i++ { 29 | fmt.Print(<-ch, " ") 30 | } 31 | fmt.Println() 32 | } 33 | -------------------------------------------------------------------------------- /ch07/channelFunc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func printer(ch chan<- bool) { 9 | ch <- true 10 | } 11 | 12 | func writeToChannel(c chan<- int, x int) { 13 | fmt.Println("1", x) 14 | c <- x 15 | fmt.Println("2", x) 16 | } 17 | 18 | func f2(out <-chan int, in chan<- int) { 19 | x := <-out 20 | fmt.Println("Read (f2):", x) 21 | in <- x 22 | return 23 | } 24 | 25 | func main() { 26 | c := make(chan int) 27 | go writeToChannel(c, 10) 28 | time.Sleep(1 * time.Second) 29 | fmt.Println("Read:", <-c) 30 | time.Sleep(1 * time.Second) 31 | close(c) 32 | 33 | c1 := make(chan int, 1) 34 | c2 := make(chan int, 1) 35 | 36 | // Write to channel 37 | c1 <- 5 38 | f2(c1, c2) 39 | // Read from channel 40 | fmt.Println("Read (main):", <-c2) 41 | } 42 | -------------------------------------------------------------------------------- /ch07/channels.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func writeToChannel(c chan int, x int) { 9 | c <- x 10 | close(c) 11 | } 12 | 13 | func printer(ch chan bool) { 14 | ch <- true 15 | } 16 | 17 | func main() { 18 | c := make(chan int, 1) 19 | 20 | var waitGroup sync.WaitGroup 21 | waitGroup.Add(1) 22 | go func(c chan int) { 23 | defer waitGroup.Done() 24 | writeToChannel(c, 10) 25 | fmt.Println("Exit.") 26 | }(c) 27 | 28 | fmt.Println("Read:", <-c) 29 | _, ok := <-c 30 | if ok { 31 | fmt.Println("Channel is open!") 32 | } else { 33 | fmt.Println("Channel is closed!") 34 | } 35 | 36 | waitGroup.Wait() 37 | 38 | var ch chan bool = make(chan bool) 39 | for i := 0; i < 5; i++ { 40 | go printer(ch) 41 | } 42 | 43 | // Range on channels 44 | // IMPORTANT: As the channel c is not closed, 45 | // the range loop does not exit by its own. 46 | n := 0 47 | for i := range ch { 48 | fmt.Println(i) 49 | if i == true { 50 | n++ 51 | } 52 | if n > 2 { 53 | fmt.Println("n:", n) 54 | close(ch) 55 | break 56 | } 57 | } 58 | 59 | for i := 0; i < 5; i++ { 60 | fmt.Println(<-ch) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ch07/closeNil.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var c chan string 5 | close(c) 6 | } 7 | -------------------------------------------------------------------------------- /ch07/create.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func printme(x int) { 9 | fmt.Println("*", x) 10 | return 11 | } 12 | 13 | func main() { 14 | go func(x int) { 15 | fmt.Printf("%d ", x) 16 | }(10) 17 | 18 | go printme(15) 19 | 20 | time.Sleep(time.Second) 21 | fmt.Println("Exiting...") 22 | } 23 | -------------------------------------------------------------------------------- /ch07/defineOrder.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var wg sync.WaitGroup 10 | 11 | func A(a, b chan struct{}) { 12 | <-a 13 | fmt.Println("A()!") 14 | time.Sleep(time.Second) 15 | close(b) 16 | } 17 | 18 | func B(a, b chan struct{}) { 19 | <-a 20 | fmt.Println("B()!") 21 | time.Sleep(3 * time.Second) 22 | close(b) 23 | } 24 | 25 | func C(a, b chan struct{}) { 26 | <-a 27 | fmt.Println("C()!") 28 | close(b) 29 | } 30 | 31 | func D(a chan struct{}) { 32 | <-a 33 | fmt.Println("D()!") 34 | wg.Done() 35 | } 36 | 37 | func main() { 38 | x := make(chan struct{}) 39 | y := make(chan struct{}) 40 | z := make(chan struct{}) 41 | w := make(chan struct{}) 42 | 43 | wg.Add(1) 44 | go func() { 45 | D(w) 46 | }() 47 | 48 | wg.Add(1) 49 | go func() { 50 | D(w) 51 | }() 52 | 53 | go A(x, y) 54 | 55 | wg.Add(1) 56 | go func() { 57 | D(w) 58 | }() 59 | 60 | go C(z, w) 61 | go B(y, z) 62 | 63 | wg.Add(1) 64 | go func() { 65 | D(w) 66 | }() 67 | 68 | // This triggers the process 69 | close(x) 70 | wg.Wait() 71 | } 72 | -------------------------------------------------------------------------------- /ch07/forgetMutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var m sync.Mutex 9 | var w sync.WaitGroup 10 | 11 | func function() { 12 | m.Lock() 13 | fmt.Println("Locked!") 14 | } 15 | 16 | func main() { 17 | w.Add(1) 18 | go func() { 19 | defer w.Done() 20 | function() 21 | }() 22 | 23 | w.Add(1) 24 | go func() { 25 | defer w.Done() 26 | function() 27 | }() 28 | 29 | w.Wait() 30 | } 31 | -------------------------------------------------------------------------------- /ch07/goClosure.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | for i := 0; i <= 20; i++ { 10 | go func() { 11 | fmt.Print(i, " ") 12 | }() 13 | } 14 | time.Sleep(time.Second) 15 | fmt.Println() 16 | } 17 | -------------------------------------------------------------------------------- /ch07/goClosureCorrect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | for i := 0; i <= 20; i++ { 10 | i := i 11 | go func() { 12 | fmt.Print(i, " ") 13 | }() 14 | } 15 | time.Sleep(time.Second) 16 | fmt.Println() 17 | 18 | for i := 0; i <= 20; i++ { 19 | go func(x int) { 20 | fmt.Print(x, " ") 21 | }(i) 22 | } 23 | time.Sleep(time.Second) 24 | fmt.Println() 25 | } 26 | -------------------------------------------------------------------------------- /ch07/keyVal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | type aKey string 9 | 10 | func searchKey(ctx context.Context, k aKey) { 11 | v := ctx.Value(k) 12 | if v != nil { 13 | fmt.Println("found value:", v) 14 | return 15 | } else { 16 | fmt.Println("key not found:", k) 17 | } 18 | } 19 | 20 | func main() { 21 | myKey := aKey("mySecretValue") 22 | ctx := context.WithValue(context.Background(), myKey, "mySecret") 23 | searchKey(ctx, myKey) 24 | 25 | searchKey(ctx, aKey("notThere")) 26 | emptyCtx := context.TODO() 27 | searchKey(emptyCtx, aKey("notThere")) 28 | } 29 | -------------------------------------------------------------------------------- /ch07/maxprocs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | func main() { 9 | fmt.Print("You are using ", runtime.Compiler, " ") 10 | fmt.Println("on a", runtime.GOARCH, "machine") 11 | fmt.Println("Using Go version", runtime.Version()) 12 | fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0)) 13 | } 14 | -------------------------------------------------------------------------------- /ch07/monitor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "strconv" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | var readValue = make(chan int) 13 | var writeValue = make(chan int) 14 | 15 | func set(newValue int) { 16 | writeValue <- newValue 17 | } 18 | 19 | func read() int { 20 | return <-readValue 21 | } 22 | 23 | func monitor() { 24 | var value int 25 | for { 26 | select { 27 | case newValue := <-writeValue: 28 | value = newValue 29 | fmt.Printf("%d ", value) 30 | case readValue <- value: 31 | } 32 | } 33 | } 34 | 35 | func main() { 36 | if len(os.Args) != 2 { 37 | fmt.Println("Please give an integer!") 38 | return 39 | } 40 | n, err := strconv.Atoi(os.Args[1]) 41 | if err != nil { 42 | fmt.Println(err) 43 | return 44 | } 45 | 46 | fmt.Printf("Going to create %d random numbers.\n", n) 47 | rand.Seed(time.Now().Unix()) 48 | go monitor() 49 | var wg sync.WaitGroup 50 | 51 | for r := 0; r < n; r++ { 52 | wg.Add(1) 53 | go func() { 54 | defer wg.Done() 55 | set(rand.Intn(10 * n)) 56 | }() 57 | } 58 | wg.Wait() 59 | fmt.Printf("\nLast value: %d\n", read()) 60 | } 61 | -------------------------------------------------------------------------------- /ch07/multiple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | count := 10 12 | if len(os.Args) == 1 { 13 | fmt.Println("Using default value.") 14 | } else { 15 | temp, err := strconv.Atoi(os.Args[1]) 16 | if err != nil { 17 | fmt.Println("Using default value.") 18 | } else { 19 | count = temp 20 | } 21 | } 22 | 23 | fmt.Printf("Going to create %d goroutines.\n", count) 24 | for i := 0; i < count; i++ { 25 | go func(x int) { 26 | fmt.Printf("%d ", x) 27 | }(i) 28 | } 29 | time.Sleep(time.Second) 30 | fmt.Println("\nExiting...") 31 | } 32 | -------------------------------------------------------------------------------- /ch07/mutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | var m sync.Mutex 12 | var v1 int 13 | 14 | func change(i int) { 15 | m.Lock() 16 | time.Sleep(time.Second) 17 | v1 = v1 + 1 18 | if v1 == 10 { 19 | v1 = 0 20 | fmt.Print("* ") 21 | } 22 | m.Unlock() 23 | } 24 | 25 | func read() int { 26 | m.Lock() 27 | a := v1 28 | m.Unlock() 29 | return a 30 | } 31 | 32 | func main() { 33 | if len(os.Args) != 2 { 34 | fmt.Println("Please give me an integer!") 35 | return 36 | } 37 | 38 | numGR, err := strconv.Atoi(os.Args[1]) 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | var wg sync.WaitGroup 44 | 45 | fmt.Printf("%d ", read()) 46 | for i := 0; i < numGR; i++ { 47 | wg.Add(1) 48 | go func(i int) { 49 | defer wg.Done() 50 | change(i) 51 | fmt.Printf("-> %d", read()) 52 | }(i) 53 | } 54 | 55 | wg.Wait() 56 | fmt.Printf("-> %d\n", read()) 57 | } 58 | -------------------------------------------------------------------------------- /ch07/nilChannel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var wg sync.WaitGroup 11 | 12 | func add(c chan int) { 13 | sum := 0 14 | t := time.NewTimer(time.Second) 15 | 16 | for { 17 | select { 18 | case input := <-c: 19 | sum = sum + input 20 | case <-t.C: 21 | c = nil 22 | fmt.Println(sum) 23 | wg.Done() 24 | } 25 | } 26 | } 27 | 28 | func send(c chan int) { 29 | for { 30 | c <- rand.Intn(10) 31 | } 32 | } 33 | 34 | func main() { 35 | c := make(chan int) 36 | rand.Seed(time.Now().Unix()) 37 | 38 | wg.Add(1) 39 | go add(c) 40 | go send(c) 41 | wg.Wait() 42 | } 43 | -------------------------------------------------------------------------------- /ch07/randomFiles.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "strconv" 8 | "sync" 9 | ) 10 | 11 | func random(min, max int) int { 12 | return rand.Intn(max-min) + min 13 | } 14 | 15 | func createFile(file string) { 16 | // Does the file already exist? 17 | _, err := os.Stat(file) 18 | // File already exist - do not override 19 | if err == nil { 20 | fmt.Printf("%s already exists!\n", file) 21 | return 22 | } 23 | 24 | // Can the file be created? 25 | f, err := os.Create(file) 26 | if err != nil { 27 | fmt.Println(err) 28 | return 29 | } 30 | 31 | // The number of lines is randomly chosen 32 | lines := random(10, 30) 33 | // Populate file with random data 34 | for i := 0; i < lines; i++ { 35 | data := random(0, 20) 36 | fmt.Fprintf(f, "%d\n", data) 37 | } 38 | fmt.Printf("%s created!\n", file) 39 | } 40 | 41 | func main() { 42 | arguments := os.Args 43 | if len(arguments) != 5 { 44 | fmt.Println("Usage: randomFiles firstInt lastInt filename directory") 45 | return 46 | } 47 | 48 | start, err := strconv.Atoi(arguments[1]) 49 | if err != nil { 50 | fmt.Println(err) 51 | return 52 | } 53 | 54 | end, err := strconv.Atoi(arguments[2]) 55 | if err != nil { 56 | fmt.Println(err) 57 | return 58 | } 59 | 60 | // For the for loop to work properly 61 | if end < start { 62 | fmt.Println(end, "<", start) 63 | return 64 | } 65 | 66 | filename := arguments[3] 67 | path := arguments[4] 68 | // Does the destination directory exist? 69 | _, err = os.Open(path) 70 | if err != nil { 71 | fmt.Println(path, "does not exist!") 72 | return 73 | } 74 | 75 | var waitGroup sync.WaitGroup 76 | for i := start; i <= end; i++ { 77 | waitGroup.Add(1) 78 | filepath := fmt.Sprintf("%s/%s%d", path, filename, i) 79 | go func(f string) { 80 | defer waitGroup.Done() 81 | createFile(f) 82 | }(filepath) 83 | } 84 | waitGroup.Wait() 85 | } 86 | -------------------------------------------------------------------------------- /ch07/readCloseCh.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | willClose := make(chan complex64, 10) 9 | 10 | // Write some data to the channel 11 | willClose <- -1 12 | willClose <- 1i 13 | 14 | // Read data and empty channel 15 | <-willClose 16 | <-willClose 17 | close(willClose) 18 | 19 | // Read again – this is a closed channel 20 | read := <-willClose 21 | fmt.Println(read) 22 | } 23 | -------------------------------------------------------------------------------- /ch07/rwMutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var Password *secret 10 | var wg sync.WaitGroup 11 | 12 | type secret struct { 13 | RWM sync.RWMutex 14 | password string 15 | } 16 | 17 | func Change(pass string) { 18 | fmt.Println("Change() function") 19 | Password.RWM.Lock() 20 | fmt.Println("Change() Locked") 21 | time.Sleep(4 * time.Second) 22 | Password.password = pass 23 | Password.RWM.Unlock() 24 | fmt.Println("Change() UnLocked") 25 | } 26 | 27 | func show() { 28 | defer wg.Done() 29 | Password.RWM.RLock() 30 | fmt.Println("Show function locked!") 31 | time.Sleep(2 * time.Second) 32 | fmt.Println("Pass value:", Password.password) 33 | defer Password.RWM.RUnlock() 34 | } 35 | 36 | func main() { 37 | Password = &secret{password: "myPass"} 38 | for i := 0; i < 3; i++ { 39 | wg.Add(1) 40 | go show() 41 | } 42 | 43 | wg.Add(1) 44 | go func() { 45 | defer wg.Done() 46 | Change("123456") 47 | }() 48 | 49 | wg.Add(1) 50 | go func() { 51 | defer wg.Done() 52 | Change("54321") 53 | }() 54 | 55 | wg.Wait() 56 | 57 | // Direct access to Password.password 58 | fmt.Println("Current password value:", Password.password) 59 | } 60 | -------------------------------------------------------------------------------- /ch07/select.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "strconv" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | func gen(min, max int, createNumber chan int, end chan bool) { 13 | time.Sleep(time.Second) 14 | for { 15 | select { 16 | case createNumber <- rand.Intn(max-min) + min: 17 | case <-end: 18 | fmt.Println("Ended!") 19 | // return 20 | case <-time.After(4 * time.Second): 21 | fmt.Println("time.After()!") 22 | return 23 | } 24 | } 25 | } 26 | 27 | func main() { 28 | rand.Seed(time.Now().Unix()) 29 | createNumber := make(chan int) 30 | end := make(chan bool) 31 | 32 | if len(os.Args) != 2 { 33 | fmt.Println("Please give me an integer!") 34 | return 35 | } 36 | 37 | n, _ := strconv.Atoi(os.Args[1]) 38 | fmt.Printf("Going to create %d random numbers.\n", n) 39 | 40 | var wg sync.WaitGroup 41 | wg.Add(1) 42 | go func() { 43 | gen(0, 2*n, createNumber, end) 44 | wg.Done() 45 | }() 46 | 47 | for i := 0; i < n; i++ { 48 | fmt.Printf("%d ", <-createNumber) 49 | } 50 | 51 | end <- true 52 | wg.Wait() 53 | fmt.Println("Exiting...") 54 | } 55 | -------------------------------------------------------------------------------- /ch07/semaphore.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "time" 9 | 10 | "golang.org/x/sync/semaphore" 11 | ) 12 | 13 | // Maximum number of goroutines 14 | var Workers = 4 15 | var sem = semaphore.NewWeighted(int64(Workers)) 16 | 17 | func worker(n int) int { 18 | square := n * n 19 | time.Sleep(time.Second) 20 | return square 21 | } 22 | 23 | func main() { 24 | if len(os.Args) != 2 { 25 | fmt.Println("Need #jobs!") 26 | return 27 | } 28 | 29 | nJobs, err := strconv.Atoi(os.Args[1]) 30 | if err != nil { 31 | fmt.Println(err) 32 | return 33 | } 34 | 35 | // Where to store the results 36 | var results = make([]int, nJobs) 37 | 38 | // Needed by Acquire() 39 | ctx := context.TODO() 40 | 41 | for i := range results { 42 | err = sem.Acquire(ctx, 1) 43 | if err != nil { 44 | fmt.Println("Cannot acquire semaphore:", err) 45 | break 46 | } 47 | 48 | go func(i int) { 49 | defer sem.Release(1) 50 | temp := worker(i) 51 | // No race conditions here - each goroutine writes 52 | // to a different slice element 53 | results[i] = temp 54 | }(i) 55 | } 56 | 57 | // Acquire all of the tokens 58 | // This is similar to Wait() 59 | // It blocks until all workers have finished 60 | err = sem.Acquire(ctx, int64(Workers)) 61 | if err != nil { 62 | fmt.Println(err) 63 | } 64 | 65 | for k, v := range results { 66 | fmt.Println(k, "->", v) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ch07/shareMem.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var Password = secret{password: "myPassword"} 11 | 12 | type secret struct { 13 | RWM sync.RWMutex 14 | password string 15 | } 16 | 17 | func Change(c *secret, pass string) { 18 | c.RWM.Lock() 19 | fmt.Println("LChange") 20 | time.Sleep(10 * time.Second) 21 | c.password = pass 22 | c.RWM.Unlock() 23 | } 24 | 25 | func show(c *secret) string { 26 | c.RWM.RLock() 27 | fmt.Print("show") 28 | time.Sleep(3 * time.Second) 29 | defer c.RWM.RUnlock() 30 | return c.password 31 | } 32 | 33 | func showWithLock(c *secret) string { 34 | c.RWM.Lock() 35 | fmt.Println("showWithLock") 36 | time.Sleep(3 * time.Second) 37 | defer c.RWM.Unlock() 38 | return c.password 39 | } 40 | 41 | func main() { 42 | var showFunction = func(c *secret) string { return "" } 43 | if len(os.Args) != 2 { 44 | fmt.Println("Using sync.RWMutex!") 45 | showFunction = show 46 | } else { 47 | fmt.Println("Using sync.Mutex!") 48 | showFunction = showWithLock 49 | } 50 | 51 | var waitGroup sync.WaitGroup 52 | 53 | fmt.Println("Pass:", showFunction(&Password)) 54 | for i := 0; i < 15; i++ { 55 | waitGroup.Add(1) 56 | go func() { 57 | defer waitGroup.Done() 58 | fmt.Println("Go Pass:", showFunction(&Password)) 59 | }() 60 | } 61 | 62 | go func() { 63 | waitGroup.Add(1) 64 | defer waitGroup.Done() 65 | Change(&Password, "123456") 66 | }() 67 | 68 | waitGroup.Wait() 69 | fmt.Println("Pass:", showFunction(&Password)) 70 | } 71 | -------------------------------------------------------------------------------- /ch07/timeOut1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | c1 := make(chan string) 10 | go func() { 11 | time.Sleep(3 * time.Second) 12 | c1 <- "c1 OK" 13 | }() 14 | 15 | select { 16 | case res := <-c1: 17 | fmt.Println(res) 18 | case <-time.After(time.Second): 19 | fmt.Println("timeout c1") 20 | } 21 | 22 | c2 := make(chan string) 23 | go func() { 24 | time.Sleep(3 * time.Second) 25 | c2 <- "c2 OK" 26 | }() 27 | 28 | select { 29 | case res := <-c2: 30 | fmt.Println(res) 31 | case <-time.After(4 * time.Second): 32 | fmt.Println("timeout c2") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ch07/timeOut2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | var result = make(chan bool) 11 | 12 | func timeout(t time.Duration) { 13 | temp := make(chan int) 14 | go func() { 15 | time.Sleep(5 * time.Second) 16 | defer close(temp) 17 | }() 18 | 19 | select { 20 | case <-temp: 21 | result <- false 22 | case <-time.After(t): 23 | result <- true 24 | } 25 | } 26 | 27 | func main() { 28 | arguments := os.Args 29 | if len(arguments) != 2 { 30 | fmt.Println("Please provide a time duration in milliseconds!") 31 | return 32 | } 33 | 34 | t, err := strconv.Atoi(arguments[1]) 35 | if err != nil { 36 | fmt.Println(err) 37 | return 38 | } 39 | 40 | duration := time.Duration(int32(t)) * time.Millisecond 41 | fmt.Printf("Timeout period is %s\n", duration) 42 | 43 | go timeout(duration) 44 | 45 | val := <-result 46 | if val { 47 | fmt.Println("Time out!") 48 | } else { 49 | fmt.Println("OK") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ch07/useContext.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | // The f1 function creates and executes a goroutine 12 | // The time.Sleep() call simulates the time it would take a real goroutine 13 | // to do its job - in this case it is 4 seconds. If the c1 context calls 14 | // the Done() function in less than 4 seconds, the goroutine will not have 15 | // enough time to finish. 16 | func f1(t int) { 17 | c1 := context.Background() 18 | // WithCancel returns a copy of parent context with a new Done channel 19 | c1, cancel := context.WithCancel(c1) 20 | defer cancel() 21 | 22 | go func() { 23 | time.Sleep(4 * time.Second) 24 | cancel() 25 | }() 26 | 27 | select { 28 | case <-c1.Done(): 29 | fmt.Println("f1() Done:", c1.Err()) 30 | return 31 | case r := <-time.After(time.Duration(t) * time.Second): 32 | fmt.Println("f1():", r) 33 | } 34 | return 35 | } 36 | 37 | func f2(t int) { 38 | c2 := context.Background() 39 | c2, cancel := context.WithTimeout(c2, time.Duration(t)*time.Second) 40 | defer cancel() 41 | 42 | go func() { 43 | time.Sleep(4 * time.Second) 44 | cancel() 45 | }() 46 | 47 | select { 48 | case <-c2.Done(): 49 | fmt.Println("f2() Done:", c2.Err()) 50 | return 51 | case r := <-time.After(time.Duration(t) * time.Second): 52 | fmt.Println("f2():", r) 53 | } 54 | return 55 | } 56 | 57 | func f3(t int) { 58 | c3 := context.Background() 59 | deadline := time.Now().Add(time.Duration(2*t) * time.Second) 60 | c3, cancel := context.WithDeadline(c3, deadline) 61 | defer cancel() 62 | 63 | go func() { 64 | time.Sleep(4 * time.Second) 65 | cancel() 66 | }() 67 | 68 | select { 69 | case <-c3.Done(): 70 | fmt.Println("f3() Done:", c3.Err()) 71 | return 72 | case r := <-time.After(time.Duration(t) * time.Second): 73 | fmt.Println("f3():", r) 74 | } 75 | return 76 | } 77 | 78 | func main() { 79 | if len(os.Args) != 2 { 80 | fmt.Println("Need a delay!") 81 | return 82 | } 83 | 84 | delay, err := strconv.Atoi(os.Args[1]) 85 | if err != nil { 86 | fmt.Println(err) 87 | return 88 | } 89 | fmt.Println("Delay:", delay) 90 | 91 | f1(delay) 92 | f2(delay) 93 | f3(delay) 94 | } 95 | -------------------------------------------------------------------------------- /ch07/varGoroutines.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "sync" 8 | ) 9 | 10 | func main() { 11 | count := 10 12 | arguments := os.Args 13 | if len(arguments) == 2 { 14 | t, err := strconv.Atoi(arguments[1]) 15 | if err == nil { 16 | count = t 17 | } 18 | } 19 | 20 | fmt.Printf("Going to create %d goroutines.\n", count) 21 | 22 | var waitGroup sync.WaitGroup 23 | fmt.Printf("%#v\n", waitGroup) 24 | for i := 0; i < count; i++ { 25 | waitGroup.Add(1) 26 | go func(x int) { 27 | defer waitGroup.Done() 28 | fmt.Printf("%d ", x) 29 | }(i) 30 | } 31 | 32 | fmt.Printf("%#v\n", waitGroup) 33 | waitGroup.Wait() 34 | fmt.Println("\nExiting...") 35 | } 36 | -------------------------------------------------------------------------------- /ch07/wPools.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | "strconv" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type Client struct { 13 | id int 14 | integer int 15 | } 16 | 17 | type Result struct { 18 | job Client 19 | square int 20 | } 21 | 22 | var size = runtime.GOMAXPROCS(0) 23 | var clients = make(chan Client, size) 24 | var data = make(chan Result, size) 25 | 26 | func worker(wg *sync.WaitGroup) { 27 | for c := range clients { 28 | square := c.integer * c.integer 29 | output := Result{c, square} 30 | data <- output 31 | time.Sleep(time.Second) 32 | } 33 | wg.Done() 34 | } 35 | 36 | func create(n int) { 37 | for i := 0; i < n; i++ { 38 | c := Client{i, i} 39 | clients <- c 40 | } 41 | close(clients) 42 | } 43 | 44 | func main() { 45 | if len(os.Args) != 3 { 46 | fmt.Println("Need #jobs and #workers!") 47 | return 48 | } 49 | 50 | nJobs, err := strconv.Atoi(os.Args[1]) 51 | if err != nil { 52 | fmt.Println(err) 53 | return 54 | } 55 | 56 | nWorkers, err := strconv.Atoi(os.Args[2]) 57 | if err != nil { 58 | fmt.Println(err) 59 | return 60 | } 61 | 62 | go create(nJobs) 63 | 64 | finished := make(chan interface{}) 65 | go func() { 66 | for d := range data { 67 | fmt.Printf("Client ID: %d\tint: ", d.job.id) 68 | fmt.Printf("%d\tsquare: %d\n", d.job.integer, d.square) 69 | } 70 | finished <- true 71 | }() 72 | 73 | var wg sync.WaitGroup 74 | for i := 0; i < nWorkers; i++ { 75 | wg.Add(1) 76 | go worker(&wg) 77 | } 78 | wg.Wait() 79 | close(data) 80 | 81 | fmt.Printf("Finished: %v\n", <-finished) 82 | } 83 | -------------------------------------------------------------------------------- /ch08/Dockerfile: -------------------------------------------------------------------------------- 1 | # docker build -f Dockerfile -t goapp . 2 | # docker run -it -p 1234:1234 goapp 3 | # WITH Go Modules 4 | 5 | FROM golang:alpine AS builder 6 | 7 | # Install git. 8 | # Git is required for fetching the dependencies. 9 | RUN apk update && apk add --no-cache git 10 | 11 | RUN mkdir $GOPATH/src/server 12 | ADD ./prometheus.go $GOPATH/src/server 13 | WORKDIR $GOPATH/src/server 14 | RUN go mod init 15 | RUN go mod tidy 16 | RUN go mod download 17 | RUN mkdir /pro 18 | RUN go build -o /pro/server prometheus.go 19 | 20 | FROM alpine:latest 21 | 22 | RUN mkdir /pro 23 | COPY --from=builder /pro/server /pro/server 24 | EXPOSE 1234 25 | WORKDIR /pro 26 | CMD ["/pro/server"] 27 | -------------------------------------------------------------------------------- /ch08/dFilev1: -------------------------------------------------------------------------------- 1 | # docker build -f dFilev1 -t go-app115 . 2 | # docker run -it -p 2345:2345 go-app115 3 | # WITHOUT using Go Modules 4 | 5 | FROM golang:1.15.10-alpine3.13 AS builder 6 | 7 | # Install git. 8 | # Git is required for fetching the dependencies. 9 | RUN apk update && apk add --no-cache git 10 | 11 | RUN mkdir /pro 12 | ADD ./samplePro.go /pro/ 13 | WORKDIR /pro 14 | RUN go get -d -v ./... 15 | RUN go build -o server ./samplePro.go 16 | 17 | FROM alpine:latest 18 | 19 | RUN mkdir /pro 20 | COPY --from=builder /pro/server /pro/server 21 | EXPOSE 2345 22 | WORKDIR /pro 23 | CMD ["/pro/server"] 24 | -------------------------------------------------------------------------------- /ch08/dFilev2: -------------------------------------------------------------------------------- 1 | # docker build -f dFilev2 -t go-app116 . 2 | # docker run -it -p 1234:1234 go-app116 3 | # WITH Go Modules 4 | 5 | FROM golang:alpine AS builder 6 | 7 | # Install git. 8 | # Git is required for fetching the dependencies. 9 | RUN apk update && apk add --no-cache git 10 | 11 | RUN mkdir $GOPATH/src/server 12 | ADD ./samplePro.go $GOPATH/src/server 13 | WORKDIR $GOPATH/src/server 14 | RUN go mod init 15 | RUN go mod tidy 16 | RUN go mod download 17 | RUN mkdir /pro 18 | RUN go build -o /pro/server samplePro.go 19 | 20 | FROM alpine:latest 21 | 22 | RUN mkdir /pro 23 | COPY --from=builder /pro/server /pro/server 24 | EXPOSE 1234 25 | WORKDIR /pro 26 | CMD ["/pro/server"] 27 | -------------------------------------------------------------------------------- /ch08/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | goapp: 5 | image: goapp 6 | container_name: goapp 7 | restart: always 8 | ports: 9 | - 1234:1234 10 | networks: 11 | - monitoring 12 | 13 | prometheus: 14 | image: prom/prometheus:latest 15 | container_name: prometheus 16 | restart: always 17 | user: "0" 18 | volumes: 19 | - ./prometheus/:/etc/prometheus/ 20 | - ./prometheus_data/:/prometheus/ 21 | command: 22 | - '--config.file=/etc/prometheus/prometheus.yml' 23 | - '--storage.tsdb.path=/prometheus' 24 | - '--web.console.libraries=/etc/prometheus/console_libraries' 25 | - '--web.console.templates=/etc/prometheus/consoles' 26 | - '--storage.tsdb.retention.time=200h' 27 | - '--web.enable-lifecycle' 28 | ports: 29 | - 9090:9090 30 | networks: 31 | - monitoring 32 | 33 | grafana: 34 | image: grafana/grafana 35 | container_name: grafana 36 | depends_on: 37 | - prometheus 38 | restart: always 39 | user: "0" 40 | ports: 41 | - 3000:3000 42 | environment: 43 | - GF_SECURITY_ADMIN_PASSWORD=helloThere 44 | - GF_USERS_ALLOW_SIGN_UP=false 45 | - GF_PANELS_DISABLE_SANITIZE_HTML=true 46 | - GF_SECURITY_ALLOW_EMBEDDING=true 47 | networks: 48 | - monitoring 49 | volumes: 50 | - ./grafana_data/:/var/lib/grafana/ 51 | 52 | volumes: 53 | grafana_data: {} 54 | prometheus_data: {} 55 | 56 | networks: 57 | monitoring: 58 | driver: bridge 59 | -------------------------------------------------------------------------------- /ch08/fileServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | var PORT = ":8765" 10 | 11 | func defaultHandler(w http.ResponseWriter, r *http.Request) { 12 | log.Println("Serving:", r.URL.Path, "from", r.Host) 13 | w.WriteHeader(http.StatusOK) 14 | Body := "Thanks for visiting!\n" 15 | fmt.Fprintf(w, "%s", Body) 16 | } 17 | 18 | func main() { 19 | mux := http.NewServeMux() 20 | mux.HandleFunc("/", defaultHandler) 21 | 22 | fileServer := http.FileServer(http.Dir("/tmp/")) 23 | mux.Handle("/static/", http.StripPrefix("/static", fileServer)) 24 | 25 | fmt.Println("Starting server on:", PORT) 26 | err := http.ListenAndServe(PORT, mux) 27 | fmt.Println(err) 28 | } 29 | -------------------------------------------------------------------------------- /ch08/getEntries.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | "net/http" 7 | "os" 8 | "time" 9 | ) 10 | 11 | var PORT = ":8765" 12 | var DATAFILE = "/tmp/data.csv" 13 | 14 | type Entry struct { 15 | Name string 16 | Surname string 17 | Tel string 18 | LastAccess string 19 | } 20 | 21 | type PhoneBook []Entry 22 | 23 | var data = PhoneBook{} 24 | 25 | func readCSVFile(filepath string) error { 26 | _, err := os.Stat(filepath) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | f, err := os.Open(filepath) 32 | if err != nil { 33 | return err 34 | } 35 | defer f.Close() 36 | 37 | lines, err := csv.NewReader(f).ReadAll() 38 | if err != nil { 39 | return err 40 | } 41 | 42 | for _, line := range lines { 43 | temp := Entry{ 44 | Name: line[0], 45 | Surname: line[1], 46 | Tel: line[2], 47 | LastAccess: line[3], 48 | } 49 | // Storing to global variable 50 | data = append(data, temp) 51 | } 52 | 53 | return nil 54 | } 55 | 56 | func saveCSVFile(filepath string) error { 57 | csvfile, err := os.Create(filepath) 58 | if err != nil { 59 | return err 60 | } 61 | defer csvfile.Close() 62 | 63 | csvwriter := csv.NewWriter(csvfile) 64 | for _, row := range data { 65 | temp := []string{row.Name, row.Surname, row.Tel, row.LastAccess} 66 | _ = csvwriter.Write(temp) 67 | } 68 | csvwriter.Flush() 69 | return nil 70 | } 71 | 72 | func getFileHandler(w http.ResponseWriter, r *http.Request) { 73 | var tempFileName string 74 | 75 | // Create temporary file name 76 | f, err := os.CreateTemp("", "data*.txt") 77 | tempFileName = f.Name() 78 | 79 | // Remove the file 80 | defer os.Remove(tempFileName) 81 | 82 | // Save data to it 83 | err = saveCSVFile(tempFileName) 84 | 85 | if err != nil { 86 | fmt.Println(err) 87 | w.WriteHeader(http.StatusNotFound) 88 | fmt.Fprintln(w, "Cannot create: "+tempFileName) 89 | return 90 | } 91 | 92 | fmt.Println("Serving ", tempFileName) 93 | // Serve it to the client 94 | http.ServeFile(w, r, tempFileName) 95 | 96 | // 30 seconds to get the file 97 | time.Sleep(30 * time.Second) 98 | } 99 | 100 | func defaultHandler(w http.ResponseWriter, r *http.Request) { 101 | fmt.Println("Serving:", r.URL.Path, "from", r.Host) 102 | w.WriteHeader(http.StatusOK) 103 | Body := "Thanks for visiting!\n" 104 | fmt.Fprintf(w, "%s", Body) 105 | } 106 | 107 | func main() { 108 | err := readCSVFile(DATAFILE) 109 | if err != nil { 110 | fmt.Println(err) 111 | return 112 | } 113 | 114 | mux := http.NewServeMux() 115 | mux.HandleFunc("/", defaultHandler) 116 | mux.HandleFunc("/getContents/", getFileHandler) 117 | 118 | fmt.Println("Starting server on:", PORT) 119 | err = http.ListenAndServe(PORT, mux) 120 | fmt.Println(err) 121 | } 122 | -------------------------------------------------------------------------------- /ch08/metrics.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime/metrics" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | const nGo = "/sched/goroutines:goroutines" 12 | 13 | // A slice for getting metric samples 14 | getMetric := make([]metrics.Sample, 1) 15 | getMetric[0].Name = nGo 16 | 17 | var wg sync.WaitGroup 18 | for i := 0; i < 3; i++ { 19 | wg.Add(1) 20 | go func() { 21 | defer wg.Done() 22 | time.Sleep(4 * time.Second) 23 | }() 24 | 25 | // Get actual data 26 | metrics.Read(getMetric) 27 | if getMetric[0].Value.Kind() == metrics.KindBad { 28 | fmt.Printf("metric %q no longer supported\n", nGo) 29 | } 30 | 31 | mVal := getMetric[0].Value.Uint64() 32 | fmt.Printf("Number of goroutines: %d\n", mVal) 33 | } 34 | 35 | wg.Wait() 36 | 37 | metrics.Read(getMetric) 38 | mVal := getMetric[0].Value.Uint64() 39 | fmt.Printf("Before exiting: %d\n", mVal) 40 | } 41 | -------------------------------------------------------------------------------- /ch08/prometheus.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "net/http" 7 | "runtime" 8 | "runtime/metrics" 9 | "time" 10 | 11 | "github.com/prometheus/client_golang/prometheus" 12 | "github.com/prometheus/client_golang/prometheus/promhttp" 13 | ) 14 | 15 | // PORT is the TCP port number the server will listen to 16 | var PORT = ":1234" 17 | 18 | var n_goroutines = prometheus.NewGauge( 19 | prometheus.GaugeOpts{ 20 | Namespace: "packt", 21 | Name: "n_goroutines", 22 | Help: "Number of goroutines"}) 23 | 24 | var n_memory = prometheus.NewGauge( 25 | prometheus.GaugeOpts{ 26 | Namespace: "packt", 27 | Name: "n_memory", 28 | Help: "Memory usage"}) 29 | 30 | func main() { 31 | rand.Seed(time.Now().Unix()) 32 | prometheus.MustRegister(n_goroutines) 33 | prometheus.MustRegister(n_memory) 34 | 35 | const nGo = "/sched/goroutines:goroutines" 36 | const nMem = "/memory/classes/heap/free:bytes" 37 | getMetric := make([]metrics.Sample, 2) 38 | getMetric[0].Name = nGo 39 | getMetric[1].Name = nMem 40 | 41 | http.Handle("/metrics", promhttp.Handler()) 42 | 43 | go func() { 44 | for { 45 | for i := 1; i < 4; i++ { 46 | go func() { 47 | _ = make([]int, 1000000) 48 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 49 | }() 50 | } 51 | 52 | runtime.GC() 53 | metrics.Read(getMetric) 54 | goVal := getMetric[0].Value.Uint64() 55 | memVal := getMetric[1].Value.Uint64() 56 | time.Sleep(time.Duration(rand.Intn(15)) * time.Second) 57 | 58 | n_goroutines.Set(float64(goVal)) 59 | n_memory.Set(float64(memVal)) 60 | } 61 | }() 62 | 63 | log.Println("Listening to port", PORT) 64 | log.Println(http.ListenAndServe(PORT, nil)) 65 | } 66 | -------------------------------------------------------------------------------- /ch08/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | # prometheus.yml 2 | scrape_configs: 3 | - job_name: GoServer 4 | scrape_interval: 5s 5 | static_configs: 6 | - targets: ['goapp:1234'] 7 | 8 | -------------------------------------------------------------------------------- /ch08/samplePro.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "math/rand" 8 | "time" 9 | 10 | "github.com/prometheus/client_golang/prometheus" 11 | "github.com/prometheus/client_golang/prometheus/promhttp" 12 | ) 13 | 14 | var PORT = ":1234" 15 | 16 | var counter = prometheus.NewCounter( 17 | prometheus.CounterOpts{ 18 | Namespace: "mtsouk", 19 | Name: "my_counter", 20 | Help: "This is my counter", 21 | }) 22 | 23 | var gauge = prometheus.NewGauge( 24 | prometheus.GaugeOpts{ 25 | Namespace: "mtsouk", 26 | Name: "my_gauge", 27 | Help: "This is my gauge", 28 | }) 29 | 30 | var histogram = prometheus.NewHistogram( 31 | prometheus.HistogramOpts{ 32 | Namespace: "mtsouk", 33 | Name: "my_histogram", 34 | Help: "This is my histogram", 35 | }) 36 | 37 | var summary = prometheus.NewSummary( 38 | prometheus.SummaryOpts{ 39 | Namespace: "mtsouk", 40 | Name: "my_summary", 41 | Help: "This is my summary", 42 | }) 43 | 44 | func main() { 45 | rand.Seed(time.Now().Unix()) 46 | 47 | prometheus.MustRegister(counter) 48 | prometheus.MustRegister(gauge) 49 | prometheus.MustRegister(histogram) 50 | prometheus.MustRegister(summary) 51 | 52 | go func() { 53 | for { 54 | counter.Add(rand.Float64() * 5) 55 | gauge.Add(rand.Float64()*15 - 5) 56 | histogram.Observe(rand.Float64() * 10) 57 | summary.Observe(rand.Float64() * 10) 58 | time.Sleep(2 * time.Second) 59 | } 60 | }() 61 | 62 | http.Handle("/metrics", promhttp.Handler()) 63 | fmt.Println("Listening to port", PORT) 64 | fmt.Println(http.ListenAndServe(PORT, nil)) 65 | } 66 | -------------------------------------------------------------------------------- /ch08/simpleClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) != 2 { 13 | fmt.Printf("Usage: %s URL\n", filepath.Base(os.Args[0])) 14 | return 15 | } 16 | 17 | URL := os.Args[1] 18 | data, err := http.Get(URL) 19 | 20 | if err != nil { 21 | fmt.Println(err) 22 | return 23 | } 24 | 25 | _, err = io.Copy(os.Stdout, data.Body) 26 | if err != nil { 27 | fmt.Println(err) 28 | return 29 | } 30 | data.Body.Close() 31 | } 32 | -------------------------------------------------------------------------------- /ch08/timeoutClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | var myUrl string 15 | var delay int = 5 16 | var wg sync.WaitGroup 17 | 18 | type myData struct { 19 | r *http.Response 20 | err error 21 | } 22 | 23 | // In packages that use contexts, convention is to pass them as 24 | // the first argument to a function. 25 | func connect(c context.Context) error { 26 | defer wg.Done() 27 | data := make(chan myData, 1) 28 | tr := &http.Transport{} 29 | httpClient := &http.Client{Transport: tr} 30 | req, _ := http.NewRequest("GET", myUrl, nil) 31 | 32 | go func() { 33 | response, err := httpClient.Do(req) 34 | if err != nil { 35 | fmt.Println(err) 36 | data <- myData{nil, err} 37 | return 38 | } else { 39 | pack := myData{response, err} 40 | data <- pack 41 | } 42 | }() 43 | 44 | select { 45 | case <-c.Done(): 46 | tr.CancelRequest(req) 47 | <-data 48 | fmt.Println("The request was canceled!") 49 | return c.Err() 50 | case ok := <-data: 51 | err := ok.err 52 | resp := ok.r 53 | if err != nil { 54 | fmt.Println("Error select:", err) 55 | return err 56 | } 57 | defer resp.Body.Close() 58 | 59 | realHTTPData, err := io.ReadAll(resp.Body) 60 | if err != nil { 61 | fmt.Println("Error select:", err) 62 | return err 63 | } 64 | // Although fmt.Printf() is used here, server processes 65 | // use the log.Printf() function instead. 66 | fmt.Printf("Server Response: %s\n", realHTTPData) 67 | } 68 | return nil 69 | } 70 | 71 | func main() { 72 | if len(os.Args) == 1 { 73 | fmt.Println("Need a URL and a delay!") 74 | return 75 | } 76 | 77 | myUrl = os.Args[1] 78 | if len(os.Args) == 3 { 79 | t, err := strconv.Atoi(os.Args[2]) 80 | if err != nil { 81 | fmt.Println(err) 82 | return 83 | } 84 | delay = t 85 | } 86 | 87 | fmt.Println("Delay:", delay) 88 | c := context.Background() 89 | c, cancel := context.WithTimeout(c, time.Duration(delay)*time.Second) 90 | defer cancel() 91 | 92 | fmt.Printf("Connecting to %s \n", myUrl) 93 | wg.Add(1) 94 | go connect(c) 95 | wg.Wait() 96 | fmt.Println("Exiting...") 97 | } 98 | -------------------------------------------------------------------------------- /ch08/timeoutServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "time" 8 | ) 9 | 10 | func myHandler(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, "Serving: %s\n", r.URL.Path) 12 | fmt.Printf("Served: %s\n", r.Host) 13 | } 14 | 15 | func timeHandler(w http.ResponseWriter, r *http.Request) { 16 | t := time.Now().Format(time.RFC1123) 17 | Body := "The current time is:" 18 | fmt.Fprintf(w, "

%s

", Body) 19 | fmt.Fprintf(w, "

%s

\n", t) 20 | fmt.Fprintf(w, "Serving: %s\n", r.URL.Path) 21 | fmt.Printf("Served time for: %s\n", r.Host) 22 | } 23 | 24 | func main() { 25 | PORT := ":8001" 26 | arguments := os.Args 27 | if len(arguments) != 1 { 28 | PORT = ":" + arguments[1] 29 | } 30 | fmt.Println("Using port number: ", PORT) 31 | 32 | m := http.NewServeMux() 33 | srv := &http.Server{ 34 | Addr: PORT, 35 | Handler: m, 36 | ReadTimeout: 3 * time.Second, 37 | WriteTimeout: 3 * time.Second, 38 | } 39 | 40 | m.HandleFunc("/time", timeHandler) 41 | m.HandleFunc("/", myHandler) 42 | 43 | err := srv.ListenAndServe() 44 | if err != nil { 45 | fmt.Println(err) 46 | return 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ch08/withDeadline.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | "time" 11 | ) 12 | 13 | var timeout = time.Duration(time.Second) 14 | 15 | func Timeout(network, host string) (net.Conn, error) { 16 | conn, err := net.DialTimeout(network, host, timeout) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | conn.SetDeadline(time.Now().Add(timeout)) 22 | return conn, nil 23 | } 24 | 25 | func main() { 26 | if len(os.Args) == 1 { 27 | fmt.Println("Please provide a URL") 28 | return 29 | } 30 | 31 | if len(os.Args) == 3 { 32 | temp, err := strconv.Atoi(os.Args[2]) 33 | if err == nil { 34 | timeout = time.Duration(time.Duration(temp) * time.Second) 35 | } 36 | } 37 | fmt.Println("Timeout value:", timeout) 38 | 39 | URL := os.Args[1] 40 | t := http.Transport{ 41 | Dial: Timeout, 42 | } 43 | 44 | client := http.Client{ 45 | Transport: &t, 46 | } 47 | 48 | data, err := client.Get(URL) 49 | if err != nil { 50 | fmt.Println(err) 51 | } else { 52 | defer data.Body.Close() 53 | _, err := io.Copy(os.Stdout, data.Body) 54 | if err != nil { 55 | fmt.Println(err) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ch08/wwwClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httputil" 7 | "net/url" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | func main() { 15 | if len(os.Args) != 2 { 16 | fmt.Printf("Usage: %s URL\n", filepath.Base(os.Args[0])) 17 | return 18 | } 19 | 20 | URL, err := url.Parse(os.Args[1]) 21 | if err != nil { 22 | fmt.Println("Error in parsing:", err) 23 | return 24 | } 25 | 26 | c := &http.Client{ 27 | Timeout: 15 * time.Second, 28 | } 29 | 30 | request, err := http.NewRequest(http.MethodGet, URL.String(), nil) 31 | if err != nil { 32 | fmt.Println("Get:", err) 33 | return 34 | } 35 | 36 | httpData, err := c.Do(request) 37 | if err != nil { 38 | fmt.Println("Error in Do():", err) 39 | return 40 | } 41 | 42 | fmt.Println("Status code:", httpData.Status) 43 | header, _ := httputil.DumpResponse(httpData, false) 44 | fmt.Print(string(header)) 45 | 46 | contentType := httpData.Header.Get("Content-Type") 47 | characterSet := strings.SplitAfter(contentType, "charset=") 48 | if len(characterSet) > 1 { 49 | fmt.Println("Character Set:", characterSet[1]) 50 | } 51 | 52 | if httpData.ContentLength == -1 { 53 | fmt.Println("ContentLength is unknown!") 54 | } else { 55 | fmt.Println("ContentLength:", httpData.ContentLength) 56 | } 57 | 58 | length := 0 59 | var buffer [1024]byte 60 | r := httpData.Body 61 | for { 62 | n, err := r.Read(buffer[0:]) 63 | if err != nil { 64 | fmt.Println(err) 65 | break 66 | } 67 | length = length + n 68 | } 69 | fmt.Println("Calculated response data length:", length) 70 | } 71 | -------------------------------------------------------------------------------- /ch08/wwwServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "time" 8 | ) 9 | 10 | func myHandler(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, "Serving: %s\n", r.URL.Path) 12 | fmt.Printf("Served: %s\n", r.Host) 13 | } 14 | 15 | func timeHandler(w http.ResponseWriter, r *http.Request) { 16 | t := time.Now().Format(time.RFC1123) 17 | Body := "The current time is:" 18 | fmt.Fprintf(w, "

%s

", Body) 19 | fmt.Fprintf(w, "

%s

\n", t) 20 | 21 | fmt.Fprintf(w, "Serving: %s\n", r.URL.Path) 22 | fmt.Printf("Served time for: %s\n", r.Host) 23 | } 24 | 25 | func main() { 26 | PORT := ":8001" 27 | arguments := os.Args 28 | if len(arguments) != 1 { 29 | PORT = ":" + arguments[1] 30 | } 31 | fmt.Println("Using port number: ", PORT) 32 | 33 | http.HandleFunc("/time", timeHandler) 34 | http.HandleFunc("/", myHandler) 35 | 36 | err := http.ListenAndServe(PORT, nil) 37 | if err != nil { 38 | fmt.Println(err) 39 | return 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ch09/concTCP.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var count = 0 13 | 14 | func handleConnection(c net.Conn) { 15 | fmt.Print(".") 16 | for { 17 | netData, err := bufio.NewReader(c).ReadString('\n') 18 | if err != nil { 19 | fmt.Println(err) 20 | return 21 | } 22 | 23 | temp := strings.TrimSpace(string(netData)) 24 | if temp == "STOP" { 25 | break 26 | } 27 | fmt.Println(temp) 28 | 29 | counter := "Client number: " + strconv.Itoa(count) + "\n" 30 | c.Write([]byte(string(counter))) 31 | } 32 | c.Close() 33 | } 34 | 35 | func main() { 36 | arguments := os.Args 37 | if len(arguments) == 1 { 38 | fmt.Println("Please provide a port number!") 39 | return 40 | } 41 | 42 | PORT := ":" + arguments[1] 43 | l, err := net.Listen("tcp4", PORT) 44 | if err != nil { 45 | fmt.Println(err) 46 | return 47 | } 48 | defer l.Close() 49 | 50 | for { 51 | c, err := l.Accept() 52 | if err != nil { 53 | fmt.Println(err) 54 | return 55 | } 56 | go handleConnection(c) 57 | count++ 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ch09/otherTCPclient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | arguments := os.Args 13 | if len(arguments) == 1 { 14 | fmt.Println("Please provide a server:port string!") 15 | return 16 | } 17 | 18 | connect := arguments[1] 19 | tcpAddr, err := net.ResolveTCPAddr("tcp4", connect) 20 | if err != nil { 21 | fmt.Println("ResolveTCPAddr:", err) 22 | return 23 | } 24 | 25 | conn, err := net.DialTCP("tcp4", nil, tcpAddr) 26 | if err != nil { 27 | fmt.Println("DialTCP:", err) 28 | return 29 | } 30 | 31 | for { 32 | reader := bufio.NewReader(os.Stdin) 33 | fmt.Print(">> ") 34 | text, _ := reader.ReadString('\n') 35 | fmt.Fprintf(conn, text+"\n") 36 | 37 | message, _ := bufio.NewReader(conn).ReadString('\n') 38 | fmt.Print("->: " + message) 39 | if strings.TrimSpace(string(text)) == "STOP" { 40 | fmt.Println("TCP client exiting...") 41 | conn.Close() 42 | return 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ch09/otherTCPserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | arguments := os.Args 12 | if len(arguments) == 1 { 13 | fmt.Println("Please provide a port number!") 14 | return 15 | } 16 | 17 | SERVER := "localhost" + ":" + arguments[1] 18 | s, err := net.ResolveTCPAddr("tcp", SERVER) 19 | if err != nil { 20 | fmt.Println(err) 21 | return 22 | } 23 | 24 | l, err := net.ListenTCP("tcp", s) 25 | if err != nil { 26 | fmt.Println(err) 27 | return 28 | } 29 | 30 | buffer := make([]byte, 1024) 31 | conn, err := l.Accept() 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | for { 38 | n, err := conn.Read(buffer) 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | 44 | if strings.TrimSpace(string(buffer[0:n])) == "STOP" { 45 | fmt.Println("Exiting TCP server!") 46 | conn.Close() 47 | return 48 | } 49 | 50 | fmt.Print("> ", string(buffer[0:n-1]), "\n") 51 | _, err = conn.Write(buffer) 52 | if err != nil { 53 | fmt.Println(err) 54 | return 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ch09/socketClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | // Read socket path 14 | if len(os.Args) == 1 { 15 | fmt.Println("Need socket path") 16 | return 17 | } 18 | socketPath := os.Args[1] 19 | 20 | c, err := net.Dial("unix", socketPath) 21 | if err != nil { 22 | fmt.Println(err) 23 | return 24 | } 25 | defer c.Close() 26 | 27 | for { 28 | reader := bufio.NewReader(os.Stdin) 29 | fmt.Print(">> ") 30 | text, _ := reader.ReadString('\n') 31 | 32 | _, err = c.Write([]byte(text)) 33 | if err != nil { 34 | fmt.Println("Write:", err) 35 | break 36 | } 37 | 38 | buf := make([]byte, 256) 39 | 40 | n, err := c.Read(buf[:]) 41 | if err != nil { 42 | fmt.Println(err, n) 43 | return 44 | } 45 | fmt.Print("Read: ", string(buf[0:n])) 46 | 47 | if strings.TrimSpace(string(text)) == "STOP" { 48 | fmt.Println("Exiting UNIX domain socket client!") 49 | return 50 | } 51 | 52 | time.Sleep(5 * time.Second) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ch09/socketServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | ) 8 | 9 | func echo(c net.Conn) { 10 | for { 11 | buf := make([]byte, 128) 12 | n, err := c.Read(buf) 13 | if err != nil { 14 | fmt.Println("Read:", err) 15 | return 16 | } 17 | 18 | data := buf[0:n] 19 | fmt.Print("Server got: ", string(data)) 20 | _, err = c.Write(data) 21 | if err != nil { 22 | fmt.Println("Write:", err) 23 | return 24 | } 25 | } 26 | } 27 | 28 | func main() { 29 | // Read socket path 30 | if len(os.Args) == 1 { 31 | fmt.Println("Need socket path") 32 | return 33 | } 34 | socketPath := os.Args[1] 35 | 36 | // If socketPath exists, delete it 37 | _, err := os.Stat(socketPath) 38 | if err == nil { 39 | fmt.Println("Deleting existing", socketPath) 40 | err := os.Remove(socketPath) 41 | if err != nil { 42 | fmt.Println(err) 43 | return 44 | } 45 | } 46 | 47 | l, err := net.Listen("unix", socketPath) 48 | if err != nil { 49 | fmt.Println("listen error:", err) 50 | return 51 | } 52 | 53 | for { 54 | fd, err := l.Accept() 55 | if err != nil { 56 | fmt.Println("Accept error:", err) 57 | return 58 | } 59 | go echo(fd) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ch09/tcpC.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | arguments := os.Args 13 | if len(arguments) == 1 { 14 | fmt.Println("Please provide host:port.") 15 | return 16 | } 17 | 18 | connect := arguments[1] 19 | c, err := net.Dial("tcp", connect) 20 | if err != nil { 21 | fmt.Println(err) 22 | return 23 | } 24 | 25 | for { 26 | reader := bufio.NewReader(os.Stdin) 27 | fmt.Print(">> ") 28 | text, _ := reader.ReadString('\n') 29 | fmt.Fprintf(c, text+"\n") 30 | 31 | message, _ := bufio.NewReader(c).ReadString('\n') 32 | fmt.Print("->: " + message) 33 | if strings.TrimSpace(string(text)) == "STOP" { 34 | fmt.Println("TCP client exiting...") 35 | return 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ch09/tcpS.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | arguments := os.Args 14 | if len(arguments) == 1 { 15 | fmt.Println("Please provide port number") 16 | return 17 | } 18 | 19 | PORT := ":" + arguments[1] 20 | l, err := net.Listen("tcp", PORT) 21 | if err != nil { 22 | fmt.Println(err) 23 | return 24 | } 25 | defer l.Close() 26 | 27 | c, err := l.Accept() 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | 33 | for { 34 | netData, err := bufio.NewReader(c).ReadString('\n') 35 | if err != nil { 36 | fmt.Println(err) 37 | return 38 | } 39 | if strings.TrimSpace(string(netData)) == "STOP" { 40 | fmt.Println("Exiting TCP server!") 41 | return 42 | } 43 | 44 | fmt.Print("-> ", string(netData)) 45 | t := time.Now() 46 | myTime := t.Format(time.RFC3339) + "\n" 47 | c.Write([]byte(myTime)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch09/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Testing a WebSocket Server 10 | 11 | 12 |

Hello There!

13 | 14 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ch09/udpC.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | arguments := os.Args 13 | if len(arguments) == 1 { 14 | fmt.Println("Please provide a host:port string") 15 | return 16 | } 17 | CONNECT := arguments[1] 18 | 19 | s, err := net.ResolveUDPAddr("udp4", CONNECT) 20 | c, err := net.DialUDP("udp4", nil, s) 21 | 22 | if err != nil { 23 | fmt.Println(err) 24 | return 25 | } 26 | 27 | fmt.Printf("The UDP server is %s\n", c.RemoteAddr().String()) 28 | defer c.Close() 29 | 30 | for { 31 | reader := bufio.NewReader(os.Stdin) 32 | fmt.Print(">> ") 33 | text, _ := reader.ReadString('\n') 34 | data := []byte(text + "\n") 35 | _, err = c.Write(data) 36 | if strings.TrimSpace(string(data)) == "STOP" { 37 | fmt.Println("Exiting UDP client!") 38 | return 39 | } 40 | 41 | if err != nil { 42 | fmt.Println(err) 43 | return 44 | } 45 | 46 | buffer := make([]byte, 1024) 47 | n, _, err := c.ReadFromUDP(buffer) 48 | if err != nil { 49 | fmt.Println(err) 50 | return 51 | } 52 | fmt.Printf("Reply: %s\n", string(buffer[0:n])) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ch09/udpS.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net" 7 | "os" 8 | "strconv" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | func random(min, max int) int { 14 | return rand.Intn(max-min) + min 15 | } 16 | 17 | func main() { 18 | arguments := os.Args 19 | if len(arguments) == 1 { 20 | fmt.Println("Please provide a port number!") 21 | return 22 | } 23 | PORT := ":" + arguments[1] 24 | 25 | s, err := net.ResolveUDPAddr("udp4", PORT) 26 | if err != nil { 27 | fmt.Println(err) 28 | return 29 | } 30 | 31 | connection, err := net.ListenUDP("udp4", s) 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | defer connection.Close() 38 | buffer := make([]byte, 1024) 39 | rand.Seed(time.Now().Unix()) 40 | 41 | for { 42 | n, addr, err := connection.ReadFromUDP(buffer) 43 | fmt.Print("-> ", string(buffer[0:n-1])) 44 | 45 | if strings.TrimSpace(string(buffer[0:n])) == "STOP" { 46 | fmt.Println("Exiting UDP server!") 47 | return 48 | } 49 | 50 | data := []byte(strconv.Itoa(random(1, 1001))) 51 | fmt.Printf("data: %s\n", string(data)) 52 | _, err = connection.WriteToUDP(data, addr) 53 | if err != nil { 54 | fmt.Println(err) 55 | return 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ch10/rClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | "time" 11 | ) 12 | 13 | type User struct { 14 | Username string `json:"user"` 15 | Password string `json:"password"` 16 | } 17 | 18 | var u1 = User{"admin", "admin"} 19 | var u2 = User{"tsoukalos", "pass"} 20 | var u3 = User{"", "pass"} 21 | 22 | func deleteEndpoint(server string, user User) int { 23 | userMarshall, _ := json.Marshal(user) 24 | u := bytes.NewReader(userMarshall) 25 | 26 | req, err := http.NewRequest("DELETE", server+deleteEndPoint, u) 27 | if err != nil { 28 | fmt.Println("Error in req: ", err) 29 | return http.StatusInternalServerError 30 | } 31 | req.Header.Set("Content-Type", "application/json") 32 | 33 | c := &http.Client{ 34 | Timeout: 15 * time.Second, 35 | } 36 | 37 | resp, err := c.Do(req) 38 | defer resp.Body.Close() 39 | 40 | if err != nil { 41 | fmt.Println("Error:", err) 42 | } 43 | if resp == nil { 44 | return http.StatusNotFound 45 | } 46 | 47 | data, err := io.ReadAll(resp.Body) 48 | fmt.Print("/delete returned: ", string(data)) 49 | if err != nil { 50 | fmt.Println("Error:", err) 51 | } 52 | return resp.StatusCode 53 | } 54 | 55 | func getEndpoint(server string, user User) int { 56 | userMarshall, _ := json.Marshal(user) 57 | u := bytes.NewReader(userMarshall) 58 | 59 | req, err := http.NewRequest("GET", server+getEndPoint, u) 60 | if err != nil { 61 | fmt.Println("Error in req: ", err) 62 | return http.StatusInternalServerError 63 | } 64 | req.Header.Set("Content-Type", "application/json") 65 | 66 | c := &http.Client{ 67 | Timeout: 15 * time.Second, 68 | } 69 | 70 | resp, err := c.Do(req) 71 | defer resp.Body.Close() 72 | 73 | if err != nil { 74 | fmt.Println("Error:", err) 75 | } 76 | if resp == nil { 77 | return http.StatusNotFound 78 | } 79 | 80 | data, err := io.ReadAll(resp.Body) 81 | fmt.Print("/get returned: ", string(data)) 82 | if err != nil { 83 | fmt.Println("Error:", err) 84 | } 85 | return resp.StatusCode 86 | } 87 | 88 | func addEndpoint(server string, user User) int { 89 | userMarshall, _ := json.Marshal(user) 90 | u := bytes.NewReader(userMarshall) 91 | 92 | req, err := http.NewRequest("POST", server+addEndPoint, u) 93 | if err != nil { 94 | fmt.Println("Error in req: ", err) 95 | return http.StatusInternalServerError 96 | } 97 | req.Header.Set("Content-Type", "application/json") 98 | 99 | c := &http.Client{ 100 | Timeout: 15 * time.Second, 101 | } 102 | 103 | resp, err := c.Do(req) 104 | defer resp.Body.Close() 105 | 106 | if resp == nil || (resp.StatusCode == http.StatusNotFound) { 107 | return resp.StatusCode 108 | } 109 | 110 | return resp.StatusCode 111 | } 112 | 113 | func timeEndpoint(server string) (int, string) { 114 | req, err := http.NewRequest("POST", server+timeEndPoint, nil) 115 | if err != nil { 116 | fmt.Println("Error in req: ", err) 117 | return http.StatusInternalServerError, "" 118 | } 119 | 120 | c := &http.Client{ 121 | Timeout: 15 * time.Second, 122 | } 123 | 124 | resp, err := c.Do(req) 125 | defer resp.Body.Close() 126 | 127 | if resp == nil || (resp.StatusCode == http.StatusNotFound) { 128 | return resp.StatusCode, "" 129 | } 130 | 131 | data, _ := io.ReadAll(resp.Body) 132 | return resp.StatusCode, string(data) 133 | } 134 | 135 | func slashEndpoint(server, URL string) (int, string) { 136 | req, err := http.NewRequest("POST", server+URL, nil) 137 | if err != nil { 138 | fmt.Println("Error in req: ", err) 139 | return http.StatusInternalServerError, "" 140 | } 141 | 142 | c := &http.Client{ 143 | Timeout: 15 * time.Second, 144 | } 145 | 146 | resp, err := c.Do(req) 147 | defer resp.Body.Close() 148 | 149 | if resp == nil { 150 | return resp.StatusCode, "" 151 | } 152 | 153 | data, _ := io.ReadAll(resp.Body) 154 | return resp.StatusCode, string(data) 155 | } 156 | 157 | const addEndPoint = "/add" 158 | const getEndPoint = "/get" 159 | const deleteEndPoint = "/delete" 160 | const timeEndPoint = "/time" 161 | 162 | func main() { 163 | if len(os.Args) != 2 { 164 | fmt.Println("Wrong number of arguments!") 165 | fmt.Println("Need: Server") 166 | return 167 | } 168 | server := os.Args[1] 169 | 170 | fmt.Println("/add") 171 | HTTPcode := addEndpoint(server, u1) 172 | if HTTPcode != http.StatusOK { 173 | fmt.Println("u1 Return code:", HTTPcode) 174 | } else { 175 | fmt.Println("u1 Data added:", u1, HTTPcode) 176 | } 177 | 178 | HTTPcode = addEndpoint(server, u2) 179 | if HTTPcode != http.StatusOK { 180 | fmt.Println("u2 Return code:", HTTPcode) 181 | } else { 182 | fmt.Println("u2 Data added:", u2, HTTPcode) 183 | } 184 | 185 | HTTPcode = addEndpoint(server, u3) 186 | if HTTPcode != http.StatusOK { 187 | fmt.Println("u3 Return code:", HTTPcode) 188 | } else { 189 | fmt.Println("u3 Data added:", u3, HTTPcode) 190 | } 191 | 192 | fmt.Println("/get") 193 | HTTPcode = getEndpoint(server, u1) 194 | fmt.Println("/get u1 return code:", HTTPcode) 195 | HTTPcode = getEndpoint(server, u2) 196 | fmt.Println("/get u2 return code:", HTTPcode) 197 | HTTPcode = getEndpoint(server, u3) 198 | fmt.Println("/get u3 return code:", HTTPcode) 199 | 200 | fmt.Println("/delete") 201 | HTTPcode = deleteEndpoint(server, u1) 202 | fmt.Println("/delete u1 return code:", HTTPcode) 203 | HTTPcode = deleteEndpoint(server, u1) 204 | fmt.Println("/delete u1 return code:", HTTPcode) 205 | HTTPcode = deleteEndpoint(server, u2) 206 | fmt.Println("/delete u2 return code:", HTTPcode) 207 | HTTPcode = deleteEndpoint(server, u3) 208 | fmt.Println("/delete u3 return code:", HTTPcode) 209 | 210 | fmt.Println("/time") 211 | HTTPcode, myTime := timeEndpoint(server) 212 | fmt.Print("/time returned: ", HTTPcode, " ", myTime) 213 | time.Sleep(time.Second) 214 | HTTPcode, myTime = timeEndpoint(server) 215 | fmt.Print("/time returned: ", HTTPcode, " ", myTime) 216 | 217 | fmt.Println("/") 218 | URL := "/" 219 | HTTPcode, response := slashEndpoint(server, URL) 220 | fmt.Print("/ returned: ", HTTPcode, " with response: ", response) 221 | 222 | fmt.Println("/what") 223 | URL = "/what" 224 | HTTPcode, response = slashEndpoint(server, URL) 225 | fmt.Print(URL, " returned: ", HTTPcode, " with response: ", response) 226 | } 227 | -------------------------------------------------------------------------------- /ch10/rServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net/http" 9 | "os" 10 | "time" 11 | ) 12 | 13 | type User struct { 14 | Username string `json:"user"` 15 | Password string `json:"password"` 16 | } 17 | 18 | var user User 19 | 20 | // PORT is where the web server listens to 21 | var PORT = ":1234" 22 | 23 | // DATA is the map that holds User records 24 | var DATA = make(map[string]string) 25 | 26 | func defaultHandler(w http.ResponseWriter, r *http.Request) { 27 | log.Println("Serving:", r.URL.Path, "from", r.Host) 28 | w.WriteHeader(http.StatusNotFound) 29 | Body := "Thanks for visiting!\n" 30 | fmt.Fprintf(w, "%s", Body) 31 | } 32 | 33 | func timeHandler(w http.ResponseWriter, r *http.Request) { 34 | log.Println("Serving:", r.URL.Path, "from", r.Host) 35 | t := time.Now().Format(time.RFC1123) 36 | Body := "The current time is: " + t + "\n" 37 | fmt.Fprintf(w, "%s", Body) 38 | } 39 | 40 | func addHandler(w http.ResponseWriter, r *http.Request) { 41 | log.Println("Serving:", r.URL.Path, "from", r.Host, r.Method) 42 | if r.Method != http.MethodPost { 43 | http.Error(w, "Error:", http.StatusMethodNotAllowed) 44 | fmt.Fprintf(w, "%s\n", "Method not allowed!") 45 | return 46 | } 47 | 48 | d, err := io.ReadAll(r.Body) 49 | if err != nil { 50 | http.Error(w, "Error:", http.StatusBadRequest) 51 | return 52 | } 53 | 54 | err = json.Unmarshal(d, &user) 55 | if err != nil { 56 | log.Println(err) 57 | http.Error(w, "Error:", http.StatusBadRequest) 58 | return 59 | } 60 | 61 | if user.Username != "" { 62 | DATA[user.Username] = user.Password 63 | log.Println(DATA) 64 | w.WriteHeader(http.StatusOK) 65 | } else { 66 | http.Error(w, "Error:", http.StatusBadRequest) 67 | return 68 | } 69 | } 70 | 71 | func getHandler(w http.ResponseWriter, r *http.Request) { 72 | log.Println("Serving:", r.URL.Path, "from", r.Host, r.Method) 73 | if r.Method != http.MethodGet { 74 | http.Error(w, "Error:", http.StatusMethodNotAllowed) 75 | fmt.Fprintf(w, "%s\n", "Method not allowed!") 76 | return 77 | } 78 | 79 | d, err := io.ReadAll(r.Body) 80 | if err != nil { 81 | http.Error(w, "ReadAll - Error", http.StatusBadRequest) 82 | return 83 | } 84 | 85 | err = json.Unmarshal(d, &user) 86 | if err != nil { 87 | log.Println(err) 88 | http.Error(w, "Unmarshal - Error", http.StatusBadRequest) 89 | return 90 | } 91 | fmt.Println(user) 92 | 93 | _, ok := DATA[user.Username] 94 | if ok && user.Username != "" { 95 | log.Println("Found!") 96 | w.WriteHeader(http.StatusOK) 97 | fmt.Fprintf(w, "%s\n", d) 98 | } else { 99 | log.Println("Not found!") 100 | w.WriteHeader(http.StatusNotFound) 101 | http.Error(w, "Map - Resource not found!", http.StatusNotFound) 102 | } 103 | return 104 | } 105 | 106 | func deleteHandler(w http.ResponseWriter, r *http.Request) { 107 | log.Println("Serving:", r.URL.Path, "from", r.Host, r.Method) 108 | if r.Method != http.MethodDelete { 109 | http.Error(w, "Error:", http.StatusMethodNotAllowed) 110 | fmt.Fprintf(w, "%s\n", "Method not allowed!") 111 | return 112 | } 113 | 114 | d, err := io.ReadAll(r.Body) 115 | if err != nil { 116 | http.Error(w, "ReadAll - Error", http.StatusBadRequest) 117 | return 118 | } 119 | 120 | err = json.Unmarshal(d, &user) 121 | if err != nil { 122 | log.Println(err) 123 | http.Error(w, "Unmarshal - Error", http.StatusBadRequest) 124 | return 125 | } 126 | log.Println(user) 127 | 128 | _, ok := DATA[user.Username] 129 | if ok && user.Username != "" { 130 | if user.Password == DATA[user.Username] { 131 | delete(DATA, user.Username) 132 | w.WriteHeader(http.StatusOK) 133 | fmt.Fprintf(w, "%s\n", d) 134 | log.Println(DATA) 135 | } 136 | } else { 137 | log.Println("User", user.Username, "Not found!") 138 | w.WriteHeader(http.StatusNotFound) 139 | http.Error(w, "Delete - Resource not found!", http.StatusNotFound) 140 | } 141 | log.Println("After:", DATA) 142 | return 143 | } 144 | 145 | func main() { 146 | arguments := os.Args 147 | if len(arguments) != 1 { 148 | PORT = ":" + arguments[1] 149 | } 150 | 151 | mux := http.NewServeMux() 152 | s := &http.Server{ 153 | Addr: PORT, 154 | Handler: mux, 155 | IdleTimeout: 10 * time.Second, 156 | ReadTimeout: time.Second, 157 | WriteTimeout: time.Second, 158 | } 159 | 160 | mux.Handle("/time", http.HandlerFunc(timeHandler)) 161 | mux.Handle("/add", http.HandlerFunc(addHandler)) 162 | mux.Handle("/get", http.HandlerFunc(getHandler)) 163 | mux.Handle("/delete", http.HandlerFunc(deleteHandler)) 164 | mux.Handle("/", http.HandlerFunc(defaultHandler)) 165 | 166 | fmt.Println("Ready to serve at", PORT) 167 | err := s.ListenAndServe() 168 | if err != nil { 169 | fmt.Println(err) 170 | return 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /ch11/cannotReach.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func S2() { 8 | return 9 | fmt.Println("Hello!") 10 | } 11 | 12 | func S1() { 13 | fmt.Println("In S1()") 14 | return 15 | 16 | fmt.Println("Leaving S1()") 17 | } 18 | 19 | func main() { 20 | S1() 21 | } 22 | -------------------------------------------------------------------------------- /ch11/cleanup/cleanup.go: -------------------------------------------------------------------------------- 1 | package cleanup 2 | 3 | import "fmt" 4 | 5 | func Foo() { 6 | fmt.Println("Inside foo!") 7 | } 8 | -------------------------------------------------------------------------------- /ch11/cleanup/cleanup_test.go: -------------------------------------------------------------------------------- 1 | package cleanup 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "testing" 8 | ) 9 | 10 | func myCleanUp() func() { 11 | return func() { 12 | fmt.Println("Cleaning up!") 13 | } 14 | } 15 | 16 | func TestFoo(t *testing.T) { 17 | t1 := path.Join(os.TempDir(), "test01") 18 | t2 := path.Join(os.TempDir(), "test02") 19 | err := os.Mkdir(t1, 0755) 20 | if err != nil { 21 | t.Error("os.Mkdir() failed:", err) 22 | return 23 | } 24 | 25 | defer t.Cleanup(func() { 26 | err = os.Remove(t1) 27 | if err != nil { 28 | t.Error("os.Mkdir() failed:", err) 29 | } 30 | }) 31 | 32 | err = os.Mkdir(t2, 0755) 33 | if err != nil { 34 | t.Error("os.Mkdir() failed:", err) 35 | return 36 | } 37 | } 38 | 39 | func TestBar(t *testing.T) { 40 | t1 := t.TempDir() 41 | fmt.Println(t1) 42 | t.Cleanup(myCleanUp()) 43 | } 44 | -------------------------------------------------------------------------------- /ch11/coverage/coverage.go: -------------------------------------------------------------------------------- 1 | package coverage 2 | 3 | import "fmt" 4 | 5 | func f1() { 6 | if true { 7 | fmt.Println("Hello!") 8 | } else { 9 | fmt.Println("Hi!") 10 | } 11 | } 12 | 13 | func f2(n int) int { 14 | if n >= 0 { 15 | return 0 16 | } else if n == 1 { 17 | return 1 18 | } else { 19 | return f2(n-1) + f2(n-2) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ch11/coverage/coverage_test.go: -------------------------------------------------------------------------------- 1 | package coverage 2 | 3 | import "testing" 4 | 5 | func Test_f1(t *testing.T) { 6 | f1() 7 | } 8 | 9 | func Test_f2(t *testing.T) { 10 | _ = f2(123) 11 | } 12 | -------------------------------------------------------------------------------- /ch11/crossCompile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | func main() { 9 | fmt.Print("You are using ", runtime.GOOS, " ") 10 | fmt.Println("on a(n)", runtime.GOARCH, "machine") 11 | fmt.Println("with Go version", runtime.Version()) 12 | } 13 | -------------------------------------------------------------------------------- /ch11/exampleFunctions/exampleFunctions.go: -------------------------------------------------------------------------------- 1 | package exampleFunctions 2 | 3 | func LengthRange(s string) int { 4 | i := 0 5 | for _, _ = range s { 6 | i = i + 1 7 | } 8 | return i 9 | } 10 | -------------------------------------------------------------------------------- /ch11/exampleFunctions/exampleFunctions_test.go: -------------------------------------------------------------------------------- 1 | package exampleFunctions 2 | 3 | import "fmt" 4 | 5 | func ExampleLengthRange() { 6 | fmt.Println(LengthRange("Mihalis")) 7 | fmt.Println(LengthRange("Mastering Go, 3rd edition!")) 8 | // Output: 9 | // 7 10 | // 7 11 | } 12 | -------------------------------------------------------------------------------- /ch11/generate/echo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Hello world!" -------------------------------------------------------------------------------- /ch11/generate/goGenerate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | //go:generate ./echo.sh 6 | 7 | //go:generate echo GOFILE: $GOFILE 8 | //go:generate echo GOARCH: $GOARCH 9 | //go:generate echo GOOS: $GOOS 10 | //go:generate echo GOLINE: $GOLINE 11 | //go:generate echo GOPACKAGE: $GOPACKAGE 12 | //go:generate echo DOLLAR: $DOLLAR 13 | 14 | //go:generate echo Hello! 15 | //go:generate ls -l 16 | //go:generate ./hello.py 17 | 18 | func main() { 19 | fmt.Println("Hello there!") 20 | } 21 | -------------------------------------------------------------------------------- /ch11/generate/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | print("Hello from Python!") 4 | -------------------------------------------------------------------------------- /ch11/intRE/intRE.go: -------------------------------------------------------------------------------- 1 | package intRE 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | func matchInt(s string) bool { 8 | t := []byte(s) 9 | re := regexp.MustCompile(`^[-+]?\d+$`) 10 | return re.Match(t) 11 | } 12 | -------------------------------------------------------------------------------- /ch11/intRE/intRE_test.go: -------------------------------------------------------------------------------- 1 | package intRE 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func random(min, max int) int { 11 | return rand.Intn(max-min) + min 12 | } 13 | 14 | func Test_matchInt(t *testing.T) { 15 | if matchInt("") { 16 | t.Error(`matchInt("") != true`) 17 | } 18 | 19 | if matchInt("00") == false { 20 | t.Error(`matchInt("00") != true`) 21 | } 22 | 23 | if matchInt("-00") == false { 24 | t.Error(`matchInt("-00") != true`) 25 | } 26 | 27 | if matchInt("+00") == false { 28 | t.Error(`matchInt("+00") != true`) 29 | } 30 | } 31 | 32 | func Test_with_random(t *testing.T) { 33 | SEED := time.Now().Unix() 34 | rand.Seed(SEED) 35 | n := strconv.Itoa(random(-100000, 19999)) 36 | 37 | if matchInt(n) == false { 38 | t.Error("n = ", n) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ch11/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | err := run(os.Args, os.Stdout) 12 | if err != nil { 13 | fmt.Printf("%s\n", err) 14 | return 15 | } 16 | } 17 | 18 | func run(args []string, stdout io.Writer) error { 19 | if len(args) == 1 { 20 | return errors.New("No input!") 21 | } 22 | 23 | // Continue with the implementation of run() 24 | // as you would have with main() 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /ch11/profileCla.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "os" 7 | "path" 8 | "runtime" 9 | "runtime/pprof" 10 | "time" 11 | ) 12 | 13 | func fibo1(n int) int64 { 14 | if n == 0 || n == 1 { 15 | return int64(n) 16 | } 17 | time.Sleep(time.Millisecond) 18 | return int64(fibo2(n-1)) + int64(fibo2(n-2)) 19 | } 20 | 21 | func fibo2(n int) int { 22 | fn := make(map[int]int) 23 | for i := 0; i <= n; i++ { 24 | var f int 25 | if i <= 2 { 26 | f = 1 27 | } else { 28 | f = fn[i-1] + fn[i-2] 29 | } 30 | fn[i] = f 31 | } 32 | time.Sleep(50 * time.Millisecond) 33 | return fn[n] 34 | } 35 | 36 | func N1(n int) bool { 37 | k := math.Floor(float64(n/2 + 1)) 38 | for i := 2; i < int(k); i++ { 39 | if (n % i) == 0 { 40 | return false 41 | } 42 | } 43 | return true 44 | } 45 | 46 | func N2(n int) bool { 47 | for i := 2; i < n; i++ { 48 | if (n % i) == 0 { 49 | return false 50 | } 51 | } 52 | return true 53 | } 54 | 55 | func main() { 56 | cpuFilename := path.Join(os.TempDir(), "cpuProfileCla.out") 57 | cpuFile, err := os.Create(cpuFilename) 58 | if err != nil { 59 | fmt.Println(err) 60 | return 61 | } 62 | pprof.StartCPUProfile(cpuFile) 63 | defer pprof.StopCPUProfile() 64 | 65 | total := 0 66 | for i := 2; i < 100000; i++ { 67 | n := N1(i) 68 | if n { 69 | total = total + 1 70 | } 71 | } 72 | fmt.Println("Total primes:", total) 73 | 74 | total = 0 75 | for i := 2; i < 100000; i++ { 76 | n := N2(i) 77 | if n { 78 | total = total + 1 79 | } 80 | } 81 | fmt.Println("Total primes:", total) 82 | 83 | for i := 1; i < 90; i++ { 84 | n := fibo1(i) 85 | fmt.Print(n, " ") 86 | } 87 | fmt.Println() 88 | 89 | for i := 1; i < 90; i++ { 90 | n := fibo2(i) 91 | fmt.Print(n, " ") 92 | } 93 | fmt.Println() 94 | 95 | runtime.GC() 96 | 97 | // Memory profiling! 98 | memoryFilename := path.Join(os.TempDir(), "memoryProfileCla.out") 99 | memory, err := os.Create(memoryFilename) 100 | if err != nil { 101 | fmt.Println(err) 102 | return 103 | } 104 | defer memory.Close() 105 | 106 | for i := 0; i < 10; i++ { 107 | s := make([]byte, 50000000) 108 | if s == nil { 109 | fmt.Println("Operation failed!") 110 | } 111 | time.Sleep(50 * time.Millisecond) 112 | } 113 | 114 | err = pprof.WriteHeapProfile(memory) 115 | if err != nil { 116 | fmt.Println(err) 117 | return 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /ch11/profileHTTP.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/pprof" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func myHandler(w http.ResponseWriter, r *http.Request) { 12 | fmt.Fprintf(w, "Serving: %s\n", r.URL.Path) 13 | fmt.Printf("Served: %s\n", r.Host) 14 | } 15 | 16 | func timeHandler(w http.ResponseWriter, r *http.Request) { 17 | t := time.Now().Format(time.RFC1123) 18 | Body := "The current time is:" 19 | fmt.Fprintf(w, "%s %s", Body, t) 20 | fmt.Fprintf(w, "Serving: %s\n", r.URL.Path) 21 | fmt.Printf("Served time for: %s\n", r.Host) 22 | } 23 | 24 | func main() { 25 | PORT := ":8001" 26 | arguments := os.Args 27 | if len(arguments) == 1 { 28 | fmt.Println("Using default port number: ", PORT) 29 | } else { 30 | PORT = ":" + arguments[1] 31 | fmt.Println("Using port number: ", PORT) 32 | } 33 | 34 | r := http.NewServeMux() 35 | r.HandleFunc("/time", timeHandler) 36 | r.HandleFunc("/", myHandler) 37 | 38 | r.HandleFunc("/debug/pprof/", pprof.Index) 39 | r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) 40 | r.HandleFunc("/debug/pprof/profile", pprof.Profile) 41 | r.HandleFunc("/debug/pprof/symbol", pprof.Symbol) 42 | r.HandleFunc("/debug/pprof/trace", pprof.Trace) 43 | 44 | err := http.ListenAndServe(PORT, r) 45 | if err != nil { 46 | fmt.Println(err) 47 | return 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch11/quickT/quickT.go: -------------------------------------------------------------------------------- 1 | package quickT 2 | 3 | type Point2D struct { 4 | X, Y int 5 | } 6 | 7 | func Add(x1, x2 Point2D) Point2D { 8 | temp := Point2D{} 9 | temp.X = x1.X + x2.X 10 | temp.Y = x1.Y + x2.Y 11 | return temp 12 | } 13 | -------------------------------------------------------------------------------- /ch11/quickT/quickT_test.go: -------------------------------------------------------------------------------- 1 | package quickT 2 | 3 | import ( 4 | "testing" 5 | "testing/quick" 6 | ) 7 | 8 | var N = 1000000 9 | 10 | func TestWithItself(t *testing.T) { 11 | condition := func(a, b Point2D) bool { 12 | return Add(a, b) == Add(b, a) 13 | } 14 | 15 | err := quick.Check(condition, &quick.Config{MaxCount: N}) 16 | if err != nil { 17 | t.Errorf("Error: %v", err) 18 | } 19 | } 20 | 21 | func TestThree(t *testing.T) { 22 | condition := func(a, b, c Point2D) bool { 23 | return Add(Add(a, b), c) == Add(a, b) 24 | } 25 | 26 | err := quick.Check(condition, &quick.Config{MaxCount: N}) 27 | if err != nil { 28 | t.Errorf("Error: %v", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ch11/table/r1.txt: -------------------------------------------------------------------------------- 1 | BenchmarkBuffer4Create-8 99690 10472 ns/op 2 | BenchmarkBuffer8Create-8 153714 6884 ns/op 3 | BenchmarkBuffer16Create-8 236559 5010 ns/op 4 | BenchmarkRead/10.txt-1-8 79945 14955 ns/op 5 | BenchmarkRead/10.txt-16-8 100158 12172 ns/op 6 | BenchmarkRead/10.txt-96-8 96610 11925 ns/op 7 | BenchmarkRead/1000.txt-1-8 3152 381347 ns/op 8 | BenchmarkRead/1000.txt-16-8 21458 54050 ns/op 9 | BenchmarkRead/1000.txt-96-8 60916 19115 ns/op 10 | BenchmarkRead/5k.txt-1-8 658 1812479 ns/op 11 | BenchmarkRead/5k.txt-16-8 5322 221801 ns/op 12 | BenchmarkRead/5k.txt-96-8 24004 51534 ns/op 13 | -------------------------------------------------------------------------------- /ch11/table/r2.txt: -------------------------------------------------------------------------------- 1 | BenchmarkBuffer4Create-8 1413792 840.4 ns/op 2 | BenchmarkBuffer4Create-8 1339208 821.1 ns/op 3 | BenchmarkBuffer8Create-8 1336443 803.2 ns/op 4 | BenchmarkBuffer8Create-8 1338175 794.6 ns/op 5 | BenchmarkBuffer16Create-8 1551810 771.3 ns/op 6 | BenchmarkBuffer16Create-8 1552940 769.7 ns/op 7 | BenchmarkRead/10.txt-1-8 301288 3981 ns/op 8 | BenchmarkRead/10.txt-1-8 302059 3993 ns/op 9 | BenchmarkRead/10.txt-16-8 475638 2588 ns/op 10 | BenchmarkRead/10.txt-16-8 475256 2579 ns/op 11 | BenchmarkRead/10.txt-96-8 448165 2617 ns/op 12 | BenchmarkRead/10.txt-96-8 455965 2607 ns/op 13 | BenchmarkRead/1000.txt-1-8 6883 174295 ns/op 14 | BenchmarkRead/1000.txt-1-8 6858 177339 ns/op 15 | BenchmarkRead/1000.txt-16-8 52712 22688 ns/op 16 | BenchmarkRead/1000.txt-16-8 52813 22674 ns/op 17 | BenchmarkRead/1000.txt-96-8 192631 6225 ns/op 18 | BenchmarkRead/1000.txt-96-8 192704 6225 ns/op 19 | BenchmarkRead/5k.txt-1-8 1329 893603 ns/op 20 | BenchmarkRead/5k.txt-1-8 1342 897768 ns/op 21 | BenchmarkRead/5k.txt-16-8 10000 108022 ns/op 22 | BenchmarkRead/5k.txt-16-8 10000 107310 ns/op 23 | BenchmarkRead/5k.txt-96-8 55705 21503 ns/op 24 | BenchmarkRead/5k.txt-96-8 55590 21528 ns/op 25 | -------------------------------------------------------------------------------- /ch11/table/table.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "math/rand" 7 | "os" 8 | ) 9 | 10 | var BUFFERSIZE int 11 | var FILESIZE int 12 | 13 | func random(min, max int) int { 14 | return rand.Intn(max-min) + min 15 | } 16 | 17 | func CreateBuffer(buf *[]byte, count int) { 18 | *buf = make([]byte, count) 19 | if count == 0 { 20 | return 21 | } 22 | 23 | for i := 0; i < count; i++ { 24 | intByte := byte(random(50, 100)) 25 | if len(*buf) > count { 26 | return 27 | } 28 | *buf = append(*buf, intByte) 29 | } 30 | } 31 | 32 | func Create(dst string, b, filesize int) error { 33 | _, err := os.Stat(dst) 34 | if err == nil { 35 | return fmt.Errorf("File %s already exists.", dst) 36 | } 37 | 38 | f, err := os.Create(dst) 39 | if err != nil { 40 | fmt.Printf("error opening file %s", err) 41 | return err 42 | } 43 | f.Close() 44 | 45 | f, err = os.OpenFile(dst, os.O_WRONLY, 0655) 46 | if err != nil { 47 | return err 48 | } 49 | defer f.Close() 50 | 51 | bwriter := bufio.NewWriterSize(f, b) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | buf := make([]byte, 0) 57 | CreateBuffer(&buf, b) 58 | buf = buf[:b] 59 | for { 60 | _, err := bwriter.Write(buf) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | if filesize < 0 { 66 | break 67 | } 68 | filesize = filesize - len(buf) 69 | } 70 | return err 71 | } 72 | 73 | func CountChars(filename string, b int) int { 74 | buf := make([]byte, 0) 75 | CreateBuffer(&buf, b) 76 | 77 | f, err := os.Open(filename) 78 | if err != nil { 79 | fmt.Printf("error opening file %s", err) 80 | return -1 81 | } 82 | defer f.Close() 83 | 84 | size := 0 85 | for { 86 | n, err := f.Read(buf) 87 | size = size + n 88 | if err != nil { 89 | break 90 | } 91 | } 92 | 93 | return size 94 | } 95 | -------------------------------------------------------------------------------- /ch11/table/table_test.go: -------------------------------------------------------------------------------- 1 | package table 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "strconv" 8 | "testing" 9 | ) 10 | 11 | var ERR error 12 | var countChars int 13 | 14 | func benchmarkCreate(b *testing.B, buffer, filesize int) { 15 | filename := path.Join(os.TempDir(), strconv.Itoa(buffer)) 16 | filename = filename + "-" + strconv.Itoa(filesize) 17 | var err error 18 | for i := 0; i < b.N; i++ { 19 | err = Create(filename, buffer, filesize) 20 | } 21 | ERR = err 22 | 23 | err = os.Remove(filename) 24 | if err != nil { 25 | fmt.Println(err) 26 | } 27 | ERR = err 28 | } 29 | 30 | func BenchmarkBuffer4Create(b *testing.B) { 31 | benchmarkCreate(b, 4, 1000000) 32 | } 33 | 34 | func BenchmarkBuffer8Create(b *testing.B) { 35 | benchmarkCreate(b, 8, 1000000) 36 | } 37 | 38 | func BenchmarkBuffer16Create(b *testing.B) { 39 | benchmarkCreate(b, 16, 1000000) 40 | } 41 | 42 | func BenchmarkRead(b *testing.B) { 43 | buffers := []int{1, 16, 96} 44 | files := []string{"10.txt", "1000.txt", "5k.txt"} 45 | 46 | for _, filename := range files { 47 | for _, bufSize := range buffers { 48 | name := fmt.Sprintf("%s-%d", filename, bufSize) 49 | b.Run(name, func(b *testing.B) { 50 | for i := 0; i < b.N; i++ { 51 | t := CountChars("./testdata/"+filename, bufSize) 52 | countChars = t 53 | } 54 | }) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ch11/table/testdata/10.txt: -------------------------------------------------------------------------------- 1 | 123 56789 2 | -------------------------------------------------------------------------------- /ch11/table/testdata/1000.txt: -------------------------------------------------------------------------------- 1 | 24.7712935913 2 | 5.13874018895 3 | 0.666622072088 4 | 14.9251948475 5 | 12.2339933078 6 | 6.19519532978 7 | 0.270901147706 8 | 7.15760560496 9 | 28.6468517887 10 | 9.37847479006 11 | 24.7712935913 12 | 5.13874018895 13 | 14 | 0.6622072088 15 | 14.9251948475 16 | 12.2339933078 17 | 6.19519532978 18 | 0.270901147706 19 | 7.15760560496 20 | 28.6468517887 21 | 9.37847479006 22 | 24.7712935913 23 | 5.13874018895 24 | 0.6622072088 25 | 14.9251948475 26 | 12.2339933078 27 | 6.19519532978 28 | 0.270901147706 29 | 7.15760560496 30 | 28.6468517887 31 | 9.37847479006 32 | 24.7712935913 33 | 5.13874018895 34 | 0.662207208814 35 | 36 | 12.2339933078 37 | 6.19519532978 38 | 0.270901147706 39 | 7.15760560496 40 | 28.6468517887 41 | 9.37847479006 42 | 24.7712935913 43 | 5.13874018895 44 | 0.6622072088 45 | 14.9251948475 46 | 12.2339933078 47 | 48 | 6.19519532978 49 | 0.270901147706 50 | 7.15760560496 51 | 28.6468517887 52 | 9.37847479006 53 | 24.7712935913 54 | 5.13874018895 55 | 0.6622072088 56 | 14.9251948475 57 | 12.2339933078 58 | 6.19519532978 59 | 0.270901147706 60 | 61 | 7.15760560496 62 | 28.6468517887 63 | 9.37847479006 64 | 24.7712935913 65 | 5.13874018895 66 | 0.6622072088 67 | 14.9251948475 68 | 12.233933078 69 | 6.19519532978 70 | 0.270901147706 71 | 7.1576560496 72 | 28.6468517887 73 | 9.37847479006 74 | 75 | 9.3784747006 76 | 9.37847470061 77 | -------------------------------------------------------------------------------- /ch11/traceCLA.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "runtime/trace" 8 | "time" 9 | ) 10 | 11 | func main() { 12 | filename := path.Join(os.TempDir(), "traceCLA.out") 13 | f, err := os.Create(filename) 14 | if err != nil { 15 | panic(err) 16 | } 17 | defer f.Close() 18 | 19 | err = trace.Start(f) 20 | if err != nil { 21 | fmt.Println(err) 22 | return 23 | } 24 | defer trace.Stop() 25 | 26 | for i := 0; i < 3; i++ { 27 | s := make([]byte, 50000000) 28 | if s == nil { 29 | fmt.Println("Operation failed!") 30 | } 31 | } 32 | 33 | for i := 0; i < 5; i++ { 34 | s := make([]byte, 100000000) 35 | if s == nil { 36 | fmt.Println("Operation failed!") 37 | } 38 | time.Sleep(time.Millisecond) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ch11/traceHTTP.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptrace" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | if len(os.Args) != 2 { 12 | fmt.Printf("Usage: URL\n") 13 | return 14 | } 15 | 16 | URL := os.Args[1] 17 | client := http.Client{} 18 | 19 | req, _ := http.NewRequest("GET", URL, nil) 20 | trace := &httptrace.ClientTrace{ 21 | GotFirstResponseByte: func() { 22 | fmt.Println("First response byte!") 23 | }, 24 | GotConn: func(connInfo httptrace.GotConnInfo) { 25 | fmt.Printf("Got Conn: %+v\n", connInfo) 26 | }, 27 | DNSDone: func(dnsInfo httptrace.DNSDoneInfo) { 28 | fmt.Printf("DNS Info: %+v\n", dnsInfo) 29 | }, 30 | ConnectStart: func(network, addr string) { 31 | fmt.Println("Dial start") 32 | }, 33 | ConnectDone: func(network, addr string, err error) { 34 | fmt.Println("Dial done") 35 | }, 36 | WroteHeaders: func() { 37 | fmt.Println("Wrote headers") 38 | }, 39 | } 40 | 41 | req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) 42 | fmt.Println("Requesting data from server!") 43 | _, err := http.DefaultTransport.RoundTrip(req) 44 | if err != nil { 45 | fmt.Println(err) 46 | return 47 | } 48 | 49 | _, err = client.Do(req) 50 | if err != nil { 51 | fmt.Println(err) 52 | return 53 | } 54 | // io.Copy(os.Stdout, response.Body) 55 | } 56 | -------------------------------------------------------------------------------- /ch11/walkAll.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | 8 | "github.com/gorilla/mux" 9 | ) 10 | 11 | func handler(w http.ResponseWriter, r *http.Request) { 12 | return 13 | } 14 | 15 | func (h notAllowedHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 16 | handler(rw, r) 17 | } 18 | 19 | type notAllowedHandler struct{} 20 | 21 | func main() { 22 | r := mux.NewRouter() 23 | 24 | r.NotFoundHandler = http.HandlerFunc(handler) 25 | notAllowed := notAllowedHandler{} 26 | r.MethodNotAllowedHandler = notAllowed 27 | 28 | // Register GET 29 | getMux := r.Methods(http.MethodGet).Subrouter() 30 | getMux.HandleFunc("/time", handler) 31 | getMux.HandleFunc("/getall", handler) 32 | getMux.HandleFunc("/getid", handler) 33 | getMux.HandleFunc("/logged", handler) 34 | getMux.HandleFunc("/username/{id:[0-9]+}", handler) 35 | 36 | // Register PUT 37 | // Update User 38 | putMux := r.Methods(http.MethodPut).Subrouter() 39 | putMux.HandleFunc("/update", handler) 40 | 41 | // Register POST 42 | // Add User + Login + Logout 43 | postMux := r.Methods(http.MethodPost).Subrouter() 44 | postMux.HandleFunc("/add", handler) 45 | postMux.HandleFunc("/login", handler) 46 | postMux.HandleFunc("/logout", handler) 47 | 48 | // Register DELETE 49 | // Delete User 50 | deleteMux := r.Methods(http.MethodDelete).Subrouter() 51 | deleteMux.HandleFunc("/username/{id:[0-9]+}", handler) 52 | 53 | err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { 54 | pathTemplate, err := route.GetPathTemplate() 55 | if err == nil { 56 | fmt.Println("ROUTE:", pathTemplate) 57 | } 58 | pathRegexp, err := route.GetPathRegexp() 59 | if err == nil { 60 | fmt.Println("Path regexp:", pathRegexp) 61 | } 62 | qT, err := route.GetQueriesTemplates() 63 | if err == nil { 64 | fmt.Println("Queries templates:", strings.Join(qT, ",")) 65 | } 66 | qRegexps, err := route.GetQueriesRegexp() 67 | if err == nil { 68 | fmt.Println("Queries regexps:", strings.Join(qRegexps, ",")) 69 | } 70 | methods, err := route.GetMethods() 71 | if err == nil { 72 | fmt.Println("Methods:", strings.Join(methods, ",")) 73 | } 74 | fmt.Println() 75 | return nil 76 | }) 77 | 78 | if err != nil { 79 | fmt.Println(err) 80 | } 81 | 82 | http.Handle("/", r) 83 | } 84 | -------------------------------------------------------------------------------- /ch13/allowed.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Same[T comparable](a, b T) bool { 8 | if a == b { 9 | return true 10 | } 11 | return false 12 | } 13 | 14 | func main() { 15 | fmt.Println("4 = 3 is", Same(4,3)) 16 | fmt.Println("aa = aa is", Same("aa","aa")) 17 | fmt.Println("4.1 = 4.15 is", Same(4.1,4.15)) 18 | } 19 | -------------------------------------------------------------------------------- /ch13/hw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func PrintSlice[T any](s []T) { 8 | for _, v := range s { 9 | fmt.Print(v, " ") 10 | } 11 | fmt.Println() 12 | } 13 | 14 | func main() { 15 | PrintSlice([]int{1, 2, 3}) 16 | PrintSlice([]string{"a", "b", "c"}) 17 | PrintSlice([]float64{1.2, -2.33, 4.55}) 18 | } 19 | -------------------------------------------------------------------------------- /ch13/interfaces.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Numeric interface { 8 | int | int8 | int16 | int32 | int64 | float64 9 | } 10 | 11 | func Print(s interface{}) { 12 | // type switch 13 | switch s.(type) { 14 | case int: 15 | fmt.Println(s.(int)+1) 16 | case float64: 17 | fmt.Println(s.(float64)+1) 18 | default: 19 | fmt.Println("Unknown data type!") 20 | } 21 | } 22 | 23 | func PrintGenerics[T any](s T) { 24 | fmt.Println(s) 25 | } 26 | 27 | func PrintNumeric[T Numeric](s T) { 28 | fmt.Println(s+1) 29 | } 30 | 31 | func main() { 32 | Print(12) 33 | Print(-1.23) 34 | Print("Hi!") 35 | 36 | PrintGenerics(1) 37 | PrintGenerics("a") 38 | PrintGenerics(-2.33) 39 | 40 | PrintNumeric(1) 41 | PrintNumeric(-2.33) 42 | } 43 | -------------------------------------------------------------------------------- /ch13/newDT.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "errors" 6 | ) 7 | 8 | type TreeLast[T any] []T 9 | 10 | func (t TreeLast[T]) replaceLast(element T) (TreeLast[T], error) { 11 | if len(t) == 0 { 12 | return t, errors.New("This is empty!") 13 | } 14 | 15 | t[len(t) - 1] = element 16 | return t, nil 17 | } 18 | 19 | func main() { 20 | tempStr := TreeLast[string]{"aa", "bb"} 21 | fmt.Println(tempStr) 22 | tempStr.replaceLast("cc") 23 | fmt.Println(tempStr) 24 | 25 | tempInt := TreeLast[int]{12, -3} 26 | fmt.Println(tempInt) 27 | tempInt.replaceLast(0) 28 | fmt.Println(tempInt) 29 | } 30 | -------------------------------------------------------------------------------- /ch13/numeric.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Numeric interface { 8 | type int, int8, int16, int32, int64, float64 9 | } 10 | 11 | func Add[T Numeric](a, b T) T { 12 | return a + b 13 | } 14 | 15 | func main() { 16 | fmt.Println("4 + 3 =", Add(4,3)) 17 | fmt.Println("4.1 + 3.2 =", Add(4.1,3.2)) 18 | } 19 | -------------------------------------------------------------------------------- /ch13/reflection.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func PrintReflection(s interface{}) { 9 | fmt.Println("** Reflection") 10 | val := reflect.ValueOf(s) 11 | 12 | if val.Kind() != reflect.Slice { 13 | return 14 | } 15 | 16 | for i := 0; i < val.Len(); i++ { 17 | fmt.Print(val.Index(i).Interface(), " ") 18 | } 19 | fmt.Println() 20 | } 21 | 22 | func PrintSlice[T any](s []T) { 23 | fmt.Println("** Generics") 24 | for _, v := range s { 25 | fmt.Print(v, " ") 26 | } 27 | fmt.Println() 28 | } 29 | 30 | func main() { 31 | PrintSlice([]int{1, 2, 3}) 32 | PrintSlice([]string{"a", "b", "c"}) 33 | PrintSlice([]float64{1.2, -2.33, 4.55}) 34 | 35 | PrintReflection([]int{1, 2, 3}) 36 | PrintReflection([]string{"a", "b", "c"}) 37 | PrintReflection([]float64{1.2, -2.33, 4.55}) 38 | } 39 | -------------------------------------------------------------------------------- /ch13/structures.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type node[T any] struct { 8 | Data T 9 | next *node[T] 10 | } 11 | 12 | 13 | type list[T any] struct { 14 | start *node[T] 15 | } 16 | 17 | 18 | func (l *list[T]) add(data T) { 19 | n := node[T]{ 20 | Data: data, 21 | next: nil, 22 | } 23 | 24 | if l.start == nil { 25 | l.start = &n 26 | return 27 | } 28 | 29 | if l.start.next == nil { 30 | l.start.next = &n 31 | return 32 | } 33 | 34 | temp := l.start 35 | l.start = l.start.next 36 | l.add(data) 37 | l.start = temp 38 | } 39 | 40 | func main() { 41 | var myList list[int] 42 | fmt.Println(myList) 43 | myList.add(12) 44 | myList.add(9) 45 | myList.add(3) 46 | myList.add(9) 47 | 48 | // Print all elements 49 | for { 50 | fmt.Println("*", myList.start) 51 | if myList.start == nil { 52 | break 53 | } 54 | myList.start = myList.start.next 55 | } 56 | } 57 | --------------------------------------------------------------------------------