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
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())
}
}
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)
}
}
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?