diff --git a/go/weather-forecast/HELP.md b/go/weather-forecast/HELP.md new file mode 100644 index 0000000..6047b12 --- /dev/null +++ b/go/weather-forecast/HELP.md @@ -0,0 +1,41 @@ +# Help + +## Running the tests + +To run the tests run the command `go test` from within the exercise directory. + +If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem` +flags: + + go test -v --bench . --benchmem + +Keep in mind that each reviewer will run benchmarks on a different machine, with +different specs, so the results from these benchmark tests may vary. + +## Submitting your solution + +You can submit your solution using the `exercism submit weather_forecast.go` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Go track's documentation](https://exercism.org/docs/tracks/go) +- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +To get help if you're having trouble, you can use one of the following resources: + +- [How to Write Go Code](https://golang.org/doc/code.html) +- [Effective Go](https://golang.org/doc/effective_go.html) +- [Go Resources](http://golang.org/help) +- [StackOverflow](http://stackoverflow.com/questions/tagged/go) \ No newline at end of file diff --git a/go/weather-forecast/HINTS.md b/go/weather-forecast/HINTS.md new file mode 100644 index 0000000..484da62 --- /dev/null +++ b/go/weather-forecast/HINTS.md @@ -0,0 +1,20 @@ +# Hints + +## General + +- A [documentation comment][comment] should be written directly before the entity that it is describing, start with the name of what it is describing, take the form of a sentence, and end with a period. + +## 1. Document package weather + +- The [package comment][comment] should be written directly before the package, start with `Package x`, and end with a period. + +## 2. Document the CurrentCondition and CurrentLocation variables + +- The [variable comment][variable comment] should be written right before the variable that it is describing, start with its name, and end with a period. + +## 3. Document the Forecast() function + +- The [function comment][comment] should come directly before the function, start with the name of the function and end with a period. + +[comment]: https://golang.org/doc/effective_go.html#commentary +[variable comment]: https://dave.cheney.net/practical-go/presentations/qcon-china.html#_comments \ No newline at end of file diff --git a/go/weather-forecast/README.md b/go/weather-forecast/README.md new file mode 100644 index 0000000..338d3dd --- /dev/null +++ b/go/weather-forecast/README.md @@ -0,0 +1,66 @@ +# Weather Forecast + +Welcome to Weather Forecast on Exercism's Go Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +In the previous exercise, we saw that there are two ways to write comments in Go: single-line comments that are preceded by `//`, and multiline comment blocks that are wrapped with `/*` and `*/`. + +## Documentation comments + +In Go, comments play an important role in documenting code. They are used by the `godoc` command, which extracts these comments to create documentation about Go packages. A documentation comment should be a complete sentence that starts with the name of the thing being described and ends with a period. + +Comments should precede packages as well as exported identifiers, for example exported functions, methods, package variables, constants, and structs, which you will learn more about in the next exercises. + +A package-level variable can look like this: + +```go +// TemperatureCelsius represents a certain temperature in degrees Celsius. +var TemperatureCelsius float64 +``` + +## Package comments + +Package comments should be written directly before a package clause (`package x`) and begin with `Package x ...` like this: + +```go +// Package kelvin provides tools to convert +// temperatures to and from Kelvin. +package kelvin +``` + +## Function comments + +A function comment should be written directly before the function declaration. It should be a full sentence that starts with the function name. For example, an exported comment for the function `Calculate` should take the form `Calculate ...`. It should also explain what arguments the function takes, what it does with them, and what its return values mean, ending in a period): + +```go +// CelsiusFreezingTemp returns an integer value equal to the temperature at which water freezes in degrees Celsius. +func CelsiusFreezingTemp() int { + return 0 +} +``` + +## Instructions + +Goblinocus is a country that takes its weather forecast very seriously. Since you are a renowned, responsible and proficient developer, they asked you to write a program that can forecast the current weather condition of various cities in Goblinocus. You were busy at the time and asked one of your friends to do the job instead. After a while, the president of Goblinocus contacted you and said they do not understand your friend's code. When you check the code, you discover that your friend did not act as a responsible programmer and there are no comments in the code. You feel obligated to clarify the program so goblins can understand them as well. + +## 1. Document package weather + +Since goblins are not as smart as you are, they forgot what the package should do for them. Please write a comment for `package weather` that describes its contents. The package comment should introduce the package and provide information relevant to the package as a whole. + +## 2. Document the CurrentCondition and CurrentLocation variables + +The president of Goblinocus is a bit paranoid and fears uncommented variables are used to destroy their country. Please clarify the usage of the package variables `CurrentCondition` and `CurrentLocation` and put the president's mind at ease. This should tell any user of the package what information the variables store, and what they can do with it. + +## 3. Document the Forecast() function + +Goblinocus forecast operators want to know what the `Forecast()` function does (but do not tell them how it works, since unfortunately, they will get more confused). Please write a comment for this function that describes what the function does, but not how it does it. + +## Source + +### Created by + +- @nikimanoledaki +- @micuffaro \ No newline at end of file diff --git a/go/weather-forecast/go.mod b/go/weather-forecast/go.mod new file mode 100644 index 0000000..cbea8d2 --- /dev/null +++ b/go/weather-forecast/go.mod @@ -0,0 +1,3 @@ +module weather + +go 1.18 \ No newline at end of file diff --git a/go/weather-forecast/weather_forecast.go b/go/weather-forecast/weather_forecast.go new file mode 100644 index 0000000..cf6a82f --- /dev/null +++ b/go/weather-forecast/weather_forecast.go @@ -0,0 +1,13 @@ +// Package weather_forecast provides a function to get the current weather condition for a given city. +package weather + +// CurrentCondition returns the current weather condition for a given city. +var CurrentCondition string +// CurrentLocation is the city for which the weather condition is being reported. +var CurrentLocation string + +// Forecast returns the current weather condition for a given city. +func Forecast(city, condition string) string { + CurrentLocation, CurrentCondition = city, condition + return CurrentLocation + " - current weather condition: " + CurrentCondition +} diff --git a/go/weather-forecast/weather_forecast_test.go b/go/weather-forecast/weather_forecast_test.go new file mode 100644 index 0000000..d8596b8 --- /dev/null +++ b/go/weather-forecast/weather_forecast_test.go @@ -0,0 +1,140 @@ +package weather + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "strings" + "testing" +) + +func TestComments(t *testing.T) { + filename := "weather_forecast.go" + + fs := token.NewFileSet() + f, err := parser.ParseFile(fs, filename, nil, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + + wantedComments := 4 + got := len(f.Comments) + if got != wantedComments { + t.Errorf("Incorrect number of comments: got %d, want %d", got, wantedComments) + } + + testPackageComment(t, f) + + ast.Inspect(f, func(node ast.Node) bool { + switch n := node.(type) { + case *ast.GenDecl: + if n.Lparen.IsValid() { + for _, v := range n.Specs { + testBlockIdentifierComment(t, v.(*ast.ValueSpec)) + } + } else { + testIdentifierComment(t, n) + } + case *ast.FuncDecl: + testFunctionComment(t, n) + } + return true + }) +} + +func testPackageComment(t *testing.T, node *ast.File) { + t.Helper() + if node.Doc == nil { + t.Errorf("Package weather should have a comment") + } + + packageName := node.Name.Name + want := "Package " + packageName + packageComment := node.Doc.Text() + + if ok, errStr := testComment("Package", packageName, packageComment, want); !ok { + t.Error(errStr) + } + +} +func testIdentifierComment(t *testing.T, node *ast.GenDecl) { + t.Helper() + + identifierName := node.Specs[0].(*ast.ValueSpec).Names[0].Name + if node.Doc == nil { + t.Errorf("Exported identifier %s should have a comment", identifierName) + } + + identifierComment := node.Doc.Text() + want := identifierName + + if ok, errStr := testComment("Variable", identifierName, identifierComment, want); !ok { + t.Error(errStr) + } +} + +func testBlockIdentifierComment(t *testing.T, node *ast.ValueSpec) { + t.Helper() + + identifierName := node.Names[0].Name + if node.Doc == nil { + t.Errorf("Exported identifier %s should have a comment", identifierName) + } + + identifierComment := node.Doc.Text() + want := identifierName + + if ok, errStr := testComment("Variable", identifierName, identifierComment, want); !ok { + t.Error(errStr) + } + +} + +func testFunctionComment(t *testing.T, node *ast.FuncDecl) { + t.Helper() + funcName := node.Name.Name + if node.Doc == nil { + t.Errorf("Exported function %s() should have a comment", funcName) + } + + funcComment := node.Doc.Text() + want := funcName + + if ok, errStr := testComment("Function", funcName, funcComment, want); !ok { + t.Error(errStr) + } +} + +func testComment(entityKind, entityName, comment, wantedPrefix string) (ok bool, errString string) { + + trimmedComment := strings.TrimSpace(comment) + lowerEntity := strings.ToLower(entityKind) + + // Check if comment has wanted prefix + if !strings.HasPrefix(trimmedComment, wantedPrefix) { + errorString := fmt.Sprintf("%s comment for %s '%s' should start with '// %s ...': got '// %s'", + entityKind, lowerEntity, entityName, wantedPrefix, trimmedComment) + return false, errorString + } + + // Check if comment content is empty + commentContent := strings.TrimPrefix(trimmedComment, wantedPrefix) + commentContent = strings.TrimSpace(commentContent) + commentContent = strings.TrimSuffix(commentContent, ".") + + if commentContent == "" { + lowerEntity := strings.ToLower(entityKind) + errorString := fmt.Sprintf("%s comment of '%s' should provide a description of the %s, e.g '// %s <%s_description>'", + entityKind, entityName, lowerEntity, wantedPrefix, lowerEntity) + return false, errorString + } + + // Check if comment ends in a period + if !strings.HasSuffix(trimmedComment, ".") { + return false, fmt.Sprintf("%s comment for %s '%s' should end with a period (.)", + entityKind, lowerEntity, entityName) + } + + return true, "" +}