diff --git a/model/birthdays.go b/model/birthdays.go new file mode 100644 index 0000000..c18b3f2 --- /dev/null +++ b/model/birthdays.go @@ -0,0 +1,53 @@ +package model + +import ( + "fmt" + "strconv" + "strings" +) + +type Birthday struct { + Name string + Email string + Phone string + YearOfBirth int + MonthOfBirth int + DayOfBirth int +} + +func NewBirthdayFromRecord(record []string) (*Birthday, error) { + if len(record) != 4 { + return nil, fmt.Errorf("invalid length %d for record", len(record)) + } + + dateComponents := strings.Split(strings.TrimSpace(record[3]), "/") + if len(dateComponents) != 3 { + return nil, fmt.Errorf("invalid date format in %q", strings.TrimSpace(record[3])) + } + + dayOfBirth, err := strconv.Atoi(dateComponents[0]) + if err != nil { + return nil, fmt.Errorf("error parsing day of birth from %q: %w", dateComponents[0], err) + } + + monthOfBirth, err := strconv.Atoi(dateComponents[1]) + if err != nil { + return nil, fmt.Errorf("error parsing month of birth from %q: %w", dateComponents[1], err) + } + + yearOfBirth, err := strconv.Atoi(dateComponents[2]) + if err != nil { + return nil, fmt.Errorf("error parsing year of birth from %q: %w", dateComponents[2], err) + } + + b := &Birthday{ + Name: strings.TrimSpace(record[0]), + Email: strings.TrimSpace(record[1]), + Phone: strings.TrimSpace(record[2]), + YearOfBirth: yearOfBirth, + MonthOfBirth: monthOfBirth, + DayOfBirth: dayOfBirth, + } + + return b, nil +} diff --git a/model/birthdays_test.go b/model/birthdays_test.go new file mode 100644 index 0000000..e26982d --- /dev/null +++ b/model/birthdays_test.go @@ -0,0 +1,44 @@ +package model + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewBirthdayFromRecord(t *testing.T) { + t.Run("should correctly parse different records", func(t *testing.T) { + testCases := []struct { + Record []string + ExpectedName string + ExpectedEmail string + ExpectedPhone string + ExpectedYearOfBirth int + ExpectedMonthOfBirth int + ExpectedDayOfBirth int + }{ + { + Record: []string{"John Doe ", "john@doe.com", " 123456789", "17/04/2192"}, + ExpectedName: "John Doe", + ExpectedEmail: "john@doe.com", + ExpectedPhone: "123456789", + ExpectedYearOfBirth: 2192, + ExpectedMonthOfBirth: 4, + ExpectedDayOfBirth: 17, + }, + } + + for _, tc := range testCases { + t.Run(tc.ExpectedName, func(t *testing.T) { + b, err := NewBirthdayFromRecord(tc.Record) + require.NoError(t, err) + require.Equal(t, tc.ExpectedName, b.Name) + require.Equal(t, tc.ExpectedEmail, b.Email) + require.Equal(t, tc.ExpectedPhone, b.Phone) + require.Equal(t, tc.ExpectedYearOfBirth, b.YearOfBirth) + require.Equal(t, tc.ExpectedMonthOfBirth, b.MonthOfBirth) + require.Equal(t, tc.ExpectedDayOfBirth, b.DayOfBirth) + }) + } + }) +} diff --git a/parser/csv_parser.go b/parser/csv_parser.go new file mode 100644 index 0000000..c496eab --- /dev/null +++ b/parser/csv_parser.go @@ -0,0 +1,42 @@ +package parser + +import ( + "encoding/csv" + "errors" + "fmt" + "io" + "os" + + "git.ctrlz.es/mgdelacroix/birthdaybot/model" +) + +func ParseCSV(path string) ([]*model.Birthday, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + reader := csv.NewReader(f) + + birthdays := []*model.Birthday{} + lineno := 0 + for { + lineno++ + record, err := reader.Read() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, fmt.Errorf("error reading lineno %d: %w", lineno, err) + } + + birthday, err := model.NewBirthdayFromRecord(record) + if err != nil { + return nil, fmt.Errorf("error parsing record for line %d into birthday: %w", lineno, err) + } + birthdays = append(birthdays, birthday) + } + + return birthdays, nil +} diff --git a/server/server.go b/server/server.go index 9ce8b17..f7f3ca1 100644 --- a/server/server.go +++ b/server/server.go @@ -1,4 +1,11 @@ package server -type Server struct { +import ( + "git.ctrlz.es/mgdelacroix/birthdaybot/model" +) + +type Server struct{} + +func New(config *model.Config) (*Server, error) { + return nil, nil }