aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/library/lux/world/net/uri/query.lux
blob: 5bfe26c2ed2e44275aff6b04598de525676ea95d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
(.require
 [library
  [lux (.except)
   [abstract
    [monad (.only do)]
    [codec (.only Codec)]
    [equivalence (.only Equivalence)]]
   [control
    ["?" parser]
    ["[0]" try (.only Try)]]
   [data
    ["[0]" text (.only)
     ["%" \\format]
     ["?[1]" \\parser (.only Parser)]]
    [collection
     ["[0]" list (.use "[1]#[0]" functor)]
     ["[0]" dictionary (.only Dictionary)]]]
   [math
    [number
     ["[0]" nat]]]
   [world
    [net
     ["[0]" uri
      ["[1]" encoding]]]]]])

(type .public Query
  (Dictionary Text Text))

(def .public empty
  Query
  (dictionary.empty text.hash))

(def .public equivalence
  (Equivalence Query)
  (dictionary.equivalence text.equivalence))

(def component
  (Parser Text)
  (?.rec
   (function (_ component)
     (do [! ?.monad]
       [head (?text.some (?text.none_of "+%&;"))]
       (all ?.either
            (?.after (?.either ?text.end
                               (?text.this "&"))
                     (in head))
            (do !
              [_ (?text.this "+")
               tail component]
              (in (%.format head " " tail)))
            (do !
              [_ (?text.this "%")
               code (|> (?text.exactly 2 ?text.hexadecimal)
                        (?.codec nat.hex)
                        (of ! each text.of_char))
               tail component]
              (in (%.format head code tail))))))))

(def separators
  "&;")

(def assignment
  "=")

(def invalid
  (%.format "=" "&;"))

(def (form query)
  (-> Query (Parser Query))
  (all ?.either
       (do ?.monad
         [_ ?text.end]
         (in query))
       (do [! ?.monad]
         [key (?text.some (?text.none_of ..invalid))
          key (?text.local key ..component)
          key (?.of_try (uri.decoded key))]
         (?.either (do !
                     [_ (?text.this ..assignment)
                      value ..component
                      value (?.of_try (uri.decoded value))]
                     (form (dictionary.has key value query)))
                   (do !
                     [_ (all ?.or
                             (?text.one_of ..separators)
                             ?text.end)]
                     (form (dictionary.has key "" query)))))
       ... if invalid form data, just stop parsing...
       (of ?.monad in query)))

(def format
  (%.Format Query)
  (|>> dictionary.entries
       (list#each (function (_ [key value])
                    (%.format (uri.encoded key) "=" (uri.encoded value))))
       (text.interposed "&")))

(def query
  (-> Text (Try Query))
  (?text.result (..form ..empty)))

(def .public codec
  (Codec Text Query)
  (implementation
   (def encoded ..format)
   (def decoded ..query)))