Skip to content

Commit

Permalink
Merge pull request #4 from smyrman/patch
Browse files Browse the repository at this point in the history
update to use go.mod and GitHubActions
  • Loading branch information
smyrman authored Dec 2, 2022
2 parents 5cb2f49 + 880f078 commit f1628dd
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 109 deletions.
65 changes: 65 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Go
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.17
id: go

- name: Checkout
uses: actions/checkout@v3

- name: Setup Go modules cache
id: cache-go-module
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Setup Go build cache
id: cache-go-build
uses: actions/cache@v3
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-gobuild-${{ github.sha }}
restore-keys: |
${{ runner.os }}-gobuild-
- name: Download dependencies
if: steps.cache-go-module.outputs.cache-hit != 'true'
run: go mod download

- name: Verify go modules up to date
run: |
set -ex
go mod tidy
[ -z "$(git diff -- go.{mod,sum})" ] # Check there are no changes!
- name: Test
run: go test -race ./...

# Run golangci-lint in same job as tests as the golangci-lint task can not
# resolve private dependencies.
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.50
github-token: ${{ secrets.GITHUB_TOKEN }}
only-new-issues: false
skip-cache: false
skip-pkg-cache: true
skip-build-cache: true
args: --timeout=10m
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

3 changes: 0 additions & 3 deletions CHANGELOG.md

This file was deleted.

46 changes: 12 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.org/smyrman/units.svg?branch=master)](https://travis-ci.org/smyrman/units)
[![GitHub Actions](https://github.com/smyrman/units/workflows/Go/badge.svg?branch=master)](https://github.com/smyrman/units/actions?query=workflow%3AGo+branch%master)
[![Go Reference](https://pkg.go.dev/badge/github.com/smyrman/units.svg)](https://pkg.go.dev/github.com/smyrman/units)

# Units
Expand All @@ -8,27 +8,26 @@ Units is a small Go package that utilizes Go's strict type system to achieve bet
It currently has the following sub-packages:

- [angular](http://godoc.org/github.com/smyrman/units/angular) defines the types:
- Angle
- Velocity
- Acceleration
- Angle
- Velocity
- Acceleration
- [linear](https://godoc.org/github.com/smyrman/units/linear) define the types:
- Distance
- Velocity
- Acceleration
- Distance
- Velocity
- Acceleration
- [planar](https://godoc.org/github.com/smyrman/units/planar) defines the types:
- Point
- Area
- Point
- Area

Each type has defined constants for relevant units. In general, you don't need to worry about which unit the underlying values is stored in, as long as you **always multiply with a relevant uint** when you first set your value. E.g.:

```go
var a angular.Angle = 90 * angular.Degrees
```


## Use at your own risk ⚠️

This libary has seen low usage and has little testing. If you still prefer to use this package over other packages, contributions are still welcome.
This library has seen low usage and has little testing. If you still prefer to use this package over other packages, contributions are still welcome.

## Alternatives

Expand All @@ -50,18 +49,17 @@ Alternative units pacakges:
- [x] Modern Imperial units
- [ ] Appropriate test coverage per package.


## Why would I want to use a units package?

The [Mars Climate Orbiter](https://en.wikipedia.org/wiki/Mars_Climate_Orbiter) crashed into Mars when software producing the non-SI units of pound-seconds was used to generate input to a program expecting newton-seconds (N s). While the units package would not have prevented that particular issue of communication between programs, the goal is that it will help you to avoid unit related mistakes within your own program, and to make working with various units as straight-forward and painless as possible.

Considerting the low amount of usage of _this_ units package in particular, you may want to consider one that's more used though! Or make sure to add the number of tests that you need to be confident in your solution.
Considering the low amount of usage of _this_ units package in particular, you may want to consider one that's more used though! Or make sure to add the number of tests that you need to be confident in your solution.

## Our experience

We found an earlier version of this code surprisingly useful back in 2012, when we where developing an autonomous robot using Go for the high-level programming.

While we still used plain float64 values in the matrices that calculated the next system states, we found using the units package for the input and output values of various blocks extremely useful. This package helped us verify that we never did something stupid like passing a distance to a function that expected a velocity. The simplicity of using the return value from `.Normalize()`on an angle, also made it very easy for us to ensure normalized angles for our navigation system. Forgetting to normilize an angle could lead to spinning robots...
While we still used plain float64 values in the matrices that calculated the next system states, we found using the units package for the input and output values of various blocks extremely useful. This package helped us verify that we never did something stupid like passing a distance to a function that expected a velocity. The simplicity of using the return value from `.Normalize()`on an angle, also made it very easy for us to ensure normalized angles for our navigation system. Forgetting to normalize an angle could lead to spinning robots...

We used metric values for both internal and displaying purposes, but the aim is also that this package will help those who wish to provide non-metric units as input or display values for their programs.

Expand All @@ -87,23 +85,3 @@ func main() {
fmt.Printf("v = %.2f mph\n", v.MilesPerHour())
}
```

See the [godoc reference docs](http://godoc.org/github.com/smyrman/units) for a full list of methods per type.

## Migrating from v0.0.1

Since v0.0.1, the package layout has been changed. Type and constant definitions have been moved into sub-packages to allow for shorter names, and a more restricted scope from which package symmetry can arise.

To migrate, search and replace the following:

- `units.Distance` -> `linear.Distance`
- `units.Velocity` -> `linear.Velocity`
- `units.Angle` -> `angular.Angle`
- `units.AngularVelocity` -> `angular.Velocity`
- `units.Coordinate` -> `planar.Point`

Then, assure the proper imports, most conveniently through the use of [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports), or manually by replacing the "github.com/smyrman/units" import with the appropriate import(s):

- "github.com/smyrman/units/linear"
- "github.com/smyrman/units/angular"
- "github.com/smyrman/units/planar"
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/smyrman/units

go 1.17
11 changes: 7 additions & 4 deletions internal/cmd/units-codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

const helpText = `Usage:
units-codegen[ -o FILE] -t TYPE PKG_CONFIG_FILE
`

type outputVal struct {
Expand All @@ -26,7 +27,7 @@ func (o outputVal) String() string {

func (o *outputVal) Write(p []byte) (n int, err error) {
if o.w == nil {
return 0, errors.New("No writer configured")
return 0, errors.New("no writer configured")
}
return o.w.Write(p)
}
Expand All @@ -40,7 +41,7 @@ func (o *outputVal) Set(s string) error {
o.name = s
fd, err := os.Create(s)
if err != nil {
return fmt.Errorf("Could not create output file: %s", err.Error())
return fmt.Errorf("could not create output file: %s", err.Error())
}
o.w = fd
return nil
Expand All @@ -54,10 +55,12 @@ var options struct {

func init() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, helpText)
fmt.Fprint(os.Stderr, helpText)
flag.PrintDefaults()
}
options.output.Set("")
if err := options.output.Set(""); err != nil {
panic(err)
}
flag.Var(&options.output, "o", "Write to file instead of stdout")
flag.StringVar(&options.typeName, "t", "", "Type to generate code for (required)")
log.SetPrefix("units-codegen:")
Expand Down
60 changes: 3 additions & 57 deletions internal/cmd/units-codegen/template.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
_ "embed"
"fmt"
"regexp"
"strings"
Expand Down Expand Up @@ -156,60 +157,5 @@ var tmplFunctions = template.FuncMap{
"lower": strings.ToLower,
}

const tmpl = `// This code is generated by units-codegen; do not manualy edit this file.
{{$self := .Self}}{{$name := .Name}}
package {{.PkgName}}
{{if .Import}}import ({{range .Import}}
"{{.}}"{{end}}
){{end}}
// Units for {{.Name}} values. Always multiply with a unit when setting the initial value like you would for
// time.Time. This prevents you from having to worry about the internal storage format.
const ({{range .Units}}
{{.Name}} {{$name}} = {{.Value}}{{end}}
)
{{range .Units}}
// {{.NamePlural}} returns {{$self}} as a floating point number of {{.NamePlural|lower}}.
func ({{$self}} {{$name}}) {{.NamePlural}}() float64 {
return float64({{$self}} / {{.Name}})
}
{{end}}
// Abs returns the absolute value of {{.Self}} as a copy.
func ({{.Self}} {{.Name}}) Abs() {{.Name}} {
if {{.Self}} < 0 {
return -{{.Self}}
}
return {{.Self}}
}
// Mul returns the product of {{.Self}} * x as a new {{.Name}}.
func ({{.Self}} {{.Name}}) Mul(x float64) {{.Name}} {
return {{.Self}} * {{.Name}}(x)
}
// Div returns the quotient of {{.Self}} / x as a new {{.Name}}.
func ({{.Self}} {{.Name}}) Div(x float64) {{.Name}} {
return {{.Self}} / {{.Name}}(x)
}
// Div{{.Name}} returns the quotient of {{.Self}} / x as a floating point number.
func ({{.Self}} {{.Name}}) Div{{.Name}}(x {{.Name}}) float64 {
return float64({{.Self}} / x)
}
{{if .DivDurationType}}
// DivDuration returns the quotient of {{.Self}} / t as a {{.DivDurationType}}.
func ({{.Self}} {{.Name}}) DivDuration(t time.Duration) {{.DivDurationType}} {
return {{.DivDurationType}}(float64({{.Self}}) / float64(t))
}
// Div{{.DivDurationType}} returns the quotient of {{.Self}} / x as a time.Duration.
func ({{.Self}} {{.Name}}) Div{{.DivDurationType}}(x {{.DivDurationType}}) time.Duration {
return time.Duration(float64({{.Self}}) / float64(x))
}{{end}}
{{if .MulDurationType}}
// MulDuration returns the product of {{.Self}} * t as a {{.MulDurationType}}.
func ({{.Self}} {{.Name}}) MulDuration(t time.Duration) {{.MulDurationType}} {
return {{.MulDurationType}}(float64({{.Self}}) * float64(t))
}{{end}}
`
//go:embed template.go.tmpl
var tmpl string
56 changes: 56 additions & 0 deletions internal/cmd/units-codegen/template.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// This code is generated by units-codegen; do not manualy edit this file.
{{$self := .Self}}{{$name := .Name}}
package {{.PkgName}}

{{if .Import}}import ({{range .Import}}
"{{.}}"{{end}}
){{end}}

// Units for {{.Name}} values. Always multiply with a unit when setting the initial value like you would for
// time.Time. This prevents you from having to worry about the internal storage format.
const ({{range .Units}}
{{.Name}} {{$name}} = {{.Value}}{{end}}
)
{{range .Units}}
// {{.NamePlural}} returns {{$self}} as a floating point number of {{.NamePlural|lower}}.
func ({{$self}} {{$name}}) {{.NamePlural}}() float64 {
return float64({{$self}} / {{.Name}})
}
{{end}}
// Abs returns the absolute value of {{.Self}} as a copy.
func ({{.Self}} {{.Name}}) Abs() {{.Name}} {
if {{.Self}} < 0 {
return -{{.Self}}
}
return {{.Self}}
}

// Mul returns the product of {{.Self}} * x as a new {{.Name}}.
func ({{.Self}} {{.Name}}) Mul(x float64) {{.Name}} {
return {{.Self}} * {{.Name}}(x)
}

// Div returns the quotient of {{.Self}} / x as a new {{.Name}}.
func ({{.Self}} {{.Name}}) Div(x float64) {{.Name}} {
return {{.Self}} / {{.Name}}(x)
}

// Div{{.Name}} returns the quotient of {{.Self}} / x as a floating point number.
func ({{.Self}} {{.Name}}) Div{{.Name}}(x {{.Name}}) float64 {
return float64({{.Self}} / x)
}
{{if .DivDurationType}}
// DivDuration returns the quotient of {{.Self}} / t as a {{.DivDurationType}}.
func ({{.Self}} {{.Name}}) DivDuration(t time.Duration) {{.DivDurationType}} {
return {{.DivDurationType}}(float64({{.Self}}) / float64(t))
}

// Div{{.DivDurationType}} returns the quotient of {{.Self}} / x as a time.Duration.
func ({{.Self}} {{.Name}}) Div{{.DivDurationType}}(x {{.DivDurationType}}) time.Duration {
return time.Duration(float64({{.Self}}) / float64(x))
}{{end}}
{{if .MulDurationType}}
// MulDuration returns the product of {{.Self}} * t as a {{.MulDurationType}}.
func ({{.Self}} {{.Name}}) MulDuration(t time.Duration) {{.MulDurationType}} {
return {{.MulDurationType}}(float64({{.Self}}) * float64(t))
}{{end}}
5 changes: 1 addition & 4 deletions internal/floattest/floattest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import (
func AlmostEqual(x, y float64) bool {
const tolerance = 1e-11

if math.Abs(x-y) < tolerance {
return true
}
return false
return math.Abs(x-y) < tolerance
}

// CompareCase should be used for Go >= 1.7 sub-tests that compares a returned float64 value from a function (Func)
Expand Down
4 changes: 2 additions & 2 deletions linear/distance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ func TestImperialDistanceUnits(t *testing.T) {
Name: "Thou",
Unit: linear.Thou,
Expect: 25.4 * linear.Micrometer,
Feet: 1 / 12000,
Feet: 1.0 / 12000,
Millimeters: 0.0254,
Meters: 0.0000254,
},
{
Name: "Inch",
Unit: linear.Inch,
Expect: 1000 * linear.Thou,
Feet: 1 / 12,
Feet: 1.0 / 12,
Millimeters: 25.4,
Meters: 0.0254,
},
Expand Down

0 comments on commit f1628dd

Please sign in to comment.