Browse Source

Merge pull request #41 from rustkas/doc-general-improvements

Documentation and general improvements
Marc Worrell 2 years ago
parent
commit
9054998a47
7 changed files with 722 additions and 15 deletions
  1. 11 0
      .gitignore
  2. 13 2
      Makefile
  3. 22 2
      README.md
  4. 3 0
      doc/overview.edoc
  5. 75 0
      doc/style.css
  6. 32 11
      rebar.config
  7. 566 0
      src/oauth.erl

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+.eunit
+*.beam
+ebin/
+_build/
+rebar3
+.rebar3
+deps
+.depsolver_plt
+doc/*
+!doc/style.css
+!doc/overview.edoc

+ 13 - 2
Makefile

@@ -15,14 +15,25 @@ shell: $(REBAR)
 test: $(REBAR)
 test: $(REBAR)
 	$(REBAR) as test ct
 	$(REBAR) as test ct
 
 
-clean: $(REBAR)
+clean: $(REBAR) clean_doc
 	$(REBAR) clean
 	$(REBAR) clean
 
 
+clean_doc:
+	@rm -f doc/*.html
+	@rm -f doc/erlang.png
+	@rm -f doc/edoc-info
+
 xref: $(REBAR)
 xref: $(REBAR)
 	$(REBAR) as test xref
 	$(REBAR) as test xref
 
 
 dialyzer: $(REBAR)
 dialyzer: $(REBAR)
-	$(REBAR) as test dialyzer
+	$(REBAR) as check dialyzer
+
+doc:  $(REBAR)
+	$(REBAR) edoc
+
+doc_private: $(REBAR)
+	$(REBAR) as doc_private edoc	
 
 
 ./rebar3:
 ./rebar3:
 	$(ERL) -noshell -s inets -s ssl \
 	$(ERL) -noshell -s inets -s ssl \

+ 22 - 2
README.md

@@ -1,9 +1,18 @@
+[![Build Status][gh badge]][gh]
+[![Hex.pm version][hexpm version]][hexpm]
+[![Hex.pm Downloads][hexpm downloads]][hexpm]
+[![Hex.pm Documentation][hexdocs documentation]][hexdocs]
+[![Erlang Versions][erlang version badge]][gh]
+[![License][license]](LICENSE.txt)
+
 # erlang-oauth
 # erlang-oauth
 
 
 An Erlang implementation of [The OAuth 1.0 Protocol](https://tools.ietf.org/html/rfc5849).
 An Erlang implementation of [The OAuth 1.0 Protocol](https://tools.ietf.org/html/rfc5849).
 
 
-Functions for generating signatures (client side), verifying signatures (server side),
-and some convenience functions for making OAuth HTTP requests (client side).
+There are functions for 
+  - generating signatures (*client* side), 
+  - verifying signatures (*server* side),
+  - some convenience functions for making OAuth HTTP requests (*client* side).
 
 
 ## Usage
 ## Usage
 
 
@@ -93,3 +102,14 @@ The percent encoding/decoding implementations are based on [ibrowse](https://git
 ## License
 ## License
 
 
 This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
 This project is licensed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
+
+<!-- Badges -->
+[hexpm]: https://hex.pm/packages/oauth
+[hexpm version]: https://img.shields.io/hexpm/v/oauth.svg?style=flat-curcle "Hex version"
+[hexpm downloads]: https://img.shields.io/hexpm/dt/oauth.svg?style=flat-curcle
+[hexdocs documentation]: https://img.shields.io/badge/hex-docs-purple.svg?style=flat-curcle
+[hexdocs]: https://hexdocs.pm/oauth
+[gh]: https://github.com/erlangpack/erlang-oauth/actions/workflows/test.yaml
+[gh badge]: https://github.com/erlangpack/erlang-oauth/workflows/Test/badge.svg
+[erlang version badge]: https://img.shields.io/badge/Supported%20Erlang%2FOTP-21%20to%2023-blue.svg?style=flat-curcle
+[license]: https://img.shields.io/badge/License-MIT-blue.svg "MIT"

+ 3 - 0
doc/overview.edoc

@@ -0,0 +1,3 @@
+@title eralng-oauth
+@doc An Erlang implementation of [https://tools.ietf.org/html/rfc5849 The OAuth 1.0 Protocol]
+@copyright MIT 

+ 75 - 0
doc/style.css

@@ -0,0 +1,75 @@
+/* standard EDoc style sheet */
+body {
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+      	margin-left: .25in;
+       	margin-right: .2in;
+       	margin-top: 0.2in;
+       	margin-bottom: 0.2in;
+       	color: #000000;
+       	background-color: #ffffff;
+}
+h1,h2 {
+ 	margin-left: -0.2in;
+}
+div.navbar {
+	background-color: #add8e6;
+	padding: 0.2em;
+}
+h2.indextitle {
+	padding: 0.4em;
+	background-color: #add8e6;
+}
+h3.function,h3.typedecl {
+	background-color: #add8e6;
+ 	padding-left: 1em;
+}
+div.spec {
+ 	margin-left: 2em;
+	
+	background-color: #eeeeee;
+}
+a.module {
+	text-decoration:none
+}
+a.module:hover {
+	background-color: #eeeeee;
+}
+ul.definitions {
+	list-style-type: none;
+}
+ul.index {
+	list-style-type: none;
+	background-color: #eeeeee;
+}
+
+/*
+ * Minor style tweaks
+ */
+ul {
+	list-style-type: square;
+}
+table {
+	border-collapse: collapse;
+}
+td {
+	padding: 3px;
+	vertical-align: middle;
+}
+
+/*
+Tune styles
+*/
+
+table[summary="navigation bar"] {
+	background-image: url('http://zotonic.com/lib/images/logo.png');
+	background-repeat: no-repeat;
+	background-position: center;
+}
+
+code, p>tt, a>tt {
+	font-size: 1.2em;
+}
+
+p {
+	line-height: 1.5;
+}

+ 32 - 11
rebar.config

@@ -10,17 +10,38 @@
  ]
  ]
 }.
 }.
 
 
-{xref_checks, [
-    undefined_function_calls,
-    locals_not_used,
-    deprecated_function_calls
+{profiles, [
+    {test, [
+	    {xref_checks, [
+            undefined_function_calls,
+            locals_not_used,
+            deprecated_function_calls
+        ]},
+		{xref_ignores, [
+        ]}
+        
+    ]},
+	{edoc_private, [
+		{edoc_opts, [
+			{private, true}
+		]}
+	]},
+	{check, [
+		{dialyzer, [
+		  {warnings, [
+			  no_return
+		  ]}
+		]},
+		
+		{erl_opts, [
+			debug_info
+		]}
+	]}
 ]}.
 ]}.
 
 
-{xref_ignores, [
-]}.
 
 
-{dialyzer, [
-  {warnings, [
-      % no_return
-  ]}
-]}.
+{edoc_opts, [
+    {preprocess, true}, 
+	{stylesheet, "style.css"},
+	{pretty_printer,  erl_pp}
+]}.

+ 566 - 0
src/oauth.erl

@@ -36,77 +36,388 @@
 
 
 -include_lib("public_key/include/public_key.hrl").
 -include_lib("public_key/include/public_key.hrl").
 
 
+-type signature_method() :: plaintext | hmac_sha1 | rsa_sha1.
+%% <ul>
+%%  <li>`PLAINTEXT' is a simple method for a more efficient implementation which offloads 
+%% most of the security requirements to the HTTPS layer.</li>
+%% <li>`HMAC-SHA1' offers a simple and common algorithm that is available on most platforms 
+%% but not on all legacy devices and uses a symmetric shared secret.</li>
+%% <li>`RSA-SHA1' provides enhanced security using key-pairs but is more complex and 
+%% requires key generation and a longer learning curve.</li>
+%% </ul>
+-export_type([
+	signature_method/0
+]).
+
 -if(?OTP_RELEASE >= 22).
 -if(?OTP_RELEASE >= 22).
 -define(HMAC_SHA1(Key, Data), crypto:mac(hmac, sha, Key, Data)).
 -define(HMAC_SHA1(Key, Data), crypto:mac(hmac, sha, Key, Data)).
 -else.
 -else.
 -define(HMAC_SHA1(Key, Data), crypto:hmac(sha, Key, Data)).
 -define(HMAC_SHA1(Key, Data), crypto:hmac(sha, Key, Data)).
 -endif.
 -endif.
 
 
+%% @doc Send request using HTTP-method GET. `Token' and `TokenSecret' values are empty string.
+%% @param URL server URL
+%% @param ExtraParams signature params
+%% @param Consumer client information
+%% @equiv get(URL, ExtraParams, Consumer, "", "")
+
+-spec get(URL, ExtraParams, Consumer) -> Result when
+	URL :: httpc:url(), 
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 get(URL, ExtraParams, Consumer) ->
 get(URL, ExtraParams, Consumer) ->
   get(URL, ExtraParams, Consumer, "", "").
   get(URL, ExtraParams, Consumer, "", "").
 
 
+%% @doc Send request using HTTP-method GET.
+%% @param URL server URL
+%% @param ExtraParams signature params
+%% @param Consumer client information
+%% @param Token sign token
+%% @param TokenSecret sign token secret
+%% @equiv get(URL, ExtraParams, Consumer, Token, TokenSecret, [])
+
+-spec get(URL, ExtraParams, Consumer, Token, TokenSecret) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 get(URL, ExtraParams, Consumer, Token, TokenSecret) ->
 get(URL, ExtraParams, Consumer, Token, TokenSecret) ->
   get(URL, ExtraParams, Consumer, Token, TokenSecret, []).
   get(URL, ExtraParams, Consumer, Token, TokenSecret, []).
 
 
+%% @doc Send request using HTTP-method GET.
+%% @param URL server URL
+%% @param ExtraParams signature params
+%% @param Consumer client information
+%% @param Token sign token
+%% @param TokenSecret sign token secret
+%% @param HttpcOptions HTTP options
+
+-spec get(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	HttpcOptions :: [Option],
+	Option :: {sync, boolean()} | {stream, StreamTo} | {body_format, BodyFormat} |
+				{full_result, boolean()} | {headers_as_is, boolean()} | {socket_opts, SocketOpts} | 
+				{receiver, Receiver},
+	StreamTo :: none | self | {self, once} | httpc:filename(),
+	BodyFormat :: 'string' | 'binary',
+	SocketOpts :: [httpc:socket_opt()],
+	Receiver :: Receiver :: pid() | function() | {Module, Function, Args},
+	Module :: atom(),
+	Function :: atom(),
+	Args :: list(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 get(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
 get(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
   SignedParams = sign("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
   SignedParams = sign("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
   http_request(get, {uri(URL, SignedParams), []}, HttpcOptions).
   http_request(get, {uri(URL, SignedParams), []}, HttpcOptions).
 
 
+%% @doc Send request using HTTP-method POST.
+%% @equiv post(URL, ExtraParams, Consumer, "", "")
+
+-spec post(URL, ExtraParams, Consumer) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 post(URL, ExtraParams, Consumer) ->
 post(URL, ExtraParams, Consumer) ->
   post(URL, ExtraParams, Consumer, "", "").
   post(URL, ExtraParams, Consumer, "", "").
 
 
+%% @doc Send request using HTTP-method POST.
+%% @equiv post(URL, ExtraParams, Consumer, Token, TokenSecret, [])
+
+-spec post(URL, ExtraParams, Consumer, Token, TokenSecret) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 post(URL, ExtraParams, Consumer, Token, TokenSecret) ->
 post(URL, ExtraParams, Consumer, Token, TokenSecret) ->
   post(URL, ExtraParams, Consumer, Token, TokenSecret, []).
   post(URL, ExtraParams, Consumer, Token, TokenSecret, []).
 
 
+%% @doc Send request using HTTP-method POST.
+
+-spec post(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	HttpcOptions :: [Option],
+	Option :: {sync, boolean()} | {stream, StreamTo} | {body_format, BodyFormat} |
+				{full_result, boolean()} | {headers_as_is, boolean()} | {socket_opts, SocketOpts} | 
+				{receiver, Receiver},
+	StreamTo :: none | self | {self, once} | httpc:filename(),
+	BodyFormat :: 'string' | 'binary',
+	SocketOpts :: [httpc:socket_opt()],
+	Receiver :: Receiver :: pid() | function() | {Module, Function, Args},
+	Module :: atom(),
+	Function :: atom(),
+	Args :: list(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 post(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
 post(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
   SignedParams = sign("POST", URL, ExtraParams, Consumer, Token, TokenSecret),
   SignedParams = sign("POST", URL, ExtraParams, Consumer, Token, TokenSecret),
   http_request(post, {URL, [], "application/x-www-form-urlencoded", uri_string:compose_query(SignedParams)}, HttpcOptions).
   http_request(post, {URL, [], "application/x-www-form-urlencoded", uri_string:compose_query(SignedParams)}, HttpcOptions).
 
 
+%% @doc Send request using HTTP-method DELETE.
+%% @equiv delete(URL, ExtraParams, Consumer, "", "")
+
+-spec delete(URL, ExtraParams, Consumer) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 delete(URL, ExtraParams, Consumer) ->
 delete(URL, ExtraParams, Consumer) ->
   delete(URL, ExtraParams, Consumer, "", "").
   delete(URL, ExtraParams, Consumer, "", "").
 
 
+%% @doc Send request using HTTP-method DELETE.
+%% @equiv delete(URL, ExtraParams, Consumer, Token, TokenSecret, [])
+
+-spec delete(URL, ExtraParams, Consumer, Token, TokenSecret) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 delete(URL, ExtraParams, Consumer, Token, TokenSecret) ->
 delete(URL, ExtraParams, Consumer, Token, TokenSecret) ->
   delete(URL, ExtraParams, Consumer, Token, TokenSecret, []).
   delete(URL, ExtraParams, Consumer, Token, TokenSecret, []).
 
 
+%% @doc Send request using HTTP-method DELETE.
+
+-spec delete(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	HttpcOptions :: [Option],
+	Option :: {sync, boolean()} | {stream, StreamTo} | {body_format, BodyFormat} |
+				{full_result, boolean()} | {headers_as_is, boolean()} | {socket_opts, SocketOpts} | 
+				{receiver, Receiver},
+	StreamTo :: none | self | {self, once} | httpc:filename(),
+	BodyFormat :: 'string' | 'binary',
+	SocketOpts :: [httpc:socket_opt()],
+	Receiver :: Receiver :: pid() | function() | {Module, Function, Args},
+	Module :: atom(),
+	Function :: atom(),
+	Args :: list(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 delete(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
 delete(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
   SignedParams = sign("DELETE", URL, ExtraParams, Consumer, Token, TokenSecret),
   SignedParams = sign("DELETE", URL, ExtraParams, Consumer, Token, TokenSecret),
   http_request(delete, {URL, [], "application/x-www-form-urlencoded", uri_string:compose_query(SignedParams)}, HttpcOptions).
   http_request(delete, {URL, [], "application/x-www-form-urlencoded", uri_string:compose_query(SignedParams)}, HttpcOptions).
 
 
+%% @doc Send request using HTTP-method PUT.
+%% @equiv put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, [])
+
+-spec put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	ContentType :: httpc:content_type(), 
+	Body :: httpc:body(),
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret) ->
 put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret) ->
   put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, []).
   put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, []).
 
 
+%% @doc Send request using HTTP-method PUT.
+
+-spec put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, HttpcOptions) -> Result when
+	URL :: httpc:url(),
+	ExtraParams :: list(), 
+	ContentType :: httpc:content_type(), 
+	Body :: httpc:body(),
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(), 
+	TokenSecret :: string(),
+	HttpcOptions :: [Option],
+	Option :: {sync, boolean()} | {stream, StreamTo} | {body_format, BodyFormat} |
+				{full_result, boolean()} | {headers_as_is, boolean()} | {socket_opts, SocketOpts} | 
+				{receiver, Receiver},
+	StreamTo :: none | self | {self, once} | httpc:filename(),
+	BodyFormat :: 'string' | 'binary',
+	SocketOpts :: [httpc:socket_opt()],
+	Receiver :: Receiver :: pid() | function() | {Module, Function, Args},
+	Module :: atom(),
+	Function :: atom(),
+	Args :: list(),
+	Result :: {ok, RequestTokenResponse} | {ok, saved_to_file} | {error, Reason},
+	RequestTokenResponse :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, HttpcOptions) ->
 put(URL, ExtraParams, {ContentType, Body}, Consumer, Token, TokenSecret, HttpcOptions) ->
   SignedParams = sign("PUT", URL, ExtraParams, Consumer, Token, TokenSecret),
   SignedParams = sign("PUT", URL, ExtraParams, Consumer, Token, TokenSecret),
   http_request(put, {uri(URL, SignedParams), [], ContentType, Body}, HttpcOptions).
   http_request(put, {uri(URL, SignedParams), [], ContentType, Body}, HttpcOptions).
 
 
+%% @doc Build URI using provided parameters.
+-spec uri(Base, Params) -> Result when
+	Base :: string(), 
+	Params :: QueryList,
+	QueryList :: [{unicode:chardata(), unicode:chardata() | true}],
+	Result :: string().
 uri(Base, []) ->
 uri(Base, []) ->
   Base;
   Base;
 uri(Base, Params) ->
 uri(Base, Params) ->
   lists:concat([Base, "?", uri_string:compose_query(Params)]).
   lists:concat([Base, "?", uri_string:compose_query(Params)]).
 
 
+%% @doc Get encode authorization paramaters.
+%% @returns {"Authorization", "OAuth " ++ string()}
+
+-spec header(Params) -> Result when
+	Params :: [{Key, Value}],
+	Key :: Term, 
+	Value :: Term,
+	Term :: integer() | atom() | binary() | list(),
+	Result :: {string(), string()}.
 header(Params) ->
 header(Params) ->
   {"Authorization", "OAuth " ++ header_params_encode(Params)}.
   {"Authorization", "OAuth " ++ header_params_encode(Params)}.
 
 
+%% @doc Get `oauth_token'.
+
+-spec token(Params) -> Result when
+	Params :: [term()],
+	Result :: string().
 token(Params) ->
 token(Params) ->
   proplists:get_value("oauth_token", Params).
   proplists:get_value("oauth_token", Params).
 
 
+%% @doc Get `oauth_token_secret'.
+
+-spec token_secret(Params) -> Result when
+	Params :: [term()],
+	Result :: string().
 token_secret(Params) ->
 token_secret(Params) ->
   proplists:get_value("oauth_token_secret", Params).
   proplists:get_value("oauth_token_secret", Params).
 
 
+-spec consumer_key(Consumer) -> Result when
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: Key.
 consumer_key(_Consumer={Key, _, _}) ->
 consumer_key(_Consumer={Key, _, _}) ->
   Key.
   Key.
 
 
+-spec consumer_secret(Consumer) -> Result when
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: Secret.
 consumer_secret(_Consumer={_, Secret, _}) ->
 consumer_secret(_Consumer={_, Secret, _}) ->
   Secret.
   Secret.
 
 
+-spec signature_method(Consumer) -> Result when
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: Method.
 signature_method(_Consumer={_, _, Method}) ->
 signature_method(_Consumer={_, _, Method}) ->
   Method.
   Method.
 
 
+%% @doc Get signature parameters.
+%% @param HttpMethod "GET" | "POST" | "DELETE" | "PUT"
+
+-spec sign(HttpMethod, URL, Params, Consumer, Token, TokenSecret) -> Result when
+	HttpMethod :: string(),
+	URL :: httpc:url(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Token :: string(),
+	TokenSecret :: string(),
+	Result :: [{string(),string()}].
 sign(HttpMethod, URL, Params, Consumer, Token, TokenSecret) ->
 sign(HttpMethod, URL, Params, Consumer, Token, TokenSecret) ->
   SignatureParams = signature_params(Consumer, Params, Token),
   SignatureParams = signature_params(Consumer, Params, Token),
   Signature = signature(HttpMethod, URL, SignatureParams, Consumer, TokenSecret),
   Signature = signature(HttpMethod, URL, SignatureParams, Consumer, TokenSecret),
   [{"oauth_signature", Signature} | SignatureParams].
   [{"oauth_signature", Signature} | SignatureParams].
 
 
+-spec signature_params(Consumer, Params, Token) -> Result when
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Term :: integer() | atom() | binary() | list(),
+	Token :: string(),
+	Result :: [{string(), string()}].
 signature_params(Consumer, Params, "") ->
 signature_params(Consumer, Params, "") ->
   signature_params(Consumer, Params);
   signature_params(Consumer, Params);
 signature_params(Consumer, Params, Token) ->
 signature_params(Consumer, Params, Token) ->
@@ -123,6 +434,21 @@ signature_params(Consumer, Params) ->
   | Params
   | Params
   ].
   ].
 
 
+%% @doc Verify signature by provided signature method.
+-spec verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) -> Result when
+	Signature :: base64:base64_string() | base64:base64_binary(),
+	HttpMethod :: string(), 
+	URL :: httpc:url(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Term :: integer() | atom() | binary() | list(),
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: boolean().
 verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
 verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
   case signature_method(Consumer) of
   case signature_method(Consumer) of
     plaintext ->
     plaintext ->
@@ -133,6 +459,20 @@ verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
       rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer)
       rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer)
   end.
   end.
 
 
+%% @doc Get signature using provided signature method.
+-spec signature(HttpMethod, URL, Params, Consumer, TokenSecret) -> Result when
+	HttpMethod :: string(), 
+	URL :: httpc:url(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Term :: integer() | atom() | binary() | list(),
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: base64:base64_string().
 signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
 signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
   case signature_method(Consumer) of
   case signature_method(Consumer) of
     plaintext ->
     plaintext ->
@@ -143,6 +483,15 @@ signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
       rsa_sha1_signature(HttpMethod, URL, Params, Consumer)
       rsa_sha1_signature(HttpMethod, URL, Params, Consumer)
   end.
   end.
 
 
+%% @doc Get selected signature method.
+%% @returns "PLAINTEXT" | "HMAC-SHA1" | "RSA-SHA1"
+
+-spec signature_method_string(Consumer) -> Result when
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: string().
 signature_method_string(Consumer) ->
 signature_method_string(Consumer) ->
   case signature_method(Consumer) of
   case signature_method(Consumer) of
     plaintext ->
     plaintext ->
@@ -153,44 +502,163 @@ signature_method_string(Consumer) ->
       "RSA-SHA1"
       "RSA-SHA1"
   end.
   end.
 
 
+%% @doc Build plain text Signature
+
+-spec plaintext_signature(Consumer, TokenSecret) -> Result when
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: base64:base64_string().
 plaintext_signature(Consumer, TokenSecret) ->
 plaintext_signature(Consumer, TokenSecret) ->
   uri_join([consumer_secret(Consumer), TokenSecret]).
   uri_join([consumer_secret(Consumer), TokenSecret]).
 
 
+%% @doc Verify plain text Signature
+
+-spec plaintext_verify(Signature, Consumer, TokenSecret) -> Result when
+	Signature :: base64:base64_string() | base64:base64_binary(),
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: boolean().
 plaintext_verify(Signature, Consumer, TokenSecret) ->
 plaintext_verify(Signature, Consumer, TokenSecret) ->
   verify_in_constant_time(Signature, plaintext_signature(Consumer, TokenSecret)).
   verify_in_constant_time(Signature, plaintext_signature(Consumer, TokenSecret)).
 
 
+%% @doc Build HMAC-SHA1 Signature
+
+-spec hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret) -> Result when
+	HttpMethod :: string(),
+	URL :: httpc:url(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: base64:base64_string().
 hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
 hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
   BaseString = signature_base_string(HttpMethod, URL, Params),
   BaseString = signature_base_string(HttpMethod, URL, Params),
   hmac_sha1_signature(BaseString, Consumer, TokenSecret).
   hmac_sha1_signature(BaseString, Consumer, TokenSecret).
 
 
+%% @doc Build HMAC-SHA1 Signature
+
+-spec hmac_sha1_signature(BaseString, Consumer, TokenSecret) -> Result when
+	BaseString :: string() | binary(),
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: base64:base64_string().
 hmac_sha1_signature(BaseString, Consumer, TokenSecret) ->
 hmac_sha1_signature(BaseString, Consumer, TokenSecret) ->
   Key = uri_join([consumer_secret(Consumer), TokenSecret]),
   Key = uri_join([consumer_secret(Consumer), TokenSecret]),
   base64:encode_to_string(?HMAC_SHA1(Key, BaseString)).
   base64:encode_to_string(?HMAC_SHA1(Key, BaseString)).
 
 
+%% @doc Verify HMAC-SHA1 Signature
+
+-spec hmac_sha1_verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) -> Result when
+	Signature :: base64:base64_string() | base64:base64_binary(),
+	HttpMethod :: string(), 
+	URL :: httpc:url(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: boolean().
 hmac_sha1_verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
 hmac_sha1_verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
   verify_in_constant_time(Signature, hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret)).
   verify_in_constant_time(Signature, hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret)).
 
 
+%% @doc Verify HMAC-SHA1 Signature
+
+-spec hmac_sha1_verify(Signature, BaseString, Consumer, TokenSecret) -> Result when
+	Signature :: base64:base64_string() | base64:base64_binary(),
+	BaseString :: string() | binary(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	TokenSecret :: string(),
+	Result :: boolean().
 hmac_sha1_verify(Signature, BaseString, Consumer, TokenSecret) ->
 hmac_sha1_verify(Signature, BaseString, Consumer, TokenSecret) ->
   verify_in_constant_time(Signature, hmac_sha1_signature(BaseString, Consumer, TokenSecret)).
   verify_in_constant_time(Signature, hmac_sha1_signature(BaseString, Consumer, TokenSecret)).
 
 
+%%@doc Build RSA_SHA1 Signature
+
+-spec rsa_sha1_signature(HttpMethod, URL, Params, Consumer) -> Result when
+	HttpMethod :: string(),
+	URL :: httpc:url(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: base64:base64_string().
 rsa_sha1_signature(HttpMethod, URL, Params, Consumer) ->
 rsa_sha1_signature(HttpMethod, URL, Params, Consumer) ->
   BaseString = signature_base_string(HttpMethod, URL, Params),
   BaseString = signature_base_string(HttpMethod, URL, Params),
   rsa_sha1_signature(BaseString, Consumer).
   rsa_sha1_signature(BaseString, Consumer).
 
 
+%%@doc Build RSA_SHA1 Signature
+
+-spec rsa_sha1_signature(BaseString, Consumer) -> Result when
+	BaseString :: string() | binary(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(),
+	Result :: base64:base64_string().
 rsa_sha1_signature(BaseString, Consumer) ->
 rsa_sha1_signature(BaseString, Consumer) ->
   Key = read_private_key(consumer_secret(Consumer)),
   Key = read_private_key(consumer_secret(Consumer)),
   base64:encode_to_string(public_key:sign(list_to_binary(BaseString), sha, Key)).
   base64:encode_to_string(public_key:sign(list_to_binary(BaseString), sha, Key)).
 
 
+%%@doc Verify RSA_SHA1 Signature
+
+-spec rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer) -> Result when
+	Signature :: base64:base64_string() | base64:base64_binary(),
+	HttpMethod :: string(),
+	URL :: httpc:url(),
+	Params :: [{ParamsKey, ParamsValue}],
+	ParamsKey :: Term, 
+	ParamsValue :: Term,
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(), 
+	Result :: boolean().
 rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer) ->
 rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer) ->
   BaseString = signature_base_string(HttpMethod, URL, Params),
   BaseString = signature_base_string(HttpMethod, URL, Params),
   rsa_sha1_verify(Signature, BaseString, Consumer).
   rsa_sha1_verify(Signature, BaseString, Consumer).
 
 
+%%@doc Verify RSA_SHA1 Signature
+
+-spec rsa_sha1_verify(Signature, BaseString, Consumer) -> Result when
+	Signature :: base64:base64_string() | base64:base64_binary(), 
+	BaseString :: string() | binary(), 
+	Consumer :: {Key, Secret, Method},
+	Key :: string(), 
+	Secret :: string(), 
+	Method :: signature_method(), 
+	Result :: boolean().
 rsa_sha1_verify(Signature, BaseString, Consumer) when is_binary(BaseString) ->
 rsa_sha1_verify(Signature, BaseString, Consumer) when is_binary(BaseString) ->
   Key = read_cert_key(consumer_secret(Consumer)),
   Key = read_cert_key(consumer_secret(Consumer)),
   public_key:verify(BaseString, sha, base64:decode(Signature), Key);
   public_key:verify(BaseString, sha, base64:decode(Signature), Key);
 rsa_sha1_verify(Signature, BaseString, Consumer) when is_list(BaseString) ->
 rsa_sha1_verify(Signature, BaseString, Consumer) when is_list(BaseString) ->
   rsa_sha1_verify(Signature, list_to_binary(BaseString), Consumer).
   rsa_sha1_verify(Signature, list_to_binary(BaseString), Consumer).
 
 
+-spec verify_in_constant_time(X,Y) -> Result when
+	X :: list(),
+	Y :: list(),
+	Result :: boolean().
 verify_in_constant_time(X, Y) when is_list(X) and is_list(Y) ->
 verify_in_constant_time(X, Y) when is_list(X) and is_list(Y) ->
   case length(X) == length(Y) of
   case length(X) == length(Y) of
     true ->
     true ->
@@ -199,14 +667,33 @@ verify_in_constant_time(X, Y) when is_list(X) and is_list(Y) ->
       false
       false
   end.
   end.
 
 
+-spec verify_in_constant_time(X, Y, Result) -> FunResult when
+	X :: list(),
+	Y :: list(),
+	Result :: integer(),
+	FunResult :: boolean().
 verify_in_constant_time([X | RestX], [Y | RestY], Result) ->
 verify_in_constant_time([X | RestX], [Y | RestY], Result) ->
   verify_in_constant_time(RestX, RestY, (X bxor Y) bor Result);
   verify_in_constant_time(RestX, RestY, (X bxor Y) bor Result);
 verify_in_constant_time([], [], Result) ->
 verify_in_constant_time([], [], Result) ->
   Result == 0.
   Result == 0.
 
 
+%% @doc Build signature string.
+-spec signature_base_string(HttpMethod, URL, Params) -> Result when
+	HttpMethod :: string(), 
+	URL  :: uri_string:uri_string(), 
+	Params :: list(),
+	Result :: uri_string:uri_string().
 signature_base_string(HttpMethod, URL, Params) ->
 signature_base_string(HttpMethod, URL, Params) ->
   uri_join([HttpMethod, base_string_uri(URL), params_encode(Params)]).
   uri_join([HttpMethod, base_string_uri(URL), params_encode(Params)]).
 
 
+%%@doc Encode provided parameters.
+
+-spec params_encode(Params) -> Result when
+	Params :: [{Key, Value}],
+	Key :: Term,
+	Value :: Term,
+	Term :: integer() | atom() | binary() | list(),
+	Result :: string().
 params_encode(Params) ->
 params_encode(Params) ->
   % cf. http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
   % cf. http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
   Encoded = [{uri_encode(K), uri_encode(V)} || {K, V} <- Params],
   Encoded = [{uri_encode(K), uri_encode(V)} || {K, V} <- Params],
@@ -214,14 +701,42 @@ params_encode(Params) ->
   Concatenated = [lists:concat([K, "=", V]) || {K, V} <- Sorted],
   Concatenated = [lists:concat([K, "=", V]) || {K, V} <- Sorted],
   string:join(Concatenated, "&").
   string:join(Concatenated, "&").
 
 
+%%@doc Decode provided parameters from query string.
+
+-spec params_decode(Response) -> Result when
+	Response :: {{term(), term(), term()}, term(), Body},
+	Body :: QueryString,
+	QueryString :: uri_string:uri_string(),
+	Result :: QueryList,
+	QueryList :: [{unicode:chardata(), unicode:chardata() | true}] | uri_string:error().
 params_decode(_Response={{_, _, _}, _, Body}) ->
 params_decode(_Response={{_, _, _}, _, Body}) ->
   uri_string:dissect_query(Body).
   uri_string:dissect_query(Body).
 
 
+-spec http_request(Method, Request, Options) -> Result when
+	Method :: httpc:method(),
+	Request :: httpc:request(), 
+	Options :: [Option],
+	Option :: {sync, boolean()} | {stream, StreamTo} | {body_format, BodyFormat} |
+				{full_result, boolean()} | {headers_as_is, boolean()} | {socket_opts, SocketOpts} | 
+				{receiver, Receiver},
+	StreamTo :: none | self | {self, once} | httpc:filename(),
+	BodyFormat :: 'string' | 'binary',
+	SocketOpts :: [httpc:socket_opt()],
+	Receiver :: Receiver :: pid() | function() | {Module, Function, Args},
+	Module :: atom(),
+	Function :: atom(),
+	Args :: list(),
+	Result :: {ok, OkResult} | {ok, saved_to_file} | {error, Reason},
+	OkResult :: {httpc:status_line(), httpc:headers(), Body} | {httpc:status_code(), Body} | httpc:request_id(),
+	Body :: httpc:http_string() | binary(),
+	Reason :: term().
 http_request(Method, Request, Options) ->
 http_request(Method, Request, Options) ->
   httpc:request(Method, Request, [{autoredirect, false}], Options).
   httpc:request(Method, Request, [{autoredirect, false}], Options).
 
 
 -define(unix_epoch, 62167219200).
 -define(unix_epoch, 62167219200).
 
 
+-spec unix_timestamp() -> Result when
+	Result :: non_neg_integer().
 unix_timestamp() ->
 unix_timestamp() ->
   calendar:datetime_to_gregorian_seconds(calendar:universal_time()) - ?unix_epoch.
   calendar:datetime_to_gregorian_seconds(calendar:universal_time()) - ?unix_epoch.
 
 
@@ -236,22 +751,52 @@ read_cert_key(#'OTPTBSCertificate'{subjectPublicKeyInfo=Info}) ->
 read_cert_key(#'OTPSubjectPublicKeyInfo'{subjectPublicKey=Key}) ->
 read_cert_key(#'OTPSubjectPublicKeyInfo'{subjectPublicKey=Key}) ->
   Key.
   Key.
 
 
+-spec read_private_key(Path) -> Result when
+	Path :: file:name_all(),
+	Result :: term().
 read_private_key(Path) ->
 read_private_key(Path) ->
   {ok, Contents} = file:read_file(Path),
   {ok, Contents} = file:read_file(Path),
   [Info] = public_key:pem_decode(Contents),
   [Info] = public_key:pem_decode(Contents),
   public_key:pem_entry_decode(Info).
   public_key:pem_entry_decode(Info).
 
 
+%% @doc Encode authorization paramaters list as a string.
+%% @returns Encoded authorization paramaters list as a string.
+-spec header_params_encode(Params) -> Result when
+	Params :: [{Key, Value}],
+	Key :: Term, 
+	Value :: Term,
+	Term :: integer() | atom() | binary() | list(),
+	Result :: string().
 header_params_encode(Params) ->
 header_params_encode(Params) ->
   intercalate(", ", [lists:concat([uri_encode(K), "=\"", uri_encode(V), "\""]) || {K, V} <- Params]).
   intercalate(", ", [lists:concat([uri_encode(K), "=\"", uri_encode(V), "\""]) || {K, V} <- Params]).
 
 
+%% @doc Dencode authorization paramaters list.
+%% @returns Dencoded authorization paramaters list.
+-spec header_params_decode(String) -> Result when
+	String :: string(),
+	Result :: [Param],
+	Param :: {Key, Value},
+	Key :: string(),
+	Value :: string().
 header_params_decode(String) ->
 header_params_decode(String) ->
   [header_param_decode(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""].
   [header_param_decode(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""].
 
 
+-spec header_param_decode(Param) -> Result when
+	Param :: SeparatorList,
+	SeparatorList :: string(),
+	Result :: {Key, Value},
+	Key :: string(),
+	Value :: string().
 header_param_decode(Param) ->
 header_param_decode(Param) ->
   [Key, QuotedValue] = string:tokens(Param, "="),
   [Key, QuotedValue] = string:tokens(Param, "="),
   Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2),
   Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2),
   {uri_decode(Key), uri_decode(Value)}.
   {uri_decode(Key), uri_decode(Value)}.
 
 
+-spec base_string_uri(Str) -> Result when
+	Str :: URIString,
+	URIString :: uri_string:uri_string(),
+	Result :: URIString,
+	URIString :: uri_string:uri_string() | uri_string:error().
 base_string_uri(Str) ->
 base_string_uri(Str) ->
   % https://tools.ietf.org/html/rfc5849#section-3.4.1.2
   % https://tools.ietf.org/html/rfc5849#section-3.4.1.2
   Map1 = uri_string:parse(Str),
   Map1 = uri_string:parse(Str),
@@ -270,9 +815,18 @@ without_default_port("https", #{ port := 443 } = Map) ->
 without_default_port(_Scheme, Map) ->
 without_default_port(_Scheme, Map) ->
   Map.
   Map.
 
 
+%% @equiv uri_join(Values, "&")
+
+-spec uri_join(Values) -> Result when
+	Values :: list(integer() | atom() | binary() | list()),
+	Result :: string().
 uri_join(Values) ->
 uri_join(Values) ->
   uri_join(Values, "&").
   uri_join(Values, "&").
 
 
+-spec uri_join(Values, Separator) -> Result when
+	Values :: list(integer() | atom() | binary() | list()),
+	Separator :: string(),
+	Result :: string().
 uri_join(Values, Separator) ->
 uri_join(Values, Separator) ->
   string:join(lists:map(fun uri_encode/1, Values), Separator).
   string:join(lists:map(fun uri_encode/1, Values), Separator).
 
 
@@ -286,6 +840,9 @@ intersperse(_, [X]) ->
 intersperse(Sep, [X | Xs]) ->
 intersperse(Sep, [X | Xs]) ->
   [X, Sep | intersperse(Sep, Xs)].
   [X, Sep | intersperse(Sep, Xs)].
 
 
+-spec uri_encode(Term) -> Result when
+	Term :: integer() | atom() | binary() | list(),
+	Result :: string().
 uri_encode(Term) when is_integer(Term) ->
 uri_encode(Term) when is_integer(Term) ->
   integer_to_list(Term);
   integer_to_list(Term);
 uri_encode(Term) when is_atom(Term) ->
 uri_encode(Term) when is_atom(Term) ->
@@ -297,6 +854,10 @@ uri_encode(Term) when is_list(Term) ->
 
 
 -define(is_alphanum(C), C >= $A, C =< $Z; C >= $a, C =< $z; C >= $0, C =< $9).
 -define(is_alphanum(C), C >= $A, C =< $Z; C >= $a, C =< $z; C >= $0, C =< $9).
 
 
+-spec uri_encode(Term, Acc) -> Result when
+	Term :: string(),
+	Acc :: list(),
+	Result :: string().
 uri_encode([X | T], Acc) when ?is_alphanum(X); X =:= $-; X =:= $_; X =:= $.; X =:= $~ ->
 uri_encode([X | T], Acc) when ?is_alphanum(X); X =:= $-; X =:= $_; X =:= $.; X =:= $~ ->
   uri_encode(T, [X | Acc]);
   uri_encode(T, [X | Acc]);
 uri_encode([X | T], Acc) ->
 uri_encode([X | T], Acc) ->
@@ -305,6 +866,11 @@ uri_encode([X | T], Acc) ->
 uri_encode([], Acc) ->
 uri_encode([], Acc) ->
   Acc.
   Acc.
 
 
+%% @equiv uri_decode(Str, [])
+
+-spec uri_decode(Str) -> Result when
+	Str :: string(),
+	Result :: string().
 uri_decode(Str) when is_list(Str) ->
 uri_decode(Str) when is_list(Str) ->
   uri_decode(Str, []).
   uri_decode(Str, []).