Search code examples
gotextencodingcharmap

How to iterate list of Charmaps in charmap.All and compare names?


I'd like to write a little command-line utility to decode/encode streams of text, like iconv, but in Go. The user will provide the encoder and decoder names and the utility will check them against charmap.All to make the user args are valid before trying to decode/encode the stream.

I can iterate charmap.All and print the names like:

for _, cmap := range charmap.All {
    fmt.Println(cmap)
}

I can compare my cmap var to a known Charmap:

if cmap == charmap.ISO8859_1 {
    fmt.Println(charmap.ISO8859_1.String())
}

But I don't know how to do the next step, which seems so tantalizingly close (and easy):

var encoder string
flag.StringVar(&encoder, "encoder", "ISO 8859-1", "name of encoder")
flag.Parse()

for _, cmap := range charmap.All {
    if cmap.String() == encoder {
        // encode based on user's input
    }
}

Is that possible, given the encoding/charmap API?

Also, how is it that my cmap var and charmap.ISO8859_1 are equivalent (in the cmap == charmap.ISO8859_1 example), but cmap is really an Encoding interface, and I cannot convert:

charmap.Charmap(cmap).String()cannot convert cmap (type encoding.Encoding) to type charmap.Charmap

I'm still pretty new to Go, and don't fully understand these differences and equivalencies in types and interfaces.


Solution

  • You can break it down as follows, charmap.All is a slice of Encoding interfaces, which can be iterated using a range loop.

    The equality works because the type Charmap, implements the interface, i.e. each charmap type will have a NewDecoder() and a NewEncoder() method implementation defined. By the language specification, if a struct implements an interface, they can be compared (See Comparison operators)

    A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x

    With the above inference, we can understand that the func (*Charmap) String is a method on the Charmap struct type and not on the interface. So the right way to do this, would be to Type assert your interface value and then call the string function on the same.

    for _, enc := range charmap.All {
        cmap, ok := enc.(*charmap.Charmap)
        if ok && cmap.String() == encoder {
            // encode based on user's input
        }
    }