Golang error control on parameters when calling a function with reflection - go

I'm trying to call a function using reflection based on it's name.
I first recover the function with
f := reflect.ValueOf(s).MethodByName(name)
I do error control on the function name by checking if "f" is valid with
if !f.IsValid() {
return errors.New("There is no function with that name")
}
And finally I perform the call with
f.Call(inputs)
My challenge is that inputs depends on the user, and sometimes they can put too many parameters, too little or invalid types.
IE:
2019/01/04 16:47:54 http: panic serving [::1]:53662: reflect: Call using string as type int
How can I control that the inputs are valid before performing the call?
Maybe there is a way to recover the expected inputs in my method and check them against the provided ones?

Related

Compare dynamic error message using Error.Is function

I have a dynamic error message for example
metadata.name: invalid value "test-value"
"test-value" will be dynamic, and I need to do something if this error pattern appears
How to check the errors pattern with Error.Is function?
You say your error has a dynamic message. I think you mean that you're defining a type that satisfies the error interface. (If you're using fmt.Errorf instead though, you should define a type for this use case).
type invalidValueError struct {
value string
}
func (e *invalidValidError) Error() string {
return fmt.Sprintf("invalid value %q", e.value)
}
You can check whether any given error has this type using errors.As. This will match not only the error itself, but any errors it's wrapping.
if ive := (*invalidValueError)(nil); errors.As(err, &ive) {
// got an *invalidValueError
}
Avoid matching the text of error messages if at all possible. They can change on different systems or in different locales, and they're generally not covered by compatibility promises.

linter err113: do not define dynamic errors, use wrapped static errors instead

I am using err113 as part of golangci-lint.
It is complaining about ...
foo_test.go:55:61: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"repo gave err\")" (goerr113)
repoMock.EXPECT().Save(gomock.Eq(&foooBarBar)).Return(nil, errors.New("repo gave err")),
^
foo_test.go:22:42: err113: do not define dynamic errors, use wrapped static errors instead: "errors.New(\"oops\")" (goerr113)
repoMock.EXPECT().FindAll().Return(nil, errors.New("oops"))
^
What is best way to fix this ?
Quoting https://github.com/Djarvur/go-err113
Also, any call of errors.New() and fmt.Errorf() methods are reported
except the calls used to initialise package-level variables and the
fmt.Errorf() calls wrapping the other errors.
I am trying to get a idiomatic example for this.
Declare a package-level variables as suggested:
var repoGaveErr = errors.New("repo gave err")
func someFunc() {
repoMock.EXPECT().Save(gomock.Eq(&foooBarBar)).Return(nil, repoGaveErr)
}
Every call to errors.New allocates a new unique error value. The application creates a single value representing the error by declaring the package-level variable.
There are two motivations for the single value:
The application can compare values for equality to check for a specific error condition.
Reduce memory allocations (although probably not a big deal in practice)
The value io.EOF is a canonical example.
Since GO 1.13 you can define a new error type, wrap it and use it.
for example, if you want to return an "operation not permitted" + the operation.
you need to implement something like
var OperationNotPermit = errors.New("operation not permitted")
func OperationNotFoundError(op string) error {
return fmt.Errorf("OperationNotPermit %w : %s", OperationNotPermit, op)
}
then in your code, when you want to return the error,
return nil, OperationNotFoundError(Op)
Let's back to question case:
first, define the custom error and the wapper
var repoError = errors.New("repositoryError")
func RepositoryError(msg string) error {
return fmt.Errorf("%w: %s", repoError,msg)
}
then in your code,
repoMock.EXPECT().Save(gomock.Eq(&foooBarBar)).Return(nil, RepositoryError("YOUR CUSTOM ERROR MESSAGE"))
Since it hasn't been said before, you probably don't need to define package level errors for tests. Given the idea is to wrap errors so they can be compared and unwrapped in the caller, returning a dynamic error in a test is fine as long as the purposes of your test are served.

Return type error in eXist-db

I have just realized my logger is very active in the case of a function responsible for generating epubs. The problem is it works as expected. Hence I wonder what is going on here.
The error:
java:org.exist.xquery.XPathException, exerr:ERROR The actual return type does not match the sequence type declared in the function's signature: epub-util:render-epub(node()+, xs:string) xs:base64Binary. Expected cardinality: exactly one, got 0.
… epub is properly served to the client.
As for exerr:ERROR, I checked the general log in $EXIST_HOME/webapp/WEB-INF/logs/exist.log. There are no associated errors caught.
The suspected function:
declare function epub-util:render-epub($entries as node()+, $name as xs:string) as xs:base64Binary {
let $zip-file := compression:zip($entries, true())
let $archiveName := $name
return
response:stream-binary($zip-file, 'application/epub+zip', lower-case(replace($archiveName, ' ', '-')) || '.epub')
};
I run eXist on Ubuntu Server 14.04, eXist-db is 3.0.RC1.
response:stream-binary() is a very special function, that in essence directly writes bytes to a servlet output stream , hence it can only be used in the REST interface.
It can only write a byte stream to a HTTP agent, that is e.g. your web browser. It is not possible to store this data in a variable.
I agree that even item() is not a good thing, it should be something like void but that does not exist.
The common use-case it to have this expression as the last function call in a xquery script. A similar construct we use in the JFreechart library.
According to the eXist-db function documentation for response:stream-binary(), this function returns item(), not xs:base64binary. Replacing the return type with item() should fix the error.

Go Functions/Methods without a name

I'm really struggling to find a name for a type of function I've come across.
Here is the function in question:
https://github.com/go-fsnotify/fsnotify/blob/master/fsnotify.go#L32
This is how I'm using it (as per the fsnotify example):
select {
case event := <-watcher.Events:
log.Println("Event Triggered: ", event)
In that Println 'event' is returning the formatted string as per the function above, I'm just struggling to understand how a straight call to 'event' is using that function yet I would be expecting it to be accessed like the struct fields (event.Name, event.Op):
event.funcForReturningNicelyFormattedEvent()
It feels like this is a 'default' function as it has no name and it just returns the formatted data - I'm struggling to come up with the name/type/search term so I can find out more and understand the concept and importantly the reasoning behind it better.
Any help is appreciated.
It's very simple - println uses the String() method on any struct that implements it automatically. This is a classic use case of Go's implicit interfaces: every struct that has the methods an interface includes, is considered to be implementing the interface.
If it has func String() string it is considered a Stringer and used by fmt. You can use it on your own structs too, of course.
Function Println checks if the passed value implements interface Stringer. If it does it calls method String on this value. Event type implements that interface by supplying its implementation of String method in the excerpt you linked to.
In Go you don't have to declare that you implement interface.

Function parameter error - Missing Argument Label Anomaly - SWIFT

I have a strange issue (which I can overcome however I would like to get a proper understanding of my error).
I have a small random number generator function which I use often:
func ranNum(low: Int, high:Int) -> UInt32 {
var result = arc4random_uniform(UInt32((high+1)-low)) + low
return result
}
When I use this in XCode playgrounds it works just fine if I pass in something like:
ranNum(1, 10)
However, in a regular Swift file it generates the error message : Missing argument label 'hi:' in call. Now I can overcome this by calling the function this way:
ranNum(1, hi:10)
Apart from it just being a little harder to read, it just isn't making sense why it works in Playgrounds but also why it requires only the 2nd argument label and not both. Any help as to what I am not understandinh would be greatly appreciated.
That's called external parameter name, and by default:
global functions: don't have implicit external names
class/struct methods: external names are automatically defined for all parameters after the first
initializers: external names are automatically defined for all parameters
If not explicitly specified, external names take the same name as the local parameter.
You can override that by prefixing a local parameter name with _. In your case:
func ranNum(low: Int, _ high:Int) -> UInt32 {
...
}
You mentioned that in playground calling the function works without any external parameter name - I may argue that:
in playground you have that function as a global function
in other tests, that function is a class/struct method
Am I right?

Resources