Repository: goadesign/goa (go)

Issue:
goa usage indicates --out is a flag, when it is not
On goa version 3.1.3 running on Ubuntu, the goa gen help shows invalid usage.

Repro:
```
goa gen
```
```
Usage:
  goa gen PACKAGE [--out DIRECTORY] [--debug]
  goa example PACKAGE [--out DIRECTORY] [--debug]
  goa version

... etc
```

The `--out` flag doesn't seem to work for me, but `-o` and `-output` do, so the documentation probably just needs to be updated on those first two usage lines.

Pull request:
Fix CLI help output
fix #2569

Patch:
diff --git a/cmd/goa/main.go b/cmd/goa/main.go
index fcd2d0cd..b3e191b3 100644
--- a/cmd/goa/main.go
+++ b/cmd/goa/main.go
@@ -111,8 +111,8 @@ func help() {
 Learn more at https://goa.design.
 
 Usage:
-  goa gen PACKAGE [--out DIRECTORY] [--debug]
-  goa example PACKAGE [--out DIRECTORY] [--debug]
+  goa gen PACKAGE [--output DIRECTORY] [--debug]
+  goa example PACKAGE [--output DIRECTORY] [--debug]
   goa version
 
 Commands:
diff --git a/codegen/funcs.go b/codegen/funcs.go
index 52add593..fecb7c74 100644
--- a/codegen/funcs.go
+++ b/codegen/funcs.go
@@ -145,10 +145,15 @@ func CamelCase(name string, firstUpper bool, acronym bool) string {
 		// [w,i] is a word.
 		word := string(runes[w:i])
 		// is it one of our initialisms?
-		if u := strings.ToUpper(word); acronym && commonInitialisms[u] {
-			if firstUpper {
-				u = strings.ToUpper(u)
-			} else if w == 0 {
+		if u := strings.ToUpper(word); commonInitialisms[u] {
+			switch {
+			case firstUpper && acronym:
+				// u is already in upper case. Nothing to do here.
+			case firstUpper && !acronym:
+				u = strings.Title(strings.ToLower(u))
+			case w > 0 && !acronym:
+				u = strings.Title(strings.ToLower(u))
+			case w == 0:
 				u = strings.ToLower(u)
 			}
 
diff --git a/codegen/service/convert.go b/codegen/service/convert.go
index ff062b81..a70b2951 100644
--- a/codegen/service/convert.go
+++ b/codegen/service/convert.go
@@ -554,6 +554,13 @@ func attributeName(obj *expr.Object, name string) (string, string) {
 			return ln, name
 		}
 	}
+	// next look for a lower camel case without acronym
+	lcn := codegen.CamelCase(name, false, false)
+	for _, nat := range *obj {
+		if nat.Name == lcn {
+			return lcn, name
+		}
+	}
 	// finally look for a snake case representation
 	sn := codegen.SnakeCase(name)
 	for _, nat := range *obj {
diff --git a/grpc/codegen/protobuf.go b/grpc/codegen/protobuf.go
index 3a8f057a..20b03e15 100644
--- a/grpc/codegen/protobuf.go
+++ b/grpc/codegen/protobuf.go
@@ -107,12 +107,12 @@ func makeProtoBufMessageR(att *expr.AttributeExpr, tname *string, sd *ServiceDat
 		switch {
 		case expr.IsArray(att.Type):
 			wrapAttr(att, "ArrayOf"+tname+
-				protoBufify(protoBufMessageDef(expr.AsArray(att.Type).ElemType, sd), true), sd)
+				protoBufify(protoBufMessageDef(expr.AsArray(att.Type).ElemType, sd), true, true), sd)
 		case expr.IsMap(att.Type):
 			m := expr.AsMap(att.Type)
 			wrapAttr(att, tname+"MapOf"+
-				protoBufify(protoBufMessageDef(m.KeyType, sd), true)+
-				protoBufify(protoBufMessageDef(m.ElemType, sd), true), sd)
+				protoBufify(protoBufMessageDef(m.KeyType, sd), true, true)+
+				protoBufify(protoBufMessageDef(m.ElemType, sd), true, true), sd)
 		}
 	}
 
@@ -190,7 +190,7 @@ func protoBufMessageName(att *expr.AttributeExpr, s *codegen.NameScope) string {
 func protoBufFullMessageName(att *expr.AttributeExpr, pkg string, s *codegen.NameScope) string {
 	switch actual := att.Type.(type) {
 	case expr.UserType:
-		n := s.HashedUnique(actual, protoBufify(actual.Name(), true), "")
+		n := s.HashedUnique(actual, protoBufify(actual.Name(), true, true), "")
 		if pkg == "" {
 			return n
 		}
@@ -257,7 +257,7 @@ func protoBufMessageDef(att *expr.AttributeExpr, sd *ServiceData) string {
 				desc string
 			)
 			{
-				fn = codegen.SnakeCase(protoBufify(nat.Name, false))
+				fn = codegen.SnakeCase(protoBufify(nat.Name, false, false))
 				fnum = rpcTag(nat.Attribute)
 				if prim := getPrimitive(nat.Attribute); prim != nil {
 					typ = protoBufMessageDef(prim, sd)
@@ -295,7 +295,7 @@ func protoBufGoFullTypeRef(att *expr.AttributeExpr, pkg string, s *codegen.NameS
 //
 // If firstUpper is true the first character of the identifier is uppercase
 // otherwise it's lowercase.
-func protoBufify(str string, firstUpper bool) string {
+func protoBufify(str string, firstUpper, acronym bool) string {
 	// Optimize trivial case
 	if str == "" {
 		return ""
@@ -308,8 +308,7 @@ func protoBufify(str string, firstUpper bool) string {
 		str = str[:idx]
 	}
 
-	// We must disable acronyms to generate gRPC compatible names.
-	str = codegen.CamelCase(str, firstUpper, false)
+	str = codegen.CamelCase(str, firstUpper, acronym)
 	if str == "" {
 		// All characters are invalid. Produce a default value.
 		if firstUpper {
@@ -329,7 +328,7 @@ func protoBufifyAtt(att *expr.AttributeExpr, name string, upper bool) string {
 			name = tname[0]
 		}
 	}
-	return protoBufify(name, upper)
+	return protoBufify(name, upper, false)
 }
 
 // protoBufNativeMessageTypeName returns the protocol buffer built-in type
diff --git a/grpc/codegen/service_data.go b/grpc/codegen/service_data.go
index e5963672..bf3b5d35 100644
--- a/grpc/codegen/service_data.go
+++ b/grpc/codegen/service_data.go
@@ -451,16 +451,16 @@ func (d ServicesData) analyze(gs *expr.GRPCServiceExpr) *ServiceData {
 	}
 	for _, e := range gs.GRPCEndpoints {
 		// convert request and response types to protocol buffer message types
-		e.Request = makeProtoBufMessage(e.Request, protoBufify(e.Name()+"_request", true), sd)
+		e.Request = makeProtoBufMessage(e.Request, protoBufify(e.Name()+"_request", true, true), sd)
 		if e.MethodExpr.StreamingPayload.Type != expr.Empty {
-			e.StreamingRequest = makeProtoBufMessage(e.StreamingRequest, protoBufify(e.Name()+"_streaming_request", true), sd)
+			e.StreamingRequest = makeProtoBufMessage(e.StreamingRequest, protoBufify(e.Name()+"_streaming_request", true, true), sd)
 		}
-		e.Response.Message = makeProtoBufMessage(e.Response.Message, protoBufify(e.Name()+"_response", true), sd)
+		e.Response.Message = makeProtoBufMessage(e.Response.Message, protoBufify(e.Name()+"_response", true, true), sd)
 		for _, er := range e.GRPCErrors {
 			if er.ErrorExpr.Type == expr.ErrorResult || !expr.IsObject(er.ErrorExpr.Type) {
 				continue
 			}
-			er.Response.Message = makeProtoBufMessage(er.Response.Message, protoBufify(e.Name()+"_"+er.Name+"_error", true), sd)
+			er.Response.Message = makeProtoBufMessage(er.Response.Message, protoBufify(e.Name()+"_"+er.Name+"_error", true, true), sd)
 		}
 
 		// collect all the nested messages and return the top-level message


Test patch:
diff --git a/codegen/funcs_test.go b/codegen/funcs_test.go
index 7b8f5bbc..0be4c956 100644
--- a/codegen/funcs_test.go
+++ b/codegen/funcs_test.go
@@ -36,26 +36,34 @@ func TestSnakeCase(t *testing.T) {
 
 func TestCamelCase(t *testing.T) {
 	cases := map[string]struct {
-		str         string
-		firstUpper  bool
-		useAcronyum bool
-		expected    string
+		str        string
+		firstUpper bool
+		useAcronym bool
+		expected   string
 	}{
-		"all lower":             {"aaa", false, true, "aaa"},
-		"all lower first upper": {"aaa", true, true, "Aaa"},
-		"start upper":           {"Aaa", false, true, "aaa"},
-		"mid upper":             {"a_aa", false, true, "aAa"},
-		"end upper":             {"aa_a", false, true, "aaA"},
-		"sequential uppers":     {"aa_aaaa", false, true, "aaAaaa"},
-		"end sequential uppers": {"aa_aa", false, true, "aaAa"},
-		"multiple_uppers":       {"aa_aaa_aaa", false, true, "aaAaaAaa"},
-		"underscores":           {"aa_aaa_aaa", false, true, "aaAaaAaa"},
-		"acronym":               {"aa_id", false, true, "aaID"},
-		"disable acronym":       {"aa_id", false, false, "aaId"},
+		"all lower":                     {"aaa", false, true, "aaa"},
+		"all lower first upper":         {"aaa", true, true, "Aaa"},
+		"start upper":                   {"Aaa", false, true, "aaa"},
+		"mid upper":                     {"a_aa", false, true, "aAa"},
+		"end upper":                     {"aa_a", false, true, "aaA"},
+		"sequential uppers":             {"aa_aaaa", false, true, "aaAaaa"},
+		"end sequential uppers":         {"aa_aa", false, true, "aaAa"},
+		"multiple_uppers":               {"aa_aaa_aaa", false, true, "aaAaaAaa"},
+		"underscores":                   {"aa_aaa_aaa", false, true, "aaAaaAaa"},
+		"acronym":                       {"aa_id", false, true, "aaID"},
+		"lower camel case":              {"aa_id", false, false, "aaId"},
+		"upper camel case":              {"aaID", false, true, "aaID"},
+		"lower camel case with acronym": {"aaId", false, true, "aaID"},
+
+		"disable acronym":                    {"aa_id", false, false, "aaId"},
+		"disable acronym first upper":        {"aaID", true, false, "AaId"},
+		"disable acronym upper case acronym": {"aa_ID", false, false, "aaId"},
+		"disable acronym upper camel case":   {"aaID", false, false, "aaId"},
+		"disable acronym lower camel case":   {"aaId", false, false, "aaId"},
 	}
 	for k, tc := range cases {
 		t.Run(k, func(t *testing.T) {
-			actual := CamelCase(tc.str, tc.firstUpper, tc.useAcronyum)
+			actual := CamelCase(tc.str, tc.firstUpper, tc.useAcronym)
 			if actual != tc.expected {
 				t.Errorf("got %q, expected %q", actual, tc.expected)
 			}
diff --git a/codegen/service/convert_test.go b/codegen/service/convert_test.go
index 8fe82f77..ab4d42c7 100644
--- a/codegen/service/convert_test.go
+++ b/codegen/service/convert_test.go
@@ -246,6 +246,7 @@ func TestConvertFile(t *testing.T) {
 		{"create-object-extra", testdata.CreateObjectExtraDSL, 1, testdata.CreateObjectExtraCode},
 		{"create-external-convert", testdata.CreateExternalDSL, 0, testdata.CreateExternalConvert},
 		{"create-alias-convert", testdata.CreateAliasDSL, 0, testdata.CreateAliasConvert},
+		{"mixed-case-convert", testdata.MixedCaseDSL, 0, testdata.MixedCaseConvert},
 	}
 	for _, c := range cases {
 		t.Run(c.Name, func(t *testing.T) {
diff --git a/codegen/service/testdata/create_dsls.go b/codegen/service/testdata/create_dsls.go
index 3542ac69..4fb2a428 100644
--- a/codegen/service/testdata/create_dsls.go
+++ b/codegen/service/testdata/create_dsls.go
@@ -248,3 +248,18 @@ var CreateAliasDSL = func() {
 		})
 	})
 }
+
+var MixedCaseDSL = func() {
+	var StringType = Type("StringType", func() {
+		CreateFrom(external.MixedCaseModel{})
+		Attribute("lowerCamelId", String)
+		Attribute("UpperCamelID", String)
+		Attribute("snake_id", String)
+	})
+
+	Service("Service", func() {
+		Method("Method", func() {
+			Payload(StringType)
+		})
+	})
+}
diff --git a/codegen/service/testdata/create_functions.go b/codegen/service/testdata/create_functions.go
index 1dfa0a83..1aa5e005 100644
--- a/codegen/service/testdata/create_functions.go
+++ b/codegen/service/testdata/create_functions.go
@@ -149,3 +149,25 @@ func (t *StringType) CreateFromConvertModel(v *aliasd.ConvertModel) {
 	*t = *temp
 }
 `
+
+var MixedCaseConvert = `// Service service type conversion functions
+//
+// Command:
+// $ goa
+
+package service
+
+import (
+	external "goa.design/goa/v3/codegen/service/testdata/external"
+)
+
+// CreateFromMixedCaseModel initializes t from the fields of v
+func (t *StringType) CreateFromMixedCaseModel(v *external.MixedCaseModel) {
+	temp := &StringType{
+		LowerCamelID: &v.LowerCamelID,
+		UpperCamelID: &v.UpperCamelID,
+		SnakeID:      &v.SnakeID,
+	}
+	*t = *temp
+}
+`
diff --git a/codegen/service/testdata/external/external.go b/codegen/service/testdata/external/external.go
index 03961783..88b8285e 100644
--- a/codegen/service/testdata/external/external.go
+++ b/codegen/service/testdata/external/external.go
@@ -3,3 +3,9 @@ package external
 type ConvertModel struct {
 	Foo string
 }
+
+type MixedCaseModel struct {
+	LowerCamelID string
+	UpperCamelID string
+	SnakeID      string
+}
diff --git a/grpc/codegen/testdata/streaming_code.go b/grpc/codegen/testdata/streaming_code.go
index a6e43132..2b7068ac 100644
--- a/grpc/codegen/testdata/streaming_code.go
+++ b/grpc/codegen/testdata/streaming_code.go
@@ -328,7 +328,7 @@ func (s *MethodBidirectionalStreamingRPCClientStream) Recv() (*servicebidirectio
 	if err != nil {
 		return res, err
 	}
-	proj := NewIdView(v)
+	proj := NewIDView(v)
 	vres := &servicebidirectionalstreamingrpcviews.ID{Projected: proj, View: "default"}
 	return servicebidirectionalstreamingrpc.NewID(vres), nil
 }