Created
May 8, 2019 04:13
-
-
Save cyisfor/18b976fbbad530a883f15d3bf8f70ebb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from times import Time, `$`, DateTime, toTime | |
from sequtils import toSeq, mapIt | |
from strutils import replace | |
################ | |
# This is just some stuff to establish a few interestingly named and varying types | |
type Ident = int | |
type Actor = object | |
name: string | |
type Tag = int | |
func `$`(actor: Actor): string = | |
return "Actor " & actor.name | |
func urlencode(s: string): string = | |
return s.replace(" ", "+") | |
################################# | |
# Begin what SHOULD be the output of a macro. | |
# Note all the copy and paste code duplication. | |
# As well as the many different locations that must be kept in sync to add or remove anything | |
type ParameterKind = enum | |
local_only_kind = "local_only" | |
only_media_kind = "only_media" | |
since_kind = "since" | |
until_kind = "until" | |
poster_kind = "poster" | |
signer_kind = "signer" | |
tags_kind = "tags" | |
tags_excluded_kind = "tags_excluded" | |
type TypeKind = enum | |
Time_type | |
Actor_type | |
Tag_type | |
type Behavior = enum | |
flag_behavior | |
value_behavior | |
seq_behavior | |
type Parameter = object | |
case kind: ParameterKind: | |
of local_only_kind, only_media_kind: | |
discard | |
of since_kind, until_kind: | |
Time_value: Time | |
of poster_kind, signer_kind: | |
Actor_value: Actor | |
of tags_kind, tags_excluded_kind: | |
Tag_value: seq[Tag] | |
func doc(p: Parameter): string = | |
case p.kind: | |
of local_only_kind: return "Only local posts" | |
of only_media_kind: return "Only posts with media in them" | |
of since_kind: return "Only posts after this time" | |
of until_kind: return "Only posts before this time" | |
of poster_kind: return "Only posts posted by this guy" | |
of signer_kind: return "Only posts signed by this guy" | |
of tags_kind: return "Only posts with these tags" | |
of tags_excluded_kind: return "Only posts without these tags" | |
func behavior(p: Parameter): Behavior = | |
case p.kind: | |
of local_only_kind, only_media_kind: | |
flag_behavior | |
of since_kind, until_kind: | |
value_behavior | |
of poster_kind, signer_kind: | |
value_behavior | |
of tags_kind, tags_excluded_kind: | |
seq_behavior | |
func typeKind(p: Parameter): TypeKind = | |
case p.kind: | |
of local_only_kind, only_media_kind: | |
assert(false) | |
of since_kind, until_kind: | |
return Time_type | |
of poster_kind, signer_kind: | |
return Actor_type | |
of tags_kind, tags_excluded_kind: | |
return Tag_type | |
proc convert_flag(p: Parameter): seq[tuple[name: string, value: string]] = | |
result.add(($p.kind, "1")) | |
proc convert_value[T](p: Parameter, value: T): seq[tuple[name: string, value: string]] = | |
result.add(($p.kind, urlencode($value))) | |
proc convert_seq[T](p: Parameter, value: seq[T]): seq[tuple[name: string, value: string]] = | |
# I want to shoot whoevexr started this trend in the elbow | |
let arrayname = $p.kind & "[]" | |
for item in value: | |
result.add((arrayname, urlencode($item))) | |
proc toQueryParams*(p: Parameter): seq[tuple[name: string,value: string]] = | |
if p.behavior == flag_behavior: return convert_flag(p) | |
case p.typeKind: | |
of Time_type: return convert_value(p, p.Time_value) | |
of Actor_type: return convert_value(p, p.Actor_value) | |
of Tag_type: return convert_seq(p, p.Tag_value) | |
let local_only* = Parameter(kind: local_only_kind) | |
let only_media* = Parameter(kind: only_media_kind) | |
proc since*(value: DateTime): Parameter = | |
Parameter(kind: since_kind, Time_value: value.toTime()) | |
proc until*(value: DateTime): Parameter = | |
Parameter(kind: until_kind, Time_value: value.toTime()) | |
proc poster*(value: Actor): Parameter = | |
Parameter(kind: poster_kind, Actor_value: value) | |
proc signer*(value: Actor): Parameter = | |
Parameter(kind: signer_kind, Actor_value: value) | |
proc tags*(value: varargs[string]): Parameter = | |
Parameter(kind: tags_kind, Tag_value: value.mapIt(it.len)) | |
proc tagsExcluded*(value: varargs[string]): Parameter = | |
Parameter(kind: tags_excluded_kind, Tag_value: value.mapIt(it[0].ord)) | |
# end hypothetical macro output | |
#############################################3 | |
# this is what I would LIKE to write instead of the above code | |
when false: | |
flags: | |
local_only = "Only local posts" | |
only_media = "Only posts with media in them" | |
Time: | |
since = "Only posts after this time" | |
until = "Only posts before this time" | |
Actor: | |
poster = "Only posts posted by this guy" | |
signer = "Only posts signed by this guy" | |
seq[Tag]: | |
tags = "Only posts with these tags" | |
tags_excluded = "Only posts without these tags" | |
################################# | |
# Then just something to prove that it actually works | |
when isMainModule: | |
import times | |
from strutils import join | |
from sequtils import mapIt | |
proc showPosts(parameters: varargs[Parameter]) = | |
var query_params: seq[tuple[name: string,value: string]] | |
for parameter in parameters: | |
query_params.add(parameter.toQuery_params) | |
echo(query_params.mapIt(it[0] & "=" & it[1]).join("&")) | |
let Alice: Actor = Actor(name: "Alice") | |
let Joe: Actor = Actor(name: "Giuseppi Verdi") | |
show_posts( | |
since(now() - 30.days), | |
until(now() - 15.days), | |
poster(Alice), | |
local_only, | |
signer(Joe), | |
tags("ponies", "art", "beautiful"), | |
tagsExcluded("nyx")) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment