I'm hoping to get a suggestion on how to best resolve this problem I've got.
I have two types that are seqs of key-value pairs, stored as seq[(string, string)]. I would like each different type to have different behavior for []=, one being case sensitive, the other being not case sensitive.
Here's some example code:
import std/strutils
type
QueryParams = seq[(string, string)]
HttpHeaders = seq[(string, string)]
# Case sensitive, overwrite first matching key
proc `[]=`*(s: var QueryParams, key, value: string) =
for pair in s.mitems:
if pair[0] == key:
pair[1] = value
return
s.add((key, value))
# Not case sensitive, overwrite first matching key
proc `[]=`*(s: var HttpHeaders, key, value: string) =
for pair in s.mitems:
if cmpIgnoreCase(pair[0], key) == 0:
pair[1] = value
return
s.add((key, value))
Unfortunately right now this doesn't compile, Error: redefinition of '[]='.
Well, I can make them distinct seq[(string, string)] instead, but then they don't have all the other things I want them to have, like the items and pairs iterators, len, so many things. I could try {.borrow.} or just re-implement to get the missing stuff back, but I'm not even sure how many procs I'll need to get back to parity.
I also don't want to force everyone importing the lib to accept my behavior for []= on unaliased seq[(string, string)] since I only mean it for the specific type. I thought aliases would work, only using the new []= when the type was the alias, but alas that is not so.
Is there a smarter way to accomplish this than re-implementing stuff?
Ah the age old misunderstanding of aliases :P
The best way around this would be:
import std/[strutils, typetraits]
type
QueryParams = distinct seq[(string, string)]
HttpHeaders = distinct seq[(string, string)]
# Case sensitive, overwrite first matching key
proc `[]=`*(s: var QueryParams, key, value: string) =
for pair in s.distinctBase.mitems:
if pair[0] == key:
pair[1] = value
return
s.distinctBase.add((key, value))
# Not case sensitive, overwrite first matching key
proc `[]=`*(s: var HttpHeaders, key, value: string) =
for pair in s.distinctBase.mitems:
if cmpIgnoreCase(pair[0], key) == 0:
pair[1] = value
return
s.distinctBase.add((key, value))
var a = @{"Hello": "World", "Hmm": "huh"}
a.HttpHeaders["hello"] = "world"
a.QueryParams["hello"] = "world"
I sat down to work with this and fortunately it seems to mostly work! I noticed one important thing not working pretty quickly though:
import std/[strutils, typetraits]
type QueryParams = distinct seq[(string, string)]
converter toBase(s: var QueryParams): var seq[(string, string)] = s.distinctBase
converter toBase(s: QueryParams): seq[(string, string)] = s.distinctBase
# Case sensitive, overwrite first matching key
proc `[]=`*(s: var QueryParams, key, value: string) =
for pair in s.distinctBase.mitems:
if pair[0] == key:
pair[1] = value
return
s.distinctBase.add((key, value))
var params: QueryParams
params["hello"] = "world"
if params[0] == ("hello", "world"):
echo "test"
The params[0] index access doesn't work.
However params.toBase[0] does work. Missing indexing is a pretty big pain and telling lib users manually call a converter is not cool to me. It seems many things do go through the converter like len and items but not this. Am I doing missing something obvious again?
This does not seem related to openarray but maybe it actually is internally.