README.md
1# Modern URI
2
3[![License BSD3](https://img.shields.io/badge/license-BSD3-brightgreen.svg)](http://opensource.org/licenses/BSD-3-Clause)
4[![Hackage](https://img.shields.io/hackage/v/modern-uri.svg?style=flat)](https://hackage.haskell.org/package/modern-uri)
5[![Stackage Nightly](http://stackage.org/package/modern-uri/badge/nightly)](http://stackage.org/nightly/package/modern-uri)
6[![Stackage LTS](http://stackage.org/package/modern-uri/badge/lts)](http://stackage.org/lts/package/modern-uri)
7[![Build Status](https://travis-ci.org/mrkkrp/modern-uri.svg?branch=master)](https://travis-ci.org/mrkkrp/modern-uri)
8
9This is a modern library for working with URIs in Haskell as per RFC 3986:
10
11https://tools.ietf.org/html/rfc3986
12
13## Features
14
15The `modern-uri` package features:
16
17* Correct by construction `URI` data type. The correctness is ensured by
18 making sure that every sub-component of the `URI` record is by itself
19 cannot be invalid. This boils down to careful use of types and a set of
20 smart constructors.
21* Textual components in the `URI` data type are represented as `Text` rather
22 than `ByteString`, because they are percent-decoded and so they can
23 contain characters outside of ASCII range (i.e. Unicode). This allows for
24 easier manipulation of `URI`s, while encoding and decoding headaches are
25 handled by the parsers and renders for you.
26* Absolute and relative URIs differ only by the scheme component: if it's
27 `Nothing`, then URI is relative, otherwise it's absolute.
28* Megaparsec parser that can be used as a standalone smart constructor for
29 the `URI` data type (see `mkURI`) as well as be seamlessly integrated into
30 a bigger Megaparsec parser that consumes strict `Text` (see `parser`) or
31 strict `ByteString` (see `parserBs`).
32* The parser performs some normalization, for example it collapses
33 consecutive slashes. Some smart constructors such as `mkScheme` and
34 `mkHost` also perform normalization. So in a sense URIs are also
35 “normalized by construction” to some extent.
36* Fast rendering to strict `Text` and `ByteString` as well as to their
37 respective `Builder` types and to `String`/`ShowS`.
38* Extensive set of lensy helpers for easier manipulation of the nested data
39 types (see `Text.URI.Lens`).
40* Quasi-quoters for compile-time construction of the `URI` data type and
41 refined text types (see `Text.URI.QQ`).
42
43## Quick start
44
45The `modern-uri` package serves three main purposes:
46
47* Construction of the `URI` data type.
48* Inspection and manipulation of the `URI` data type (in the sense of
49 changing its parts).
50* Rendering of `URI`s.
51
52Let's walk through every operation quickly.
53
54### Construction of `URI`s
55
56There are four ways to create a `URI` value. First off, one could assemble
57it manually like so:
58
59```haskell
60λ> :set -XOverloadedStrings
61λ> import qualified Text.URI as URI
62λ> scheme <- URI.mkScheme "https"
63λ> scheme
64"https"
65λ> host <- URI.mkHost "markkarpov.com"
66λ> host
67"markkarpov.com"
68λ> let uri = URI.URI (Just scheme) (Right (URI.Authority Nothing host Nothing)) Nothing [] Nothing
69λ> uri
70URI
71 { uriScheme = Just "https"
72 , uriAuthority = Right
73 (Authority
74 { authUserInfo = Nothing
75 , authHost = "markkarpov.com"
76 , authPort = Nothing })
77 , uriPath = Nothing
78 , uriQuery = []
79 , uriFragment = Nothing }
80```
81
82In this library we use quite a few refined text values. They only can be
83constructed by using smart constructors like `mkScheme :: MonadThrow m =>
84Text -> m (RText 'Scheme)`. For example, if argument to `mkScheme` is not a
85valid scheme, an exception will be thrown. Note that monads such as `Maybe`
86are also instances of the `MonadThrow` type class, and so the smart
87constructors may be used in pure setting as well.
88
89There is a smart constructor that can make an entire `URI` too, it's called
90(unsurprisingly) `mkURI`:
91
92```haskell
93λ> uri <- URI.mkURI "https://markkarpov.com"
94λ> uri
95URI
96 { uriScheme = Just "https"
97 , uriAuthority = Right
98 (Authority
99 { authUserInfo = Nothing
100 , authHost = "markkarpov.com"
101 , authPort = Nothing })
102 , uriPath = Nothing
103 , uriQuery = []
104 , uriFragment = Nothing }
105```
106
107If the argument of `mkURI` is not a valid URI, then an exception will be
108thrown. The exception will contain full context and the actual parse error.
109
110If some refined text value or `URI` is known statically at compile time, we
111can use Template Haskell, namely the “quasi quotes” feature. To do so import
112the `Text.URI.QQ` module and enable the `QuasiQuotes` language extension,
113like so:
114
115```haskell
116λ> :set -XQuasiQuotes
117λ> import qualified Text.URI.QQ as QQ
118λ> let uri = [QQ.uri|https://markkarpov.com|]
119λ> uri
120URI
121 { uriScheme = Just "https"
122 , uriAuthority = Right
123 (Authority
124 { authUserInfo = Nothing
125 , authHost = "markkarpov.com"
126 , authPort = Nothing })
127 , uriPath = Nothing
128 , uriQuery = []
129 , uriFragment = Nothing }
130```
131
132Note how the value returned by the `url` quasi quote is pure, its
133construction cannot fail because when there is an invalid URI inside the
134quote it's a compilation error. The `Text.URI.QQ` module has quasi-quoters
135for scheme, host, and other components.
136
137Finally, the package provides two Megaparsec parsers: `parser` and
138`parserBs`. The first works on strict `Text`, while other one works on
139strict `ByteString`s. You can use the parsers in a bigger Megaparsec parser
140to parse `URI`s.
141
142### Inspection and manipulation
143
144Although one could use record syntax directly, possibly with language
145extensions like `RecordWildcards`, the best way to inspect and edit parts of
146`URI` is with lenses. The lenses can be found in the `Text.URI.Lens` module.
147If you have never used the
148[`lens`](https://hackage.haskell.org/package/lens) library, you could
149probably start by reading/watching materials suggested in the library
150description on Hackage.
151
152Here are some examples, just to show off what you can do:
153
154```haskell
155λ> import Text.URI.Lens
156λ> uri <- URI.mkURI "https://example.com/some/path?foo=bar&baz=quux&foo=foo"
157λ> uri ^. uriScheme
158Just "https"
159λ> uri ^? uriAuthority . _Right . authHost
160Just "example.com"
161λ> uri ^. isPathAbsolute
162True
163λ> uri ^. uriPath
164["some","path"]
165λ> k <- URI.mkQueryKey "foo"
166λ> uri ^.. uriQuery . queryParam k
167["bar","foo"]
168-- etc.
169```
170
171### Rendering
172
173Rendering turns a `URI` into a sequence of bytes or characters. Currently
174the following options are available:
175
176* `render` for rendering to strict `Text`.
177* `render'` for rendering to text `Builder`. It's possible to turn that into
178 lazy `Text` by using the `toLazyText` function from
179 `Data.Text.Lazy.Builder`.
180* `renderBs` for rendering to strict `ByteString`.
181* `renderBs'` for rendering to byte string `Builder`. Similarly it's
182 possible to get a lazy `ByteString` from that by using the
183 `toLazyByteString` function from `Data.ByteString.Builder`.
184* `renderStr` can be used to render to `String`. Sometimes it's handy. The
185 render uses difference lists internally so it's not that slow, but in
186 general I'd advise avoiding `String`s.
187* `renderStr'` returns `ShowS`, which is just a synonym for `String ->
188 String`—a function that prepends the result of rendering to a given
189 `String`. This is useful when the `URI` you want to render is a part of a
190 bigger output, just like with the builders mentioned above.
191
192Examples:
193
194```haskell
195λ> uri <- mkURI "https://markkarpov.com/posts.html"
196λ> render uri
197"https://markkarpov.com/posts.html"
198λ> renderBs uri
199"https://markkarpov.com/posts.html"
200λ> renderStr uri
201"https://markkarpov.com/posts.html"
202-- etc.
203```
204
205## Contribution
206
207Issues, bugs, and questions may be reported in [the GitHub issue tracker for
208this project](https://github.com/mrkkrp/modern-uri/issues).
209
210Pull requests are also welcome.
211
212## License
213
214Copyright © 2017–present Mark Karpov
215
216Distributed under BSD 3 clause license.
217