├── .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 |
--------------------------------------------------------------------------------