# Cheat Sheet

### Naming Convention

Thanks to: <https://medium.com/@kdnotes/golang-naming-rules-and-conventions-8efeecd23b68>

#### **Files**

1. Go follows a convention where source files are all lower case with underscore separating multiple words.
2. Compound file names are separated with \_
3. File names that begin with “.” or “\_” are ignored by the go tool
4. Files with the suffix `_test.go` are only compiled and run by the `go test` tool.

#### **Functions**

The case defines if it will be public or private for the package.

* Private: `func writeToDb(){}`
* Public: `func WriteToDb(){}`

The same for `struct properties`.

#### Constants

Constant should use all capital letters and use underscore `_` to separate words.\
Ex: `const CONNECTION_URL := "...."`

#### Variables

1. Generally, use a relatively simple (short) name.
2. Consistent naming style should be used the entire source code

`user` to `u`

`userID` to `uid`

* If variable type is `bool`, its name should start with `Has`, `Is`, `Can` or `Allow`, etc.
* A single letter represents index: `i, j, k`

### Variables

```go
// DECLARATIONS
var name string
name = "Thor"

name := "Thor"

// ARRAYS
var numbers [3]int
numbers[0] = 1

// SLICES
numbers := []int{}
numbers = append(numbers, 2)
```

### IF

```go
name := "thor"

if name == "thor" {
	
} else if name == "odin" {

} else {
	
}
```

### FOR

```go
names := []string{
	"Thor", "Odin", "Loki",
}

for i := 0; i < len(names); i++ {
	fmt.Println(i, ":", names[i])
}

for i, v := range names {
	fmt.Println(i, ":", v)
}
```

### SWITCH

```go
switch os := runtime.GOOS; os {
case "darwin":
	fmt.Println("OS X.")
case "linux":
	fmt.Println("Linux.")
default:
	// freebsd, openbsd,
	// plan9, windows...
	fmt.Printf("%s.\n", os)
}
```

### FUNCTION

```go
func simple() {
	fmt.Println("simple function")
}

func withReturnValue() string {
	return "Function with return value"
}

func withArgs(myArg string) {
	fmt.Println(myArg)
}

func withMultipleArgs(myList ...string) {
	// withMultipleArgs("Val1", "Val2", "ValN")
}

func withToupleResult() (value int, err error) {
	// value, err = withToupleResult()
	return 1, nil
}
```

```go
// FUNCTION AS PARAMETER
type Fn func()

func onError(handler Fn) {
	handler()
}
```

### Folders

```go
package main

import (
    "log"
    "os"
)

func main() {
    // Create a folder/directory at a full qualified path
    err := os.Mkdir("/Users/temp", 0755)
    if err != nil {
        log.Fatal(err)
    }

    // Create a folder recursively
    err := os.MkdirAll(dirPath, 0755)
	  if err != nil {
		    log.Fatal(err)
    }
    
    // Get current dir
    currentDirPath, err := os.Getwd()
}
```

### Read File

```go
content, _ := ioutil.ReadFile("list.txt")
fmt.Println(string(content))
```

### Write File

```go
fileName := "new-file.txt"

flags := os.O_WRONLY | os.O_CREATE | os.O_APPEND
file, _ := os.OpenFile(fileName, flags, 0666)

file.WriteString("This is a new file")
file.Close()
```

### Error Handling

```go
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
)

func main() {
	fileName := "list.txt"

	file, err := os.Open(fileName)
	handleError(err)

	reader := bufio.NewReader(file)

	var lines []string

	closeFile := func() {
		file.Close()
	}

	for {
		line, err := reader.ReadString('\n')
		if err == io.EOF {
			closeFile()
			break
		}
		handleErrorWithCallback(err, closeFile)
		line = strings.TrimSpace(line)
		lines = append(lines, line)
	}

	fmt.Println(lines)
}

type ErrorHandlerCallback func()

func handleError(err error) {
	if err != nil {
		panic(err)
	}
}

func handleErrorWithCallback(err error, handler ErrorHandlerCallback) {
	if err != nil {
		handler()
		panic(err)
	}
}

```

### Date and Time

Time Format: <https://golang.org/src/time/format.go>

```go
package main

import (
	"fmt"
	"time"
)

func main() {
	// dd/mm/yyyy hh:mm:ss
	now := time.Now().Format("02/01/2006 15:04:05")
	fmt.Println(now)
}

```

### Structs

```go
type User struct {
	email    string
	password string
	enabled  bool
}

// adding a Disable function to a user instance
func (u *User) Disable() {
	u.enabled = false
}

func main() {

	admin := User{
		email:    "admin@admin.com",
		password: "Admin123",
		enabled:  true,
	}

	guest := new(User)
	guest.email = "guest@guest.com"
}
```

### Creating Packages

```go
//
// playground/user/user.go
//
package user

type User struct {
	Email    string
	Password string
	Enabled  bool
}

func (u *User) Disable() {
	u.Enabled = false
}


//
// playground/main.go
//
package main

import (
	"fmt"
	"playground/user"
)

func main() {
	admin := user.User{
		Email:    "admin@admin.com",
		Password: "Admin123",
		Enabled:  true,
	}

	fmt.Println(admin)
}

```

### Pointers

```go
// CODE SAMPLE
package main

import "fmt"

func main() {
	user := User{Enabled: true}
	fmt.Println(user)

	disableUser(user)
	fmt.Println(user)
}

type User struct {
	Enabled bool
}

func disableUser(user User) {
	user.Enabled = false
}

// OUTPUT
{true}
{true}

/*
	This will not change the user because the function
	disableUser is recieving a user as a value, not
	a pointer to the same user in memory.
	To change the original object, we need do two things:
	
	1. Change the function to receive a pointer.
	2. Call the function sending the pointer.
	
	And here is the magic words:
	- Use * to receive a pointer
	- Use & to send a pointer
*/
func main() {
	user := User{Enabled: true}
	fmt.Println(user)

	disableUser(&user) // <=== HERE
	fmt.Println(user)
}

type User struct {
	Enabled bool
}

func disableUser(user *User /* <=== HERE*/) {
	user.Enabled = false
}

// OUTPUT NOW
{true}
{false}
```

### Interfaces

```go
func main() {
	user := User{Status: ACTIVE}
	fmt.Println(user)
	setStatus(&user, INACTIVE)
	fmt.Println(user)
}

type Status int

const (
	ACTIVE Status = iota
	INACTIVE
	DELETED
)

/*
	An interface define all methods an struct needs to
	implement to be compliance with the definition.
*/
type WithStatus interface {
	SetStatus(status Status)
}

func setStatus(entity WithStatus, status Status) {
	entity.SetStatus(status)
}


/*
	If we want to pass a user instance to the setStatus func,
	the User struct must implement all the methods the
	WithStatus interface require.
*/
type User struct {
	Status Status
}

func (user *User) SetStatus(status Status) {
	user.Status = status
}


```

### Enum

```go
type Status int

const (
	ACTIVE Status = iota
	INACTIVE
	DELETED
)
```

### WaitGroup

```go
/*
	When the application runs multiple functions
	in background, if you want to deal with the
	concurrence and know when is finish, use the
	WaitGroup from sync package.
*/

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup // create the group

func main() {
	wg.Add(1) // indicating a new work will be add
	go say("Hi")
	wg.Wait() // wait until all the work done
}

func say(text string) {
	fmt.Printf(text)
	wg.Done() // mark a work for done
}
```

### Defer

```go
/*
	When we want to run something after the function finishes
	(even for an error, for example), we can use defer.
*/
package main

import (
	"fmt"
)

func main() {
	defer finish()
	fmt.Println("Enf of main")
}

func finish() {
	fmt.Println("We are done!")
}

/*
	=== OUTPUT ===
	Enf of main
  We are done!
*/
```

### Panic and Recover

```go
/*
	When something goes wrong and a panic is raise, 
	has a function that you can call and prevent the
	software to stop work.
	Its called recover.
*/

package main

import (
	"fmt"
)

func main() {
	defer finish()
	defer cleanup()
	fmt.Println("Enf of main")
	panic("fuuuuuuck") // <=== it should kill the program, right?
}

func cleanup() {
	if r := recover(); r != nil { // <=== but here we are recovering =)
		fmt.Println("recover: ", r)
	}
}

func finish() {
	fmt.Println("We are done!")
}

```

### Channels and Concurrency

Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine.

Youtube:

* <https://www.youtube.com/watch?v=LvgVSSpwND8>

```go
package main

import (
	"fmt"
)

func main() {
	channel := make(chan int)

	go sum(channel, 5, 8)

	result := <-channel

	close(channel) // close the channel after no more work needed

	fmt.Println(result)
}

func sum(c chan int, a int, b int) {
	c <- a + b
}
```

### Unit Test

```go
// my_file_test.go
package my_package

import "testing"

func TestCrawler(t *testing.T) {
	result := my_func()
	
	if result == nil {
		t.Error("Ooops! This is the error message") // to indicate test failed
	}
}
```
