My first go at go... would you do anything different?

taijidude - Apr 8 '22 - - Dev Community

Hello for a slightly longer article. :-) I had my first try at go program and wanted to share what i have learned.

The tool: I started using obsidian notes and i'am building my self and electronic bullet journal.

Here are the links. But i will probably write about this in a later article:

https://obsidian.md/
https://bulletjournal.com/

In the bullet journal method you have a monthly overview of things. This is basicly a list of you of the number of the day and the weekday besides it. You use this list to write notes about your appointments for that month down.

Example:
01 - sa
02 - su 3 pm cake and Coffee
03 - mo

It's a little tedious to write this down by hand and the use case is easy enough to try this in a new language. I wanted to create a cli tool, which prints a list of days and their respective weekdays for the month i give as parameter when executing the tool.

Example:

.\printMonths.exe -month=january
----january----
01 - sa
02 - su
03 - mo
04 - tu
05 - we
06 - th
07 - fr
08 - sa
09 - su
10 - mo
11 - tu
12 - we
13 - th
14 - fr
15 - sa
16 - su
17 - mo
18 - tu
19 - we
20 - th
21 - fr
22 - sa
23 - su
24 - mo
25 - tu
26 - we
27 - th
28 - fr
29 - sa
30 - su
31 - mo
Enter fullscreen mode Exit fullscreen mode

So here is, what i came up with...

package main

import (
    "errors"
    "flag"
    "fmt"
)

const (
    MONDAY    string = "monday"
    TUESDAY          = "tuesday"
    WEDNESDAY        = "wednesday"
    THURSDAY         = "thursday"
    FRIDAY           = "friday"
    SATURDAY         = "saturday"
    SUNDAY           = "sunday"
)

// Struct to encapsulate the names of the weekdays and
// possibly use them in functions
type WeekData struct {
    days [7]string
}

//this struct holds the data needed to start the print of the days
type MonthData struct {
    numberOfDays int
    startDay     string
    weekData     WeekData
}

//logic for the printing of the days
func (m MonthData) print() string {
    result := ""
    currentWeekday := m.startDay
    for day := 1; day <= m.numberOfDays; day++ {
        result = result + fmt.Sprintf("%02d", day) + " - " + currentWeekday[0:2] + "\n"
        var err error
        currentWeekday, err = m.weekData.next(currentWeekday)
        if err != nil {
            fmt.Println(err)
            return ""
        }
    }
    return result
}

//gets you the next day
func (w WeekData) next(current string) (string, error) {
    if current == SUNDAY {
        return MONDAY, nil
    }
    for index, day := range w.days {
        if day == current {
            return w.days[index+1], nil
        }
    }
    return "", errors.New("Day " + current + " not found")
}

func main() {
    //the weekdata struct is used in the month data struct
    wd := WeekData{[7]string{MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}}

    //a number of month data structs mapped to the name of the months
    data := map[string]MonthData{
        "january":   MonthData{numberOfDays: 31, startDay: SATURDAY, weekData: wd},
        "febuary":   MonthData{numberOfDays: 28, startDay: TUESDAY, weekData: wd},
        "march":     MonthData{numberOfDays: 31, startDay: TUESDAY, weekData: wd},
        "april":     MonthData{numberOfDays: 30, startDay: FRIDAY, weekData: wd},
        "mai":       MonthData{numberOfDays: 31, startDay: SUNDAY, weekData: wd},
        "june":      MonthData{numberOfDays: 30, startDay: WEDNESDAY, weekData: wd},
        "july":      MonthData{numberOfDays: 31, startDay: FRIDAY, weekData: wd},
        "august":    MonthData{numberOfDays: 31, startDay: MONDAY, weekData: wd},
        "september": MonthData{numberOfDays: 30, startDay: THURSDAY, weekData: wd},
        "october":   MonthData{numberOfDays: 31, startDay: SATURDAY, weekData: wd},
        "november":  MonthData{numberOfDays: 30, startDay: TUESDAY, weekData: wd},
        "december":  MonthData{numberOfDays: 31, startDay: THURSDAY, weekData: wd},
    }

    month := flag.String("month", "default", "month to pick")
    flag.Parse()
    fmt.Println("----" + *month + "----")
    if *month != "" {
        fmt.Print(data[*month].print())
    }
}

Enter fullscreen mode Exit fullscreen mode

And the Tests:

package main

import (
    "testing"
)

func TestNext(t *testing.T) {
    wd := WeekData{[7]string{MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}}

    nextDay, _ := wd.next(MONDAY)

    if nextDay != TUESDAY {
        t.Errorf("Expected '"+TUESDAY+"', got '%s'", nextDay)
    }
}

func TestPrint(t *testing.T) {

    wd := WeekData{[7]string{MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}}
    md := MonthData{numberOfDays: 1, startDay: SATURDAY, weekData: wd}

    result := md.print()
    if result != "01 - sa\n" {
        t.Errorf("Expected '01 - sa\n', got '%s'", result)
    }

    md = MonthData{numberOfDays: 3, startDay: SATURDAY, weekData: wd}
    result = md.print()
    if result != "01 - sa\n02 - su\n03 - mo\n" {
        t.Errorf("Expected '01 - sa\n02 - su\n03 - mo\n', got '%s'", result)
    }
}

Enter fullscreen mode Exit fullscreen mode

The lessons

  • I had some problems remembering that you can give back multiple values from function. This made stumble when i tried to nest function calls.

  • As far as i understand it the basic go testing package has no assertion. You have to make the test fail yourself.

  • In Java i would create an enum for the weekdays. Im not sure if i handled it the best way here.

  • I need to get a better understanding of pointers. I tried to use string pointers in the functions, to just have one string for each day. But i still get confused.

  • I was fun and go is starting to grow on me. At the first look a lot of convience/code sugar is missing, but i'm sure i need to get more into the language to understand the concepts behind it better and not just write java in go.

What would change in the code above?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player