Full Code of florinpatrascu/bolt_sips for AI

master b21901a46ed1 cached
153 files
507.4 KB
159.5k tokens
452 symbols
1 requests
Download .txt
Showing preview only (548K chars total). Download the full file or copy to clipboard to get everything.
Repository: florinpatrascu/bolt_sips
Branch: master
Commit: b21901a46ed1
Files: 153
Total size: 507.4 KB

Directory structure:
gitextract_6ctcdb7g/

├── .credo.exs
├── .dialyzer_ignore.exs
├── .formatter.exs
├── .gitattributes
├── .gitignore
├── .iex.exs
├── .markdownlint.json
├── .prettierrc.yaml
├── .tool-versions
├── .travis.yml
├── CHANGELOG.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.md
├── benchees/
│   └── conn_to_local_bench.exs
├── config/
│   ├── config.exs
│   ├── dev.exs
│   └── test.exs
├── docker-compose.yml
├── docs/
│   ├── examples/
│   │   └── readme.md
│   ├── features/
│   │   ├── about-encoding.md
│   │   ├── about-transactions.md
│   │   ├── configuration.md
│   │   ├── multi-tenancy.md
│   │   ├── role-based-connections.md
│   │   ├── routing.md
│   │   ├── using-cypher.md
│   │   ├── using-temporal-and-spatial-types.md
│   │   └── using-with-phoenix.md
│   └── getting-started.md
├── lib/
│   ├── bolt_sips/
│   │   ├── application.ex
│   │   ├── enumerable_response.ex
│   │   ├── error.ex
│   │   ├── exception.ex
│   │   ├── internals/
│   │   │   ├── bolt_protocol.ex
│   │   │   ├── bolt_protocol_helper.ex
│   │   │   ├── bolt_protocol_v1.ex
│   │   │   ├── bolt_protocol_v2.ex
│   │   │   ├── bolt_protocol_v3.ex
│   │   │   ├── bolt_version_helper.ex
│   │   │   ├── error.ex
│   │   │   ├── logger.ex
│   │   │   ├── pack_stream/
│   │   │   │   ├── decoder.ex
│   │   │   │   ├── decoder_impl_v1.ex
│   │   │   │   ├── decoder_impl_v2.ex
│   │   │   │   ├── decoder_utils.ex
│   │   │   │   ├── decoder_v1.ex
│   │   │   │   ├── decoder_v2.ex
│   │   │   │   ├── decoder_v3.ex
│   │   │   │   ├── encoder.ex
│   │   │   │   ├── encoder_helper.ex
│   │   │   │   ├── encoder_v1.ex
│   │   │   │   ├── encoder_v2.ex
│   │   │   │   ├── encoder_v3.ex
│   │   │   │   ├── error.ex
│   │   │   │   ├── markers.ex
│   │   │   │   ├── message/
│   │   │   │   │   ├── decoder.ex
│   │   │   │   │   ├── encoder.ex
│   │   │   │   │   ├── encoder_v1.ex
│   │   │   │   │   ├── encoder_v2.ex
│   │   │   │   │   ├── encoder_v3.ex
│   │   │   │   │   └── signatures.ex
│   │   │   │   ├── message.ex
│   │   │   │   ├── utils.ex
│   │   │   │   ├── v1.ex
│   │   │   │   └── v2.ex
│   │   │   └── pack_stream.ex
│   │   ├── metadata.ex
│   │   ├── protocol.ex
│   │   ├── query.ex
│   │   ├── query_statement.ex
│   │   ├── response.ex
│   │   ├── response_encoder/
│   │   │   ├── json/
│   │   │   │   ├── jason.ex
│   │   │   │   └── poison.ex
│   │   │   └── json.ex
│   │   ├── response_encoder.ex
│   │   ├── router.ex
│   │   ├── routing/
│   │   │   ├── connection_supervisor.ex
│   │   │   ├── load_balancer.ex
│   │   │   └── routing_table.ex
│   │   ├── socket.ex
│   │   ├── types.ex
│   │   ├── types_helper.ex
│   │   └── utils.ex
│   ├── bolt_sips.ex
│   └── mix/
│       └── tasks/
│           └── cypher.ex
├── mix.exs
├── requirements.txt
└── test/
    ├── bolt_sips/
    │   ├── internals/
    │   │   ├── bolt_protocol_all_bolt_version_test.exs
    │   │   ├── bolt_protocol_bolt_v1_test.exs
    │   │   ├── bolt_protocol_bolt_v2_test.exs
    │   │   ├── bolt_protocol_bolt_v3_test.exs
    │   │   ├── bolt_protocol_v1_test.exs
    │   │   ├── bolt_protocol_v3_test.exs
    │   │   ├── bolt_version_helper_test.exs
    │   │   ├── logger_test.exs
    │   │   └── pack_stream/
    │   │       ├── decoder_test.exs
    │   │       ├── decoder_v1_test.exs
    │   │       ├── decoder_v2_test.exs
    │   │       ├── encoder_helper_test.exs
    │   │       ├── encoder_test.exs
    │   │       ├── encoder_v1_test.exs
    │   │       ├── encoder_v2_test.exs
    │   │       ├── message/
    │   │       │   ├── decoder_test.exs
    │   │       │   ├── encoder_test.exs
    │   │       │   ├── encoder_v1_test.exs
    │   │       │   └── encoder_v3_test.exs
    │   │       └── message_test.exs
    │   ├── metadata_test.exs
    │   ├── performance_test.exs
    │   ├── protocol_test.exs
    │   ├── response_encoder/
    │   │   ├── json_implementations_test.exs
    │   │   └── json_test.exs
    │   ├── response_encoder_test.exs
    │   ├── types_helpers_test.exs
    │   └── types_test.exs
    ├── boltkit_test.exs
    ├── config_test.exs
    ├── errors_test.exs
    ├── invalid_param_type_test.exs
    ├── one_test.exs
    ├── query_bolt_v2_test.exs
    ├── query_test.exs
    ├── response_test.exs
    ├── router_test.exs
    ├── routing/
    │   ├── connections_test.exs
    │   ├── crud_test.exs
    │   ├── routing_table_parser_test.exs
    │   ├── routing_test.exs
    │   └── transaction_test.exs
    ├── scripts/
    │   ├── count.bolt
    │   ├── create_a.script
    │   ├── forbidden_on_read_only_database.script
    │   ├── get_routing_table.script
    │   ├── get_routing_table_with_context.script
    │   ├── non_router.script
    │   ├── return_1.script
    │   ├── return_1_in_tx_twice.script
    │   ├── return_1_twice.script
    │   ├── return_x.bolt
    │   ├── router.script
    │   ├── router_no_readers.script
    │   └── router_no_writers.script
    ├── support/
    │   ├── boltkit_case.ex
    │   ├── conn_case.ex
    │   ├── conn_routing_case.ex
    │   ├── database.ex
    │   ├── fixture.ex
    │   └── internal_case.ex
    ├── test_helper.exs
    ├── test_large_param_set.exs
    ├── test_support.exs
    └── transaction_test.exs

================================================
FILE CONTENTS
================================================

================================================
FILE: .credo.exs
================================================
# This file contains the configuration for Credo.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
%{
  #
  # You can have as many configs as you like in the `configs:` field.
  configs: [
    %{
      #
      # Run any config using `mix credo -C <name>`. If no config name is given
      # "default" is used.
      name: "default",
      #
      # these are the files included in the analysis
      files: %{
        #
        # you can give explicit globs or simply directories
        # in the latter case `**/*.{ex,exs}` will be used
        included: ["lib/", "src/", "web/", "apps/"],
        excluded: []
      },
      #
      # The `checks:` field contains all the checks that are run. You can
      # customize the parameters of any given check by adding a second element
      # to the tuple.
      #
      # There are two ways of deactivating a check:
      # 1. deleting the check from this list
      # 2. putting `false` as second element (to quickly "comment it out"):
      #
      #      {Credo.Check.Consistency.ExceptionNames, false}
      #
      checks: [
        {Credo.Check.Consistency.ExceptionNames},
        {Credo.Check.Consistency.LineEndings, false},
        {Credo.Check.Consistency.MultiAliasImportRequireUse, false},
        {Credo.Check.Consistency.ParameterPatternMatching},
        {Credo.Check.Consistency.SpaceAroundOperators},
        {Credo.Check.Consistency.SpaceInParentheses, false},
        {Credo.Check.Consistency.TabsOrSpaces},

        # For some checks, like AliasUsage, you can only customize the priority
        # Priority values are: `low, normal, high, higher`
        {Credo.Check.Design.AliasUsage, priority: :low},
        # For others you can set parameters
        {Credo.Check.Design.DuplicatedCode, mass_threshold: 16, nodes_threshold: 2},
        {Credo.Check.Design.TagTODO, false},
        {Credo.Check.Design.TagFIXME, false},
        {Credo.Check.Readability.FunctionNames},
        {Credo.Check.Readability.LargeNumbers},
        {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 80},
        {Credo.Check.Readability.ModuleAttributeNames},
        {Credo.Check.Readability.ModuleDoc},
        {Credo.Check.Readability.ModuleNames},
        {Credo.Check.Readability.ParenthesesInCondition},
        {Credo.Check.Readability.PredicateFunctionNames},
        {Credo.Check.Readability.PreferImplicitTry},
        {Credo.Check.Readability.RedundantBlankLines},
        {Credo.Check.Readability.Specs, false},
        {Credo.Check.Readability.StringSigils},
        {Credo.Check.Readability.TrailingBlankLine},
        {Credo.Check.Readability.TrailingWhiteSpace},
        {Credo.Check.Readability.VariableNames},
        {Credo.Check.Refactor.DoubleBooleanNegation},
        {Credo.Check.Refactor.ABCSize, max_size: 50},
        {Credo.Check.Refactor.CaseTrivialMatches, false},
        {Credo.Check.Refactor.CondStatements},
        {Credo.Check.Refactor.CyclomaticComplexity},
        {Credo.Check.Refactor.FunctionArity},
        {Credo.Check.Refactor.MatchInCondition},
        {Credo.Check.Refactor.NegatedConditionsInUnless},
        {Credo.Check.Refactor.NegatedConditionsWithElse},
        {Credo.Check.Refactor.Nesting},
        {Credo.Check.Refactor.PipeChainStart},
        {Credo.Check.Refactor.CyclomaticComplexity},
        {Credo.Check.Refactor.NegatedConditionsInUnless},
        {Credo.Check.Refactor.NegatedConditionsWithElse},
        {Credo.Check.Refactor.Nesting},
        {Credo.Check.Refactor.UnlessWithElse},
        {Credo.Check.Refactor.VariableRebinding},
        {Credo.Check.Warning.BoolOperationOnSameValues},
        {Credo.Check.Warning.IExPry},
        {Credo.Check.Warning.IoInspect},
        {Credo.Check.Warning.OperationOnSameValues},
        {Credo.Check.Warning.OperationWithConstantResult},
        {Credo.Check.Warning.UnusedEnumOperation},
        {Credo.Check.Warning.UnusedFileOperation},
        {Credo.Check.Warning.UnusedKeywordOperation},
        {Credo.Check.Warning.UnusedListOperation},
        {Credo.Check.Warning.UnusedPathOperation},
        {Credo.Check.Warning.UnusedRegexOperation},
        {Credo.Check.Warning.UnusedStringOperation},
        {Credo.Check.Warning.UnusedTupleOperation}
      ]
    }
  ]
}


================================================
FILE: .dialyzer_ignore.exs
================================================
[
  ~r/__impl__.*does\ not\ exist\./
]


================================================
FILE: .formatter.exs
================================================
# Used by "mix format" and to export configuration.
export_locals_without_parens = [
  plug: 1,
  plug: 2,
  adapter: 1,
  adapter: 2
]

[
  inputs: [
    "lib/**/*.{ex,exs}",
    "test/**/*.{ex,exs}",
    "mix.exs"
  ],
  locals_without_parens: export_locals_without_parens,
  export: [locals_without_parens: export_locals_without_parens]
]


================================================
FILE: .gitattributes
================================================
* text=auto


================================================
FILE: .gitignore
================================================
### Elixir template
# The directory Mix will write compiled artifacts to.
/_build

# If you run "mix test --cover", coverage assets end up here.
/cover

# The directory Mix downloads your dependencies sources to.
/deps

# Where 3rd-party dependencies like ExDoc output generated docs.
/doc

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

/bench/snapshots
/bench/graphs
/cover

### Vim template
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~

### SublimeText template
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache

# workspace files are user-specific
*.sublime-workspace
*.sublime-project

# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project

# sftp configuration file
sftp-config.json

### OSX template
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### Tags template
# Ignore tags created by etags, ctags, gtags (GNU global) and cscope
TAGS
!TAGS/
tags
!tags/
gtags.files
GTAGS
GRTAGS
GPATH
cscope.files
cscope.out
cscope.in.out
cscope.po.out
/tmp

### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio

## Directory-based project format:
.idea/

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

.tags
.tags_sorted_by_file

### Erlang template
.eunit
*.plt
ebin
rel/example_project
.concrete/DEV_MODE

logs/
.vscode/
.elixir_ls/

# py related
*~
*.py[co]
__pycache__
.pytest_cache
*.egg-info

================================================
FILE: .iex.exs
================================================
try do
  Code.eval_file(".iex.exs", "~")
rescue
  Code.LoadError -> :rescued
end

alias Bolt.Sips.{Utils, Protocol, Router, ConnectionSupervisor, Response}
alias Bolt.Sips

Application.put_env(:tzdata, :autoupdate, :disabled)

# default port considered to be: 7687
test_config = [
  # url: 'localhost',
  url: "bolt://localhost",
  basic_auth: [username: "neo4j", password: "test"],
  pool_size: 5,
  max_overflow: 1,
  # retry the request, in case of error - in the example below the retry will
  # linearly increase the delay from 150ms following a Fibonacci pattern,
  # cap the delay at 15 seconds (the value defined by the default `:timeout`
  # parameter) and giving up after 3 attempts
  retry_linear_backoff: [delay: 150, factor: 2, tries: 3],
  read: [pool_size: 5, pool_overflow: 0],
  write: [pool_size: 1, pool_overflow: 0]
]

Mix.shell().info([
  :green,
  """
  Optional, if needed for development (Sips is the alias for Bolt.Sips):
    {:ok, _neo} = Sips.start_link(url: "bolt://neo4j:test@localhost")
    conn = Sips.conn()
  Examples:
    Sips.query!(conn, "UNWIND range(1, 10) AS n RETURN n")
    Sips.query!(conn, "RETURN 1 as n")
  --- ✄  -------------------------------------------------

  """
])


================================================
FILE: .markdownlint.json
================================================
{
    "MD013": false,
    "MD030": false
}

================================================
FILE: .prettierrc.yaml
================================================
# .prettierrc or .prettierrc.yaml
trailingComma: "es5"
tabWidth: 2
semi: false
singleQuote: true
MD013: false
MD030: false


================================================
FILE: .tool-versions
================================================
erlang 24.1.7
elixir 1.13.0-otp-24
nodejs 12.6.0
#python 3.7.3
python 3.7.3 2.7.16

ruby 2.7.5
lua 5.3.5
terraform 0.15.4
direnv 2.20.0


================================================
FILE: .travis.yml
================================================
sudo: required
services: docker
language: elixir
matrix:
  include:
    - elixir: 1.7.4
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.2.14
        - BOLT_V1_EXCLUDED=false
        - BOLT_V2_EXCLUDED=true
        - BOLT_V3_EXCLUDED=true
    - elixir: 1.7.4
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.2.14
        - BOLT_V1_EXCLUDED=false
        - BOLT_V2_EXCLUDED=true
        - BOLT_V3_EXCLUDED=true
    - elixir: 1.7.4
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.4.17
        - BOLT_V1_EXCLUDED=false
        - BOLT_V2_EXCLUDED=false
        - BOLT_V3_EXCLUDED=true
    - elixir: 1.7.4
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.5.14
        - BOLT_V1_EXCLUDED=true
        - BOLT_V2_EXCLUDED=false
        - BOLT_V3_EXCLUDED=false
    - elixir: 1.8.2
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.2.14
        - BOLT_V1_EXCLUDED=false
        - BOLT_V2_EXCLUDED=true
        - BOLT_V3_EXCLUDED=true
    - elixir: 1.8.2
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.2.14
        - BOLT_V1_EXCLUDED=false
        - BOLT_V2_EXCLUDED=true
        - BOLT_V3_EXCLUDED=true
    - elixir: 1.8.2
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.4.17
        - BOLT_V1_EXCLUDED=false
        - BOLT_V2_EXCLUDED=false
        - BOLT_V3_EXCLUDED=true
    - elixir: 1.8.2
      otp_release: 21.2
      env:
        - NEO4J_VERSION=3.5.14
        - BOLT_V1_EXCLUDED=true
        - BOLT_V2_EXCLUDED=false
        - BOLT_V3_EXCLUDED=false
    - elixir: 1.8.2
      otp_release: 21.2
      env:
        - NEO4J_VERSION=4.2.1
        - BOLT_V1_EXCLUDED=true
        - BOLT_V2_EXCLUDED=false
        - BOLT_V3_EXCLUDED=false
  exclude:
    - elixir: 1.8
      otp_release: 19.3
    - elixir: 1.8
      otp_release: 18.3
    - elixir: 1.7
      otp_release: 18.3
    - elixir: 1.6
      otp_release: 18.3
    - elixir: 1.5
      otp_release: 21.2
    - elixir: 1.4
      otp_release: 21.2
    - elixir: 1.3
      otp_release: 21.2
    - elixir: 1.3
      otp_release: 20.3
    - elixir: 1.2
      otp_release: 21.2
    - elixir: 1.2
      otp_release: 20.3

addons:
  apt:
    sources:
      - ubuntu-toolchain-r-test
    packages:
      - g++-6
      - ninja-build
cache:
  directories:
    - $HOME/cmake

env:
  global:
    - ELIXIR_ERL_OPTIONS="+T 9"
    - PATH=$HOME/cmake/bin:$PATH
    - CXX=g++-6
    - CC=gcc-6

branches:
  only:
    - master
before_install:
  - if [ ! -d "$HOME/cmake/bin" ]; then wget --no-check-certificate https://cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.sh && sh cmake-3.5.2-Linux-x86_64.sh --prefix=$HOME/cmake --exclude-subdir; fi
  - docker run --name neo4j -d -p 7687:7687 -e 'NEO4J_AUTH=neo4j/test' neo4j:$NEO4J_VERSION
  - docker logs -f neo4j | sed /Bolt\ enabled/q
script:
  - mix test --exclude routing --exclude bolt_v1:$BOLT_V1_EXCLUDED --exclude bolt_v2:$BOLT_V2_EXCLUDED --exclude bolt_v3:$BOLT_V3_EXCLUDED


================================================
FILE: CHANGELOG.md
================================================
# Changelog

## 2.1.0

Thank you https://github.com/zediogoviana, for the following improvements:

- Add configurable SSL options
- Fix local connection
- Keep the :ssl keyword to manage the options also

Dependencies updated, paving the road to switching to latest Elixir/Erlang combo.

## 2.0.11

- Issue #100: Timeout set in config in now used by queries
- DBConnection, bump dependencies

## 2.0.10

- Fix temporal types usage: microseconds are not fully available
- Review to pass test on Neo4j 4 :
  - test and doctests to use new parameter syntax (using {} is deprecated in Neo4j 4)
  - `toUpper` instead of `upper`

## 2.0.9

- fix: (Bolt.Sips.Exception) unable to encode value: -128, see: https://boltprotocol.org/v1/#ints, for details. Closes #93 Thank you, @kalamarski-marcin

## 2.0.8

- Fix Response.profile not being properly filled. Closes #91

## 2.0.7

- sometimes the server version is missing the patch number, and the router couldn't return the proper version. Thank you @barry-w-hill, for finding this bug and reporting it!
- remove the `basic_auth` when using the `&Bolt.Sips.info/0` function. Thanks @dominique-vassard, for suggestion. Closes: #89

## 2.0.6

- Fix 'unused alias' compilation warnings
- Fix Bolt.Sips.Response type: `stats` was a `list` instead of `list|map`
- Add typespec for Bolt.Sips.Types: Node, Relationship and UnboundRelationship

## 2.0.5

- fix #83. More details in commit: https://github.com/florinpatrascu/bolt_sips/commit/ebe17e62ab1d823e301b11d99d532663b0b25135 Thank you @kristofka!

## 2.0.4

- feature: support connection options in queries PR #82. Many thanks @tcrossland, for this contribution!
  This PR adds support for passing options through to DBConnection.execute/4
- fix some broken links, in the docs; closes #76
- update some dependencies, including the DBConnection package.
- squashing some compile warnings; to be continued /attn: @team ;)
- please use Elixir 1.9 or 1.10, for test and development - where possible.

## 2.0.3

- refactoring the internals for achieving a better performance, while improving the code readability and extensibility - many thanks to @kristofka and @dominique-vassard. You guys are awesome!
- fix: Consistent bad connection state after malformed query [...] issue #78

## === 2.0.2 ===

- The 2.0, stable release. Thank you all for your feedback and for contributing to making this driver better. w⦿‿⦿t!
- fix: Simple Query taking too much time to process #73

## 2.0.0-rc.2

- swapping the assets around, for better organizing the docs

## 2.0.0-rc.1

- more documentation
- fix the TravisCi build
- min versions
  erlang 21.2
  elixir 1.7

## === 2.0.0-rc ===

## What's New?

### `bolt+routing://` is now supported

Read more what this schema is, as defined by the [Neo4j team](https://neo4j.com/developer/kb/how-neo4j-browser-bolt-routing/)

### Role-based connections

Until this version, Bolt.Sips was used for connecting to a single Neo4j server, aka: the "direct" mode. Basically you configure the driver with a url to a Neo4j server and Bolt.Sips will use that to attach itself to it, using a single configuration, remaining attached to that server until it is restarted (or reconfigured). In direct mode, bolt_sips "knows" only one server.

Starting with this version you can have as many distinct connection configurations, each of them dedicated to different Neo4j servers, as/if needed. We call these connections: "role-based connections". For example, when you'll connect to a Neo4j cluster using the new protocol, i.e. by using a configuration like this:

    config :bolt_sips, Bolt,
      # default port considered to be: 7687
      url: "bolt+routing://localhost",
      basic_auth: [username: "neo4j", password: "test"],
      pool_size: 10

Bolt.Sips will automatically create three pools of size 10, with the following **reserved** names: `:read`, `:write` and `:route`. Now you can specify what type of connection you want to use, by its name (role). For example:

    wconn = Bolt.Sips.conn(:write)
    ... = Bolt.Sips.query!(wconn, "CREATE (a:Person {name:'Bob'})")

    rconn = Bolt.Sips.conn(:read)
    ... = Bolt.Sips.query!(rconn, "MATCH (a:Person {name: 'Bob'}) RETURN a.name AS name")

The roles above: `:read`, `:write` and `:route`, are reserved. Please do not name custom connections using the same names (atoms). And as you just realized, yes: now you can create as many Bolt.Sips **direct** "driver instances" as you want, or as many as your app/hardware supports.

Please see the documentation for much more details.

### Main breaking changes introduced in version 2.x

- the `hostname` config parameter is a string; used to be a charlist
- the `url` config parameter must start with a valid schema i.e. `bolt`, `bolt+routing` or `neo4j`.
  Examples:

      url: "bolt://localhost"
      url: "bolt+routing://neo4j:password@neo01.graph.example.com:123456?policy=europe"

- Bolt.Sips.Query, will return a Bolt.Sips.Response now; it used to be a simple data structure.

## === 1.5 ===

## 1.5.1

- add a test alias for running the tests compatible with the most recent Neo4j server while
  disabling the older/legacy ones
- cleanup some warning about unused aliases

## 1.5.0

- Bolt V3 support
- Decompose tests by bolt version
- Important note about transaction

## 1.4.0

- Encoding / Decoding types is now at the lowest possible level
- Decompose encoders / decoders by bolt version
- Expose only public API in docs

## 1.3.0

- 1.3.0 stable release. Many thanks to Dominique VASSARD, for his awesome contributions.

## 1.3.0-rc2

- Fix some typos
- add json encoding capability

## 1.2.2-rc2

- Bug fix: Nanoseconds formating was erroneous. Example: 54 nanoseconds was formated to "PT0.54S" instead of "PT0.000000054S"
- Bug fix: Large amount of nanoseconds (>= 1_000_000) wasn't treated and lead to Neo4j errors. Now large amount of nanoseconds are converted in seconds, with the remainder in nanoseconds.

## 1.2.1-rc2

- Bug fix: If a property contains a speciifc types (date, datetime, point, etc.), it wasn't decoded. see: https://github.com/florinpatrascu/bolt_sips/issues/55

## 1.2.0-rc2

- support for the spatial and temporal types.

## 1.1.0-rc2

- removed the `boltex` dependency and added all its "low-level" code to `internals`.

## 1.0.0-rc2

### Breaking changes introduced in version 1.x

- non-closure based transactions are not supported anymore. This is a change introduced in DBConnection 2.x. `Bolt.Sips` version tagged `v0.5.10` is the last version supporting open transactions.
- the support for ETLS was dropped. It was mostly used for development or hand-crafted deployments

This version is using the official [DBConnection 2.0.0-rc2](https://hex.pm/packages/db_connection/2.0.0-rc.0), from [hex.pm](https://hex.pm)

## 0.5.10

- update the links referencing the Bolt protocol documentation (types, etc)

## 0.5.9

- upgrade dependencies
- trading carefully around the new db_connection, as we're chasing the code from `master` currently, and there more changes in the pipe to come for the both projects; db_connection, and this one, respectively.

## 0.5.8

- dealing with negative integers see issue #42, for more details

## 0.5.7

- elixir 1.6 and code formatting, of course :)
- minor test updates
- update dependencies
- pending code for the newest `db_connection` (currently using db_connection from the master branch)

## 0.5.5

- using the [DBConnection](https://hexdocs.pm/db_connection/DBConnection.html), thanks to the work done by Dmitriy Nesteryuk.

## 0.4.11

- using Elixir 1.5
- not using the ConCache anymore. I initially intended to use its support throughout the driver, but it is not needed.
- README updated with a short snippet from a Phoenix web app demo, showing how to start Bolt.Sips, as a worker
- dependencies update
- minor code cleanup, to prep the code for receiving HA and Bolt routing capabilities

## v0.3.5

- better error messages; issue #33
- not retrying a connection when the server is not available/started
- incorrect number of retries, performed by the driver in case of errors; was one extra

## v0.3.4

- dependencies update, minor code cleanup, listening to Credo :) and finally using a Markdown linter

## v0.3.3

- Add link to travis build; #31 by vic

## v0.3.2

- Use the project's own configuration file when executing the `bolt.cypher` mix task. Fixes issue #20

## v0.3.1 Breaking changes

- rollback/refactor to optionally allow external configuration options to be defined at runtime. You must start the Bolt.Sips manually, when needed, i.e. `Bolt.Sips.start_link(url: "localhost")`, or by changing your app's mix config file, i.e.

```elixir
def application do
  [applications: [:logger, :bolt_sips],
   mod: {Bolt.Sips.Application, []}]
end
```

You can also specify custom configuration settings in you app's mix config file. These may overwrite your config file:

```elixir
def application do
  [extra_applications: [:logger], mod:
    {Bolt.Sips.Application, [url: 'localhost', pool_size: 15]}
  ]
end
```

- code cleanup

## v0.2.6 (2017-04-21)

- cleanup, and minor dependencies update

## v0.2.5 (2017-03-22)

- split multi-line Cypher statements containing semicolons only if the `;` character is at the end of the line, followed by \r\n on Windows and \n on Unix like system, otherwise it may break the Cypher statement when the semicolon appears somewhere else

## v0.2.4 (2017-02-26)

- add the fuzzyurl to the list of apps, for project using Elixir < 1.4 (thank you, @dnesteryuk!)

## v0.2.3 (2017-02-26)

- improved connection handling

## v0.2.2 (2017-02-24)

- PR #18; Bring up `:boltex` and `:retry` in `applications`, for Elixir < 1.4 (from: @wli0503, thank you!)
- PR #19; test for error message on invalid parameter types (from: @vic, thank you!).

## v0.2.1 (2017-02-20)

- stop retrying a request if the failure is an internal one (driver, or driver dependencies related).
- update the Boltex driver containing two important bug fixes: one where Boltex will fail when receiving too much data (florinpatrascu/bolt_sips/issues/16) and the other one, an improvement, make Boltex.Error.get_id/1 more resilient for new transports (details here: mschae/boltex/issues/14)
- changed the pool strategy to :fifo, and its timeout to :infinity, and let the (:gen_server) call timeout expire according to the user's :timeout configuration parameter
- added a test unit provided by @adri (thank you), for executing a Cypher query, with large set of parameters

## v0.2.0 Breaking changes

- Elixir 1.4 is now required.
- Using Boltex 0.2.0
- bugfix: invalid Cypher statements will now be properly handled when the request is retried automatically

## v0.1.11

- With a larger amount of parameters it seems like generating chunks isn't working correctly. This is a patch imported from Boltex, see: https://github.com/mschae/boltex/issues/13, for more info

## v0.1.10 (2017-02-11)

- accept Map and Struct for query parameters, transparently. Thank you [@wli0503], for the PR.

## v0.1.9 (2017-01-27)

Some of the users are encountering difficulties when trying to compile bolt_sips on Windows. This release is addressing their concern.

`Bolt.Sips` will use the optional System variable: `BOLT_WITH_ETLS`, for depending on the [ETLS](https://hex.pm/packages/etls) package. If that variable is not defined, then `Bolt.Sips` will use the standard Erlang [`:ssl` module](http://erlang.org/doc/man/ssl.html), for the SSL/TLS protocol; the default behavior, starting with this version.

Therefore, if you want the **much** faster ssl/tls support offered by ETLS, then use this: `export BOLT_WITH_ETLS=true` on Linux/OSX, for example. Then:

```elixir
 mix deps.get
 mix test
```

and so on.

(Don't forget to `mix deps.unlock --all`, if you plan to plan to further debugging/developing w/ or w/o the `BOLT_WITH_ETLS` support)

Many thanks to: [Ben Wilson](https://elixir-lang.slack.com/team/benwilson512), for advices.

## v0.1.8 (2017-01-07)

- using Elixir 1.4
- add more details to the README, about the components required to build ETLS, the TCP/TLS layer
- added newer Elixirs to the Travis CI configuration file
- minor code cleanups

## v0.1.7 (2017-01-02)

- Connection code refactored for capturing the errors when the remote server is not responding on the first request, or if the driver is misconfigured i.e. wrong port number, bad hostname ...
- updated the test configuration file with detailed info about the newly introduced option: `:retry_linear_backoff`, mostly as a reminder

## v0.1.6 (2017-01-01)

- we're already using configurable timeouts, when executing requests from the connection pool. But with Bolt, the initial handshake sequence (happening before sending any commands to the server) is represented by two important calls, executed in sequence: `handshake` and `init`, and they must both succeed, before sending any (Cypher) requests. You can see the details in the [Bolt protocol](http://boltprotocol.org/v1/#handshake) specs. This sequence is also sensitive to latencies, such as: network latencies, busy servers, etc., and because of that we're introducing a simple support for retrying the handshake (and the subsequent requests) with a linear backoff, and try the handshake sequence (or the request) a couple of times before giving up - all these as part of the exiting pool management, of course. This retry is configurable via a new configuration parameter, the: `:retry_linear_backoff`, respectively. For example:

```elixir
config :bolt_sips, Bolt,
  url: "bolt://Bilbo:Baggins@hobby-hobbits.dbs.graphenedb.com:24786",
  ssl: true,
  timeout: 15_000,
  retry_linear_backoff: [delay: 150, factor: 2, tries: 3]
```

In the example above the retry will linearly increase the delay from 150ms following a Fibonacci pattern, cap the delay at 15 seconds (the value defined by the `:timeout` parameter) and giving up after 3 attempts. The same retry mechanism (and configuration parameters) is also honored when we send requests to the neo4j server.

## v0.1.5 (2016-12-30)

- as requested by many users, this version is introducing the optional `url` configuration parameter. If present, it will be used for extracting the host name, the port and the authentication details. Please see the README, for a couple of examples. For brevity:

```elixir
config :bolt_sips, Bolt,
  url: 'bolt://demo:demo@hobby-wowsoeasy.dbs.graphenedb.com:24786',
  ssl: true
```

## v0.1.4 (Merry Christmas)

- add support for connecting to Neo4j servers on encrypted sockets. Currently only TLSv1.2 is supported, using the default [BoringSSL](https://boringssl.googlesource.com/boringssl/) cipher; via [:etls](https://github.com/kzemek/etls). To connect securely to a remote Neo4j server, such as the ones provided by graphenedb.com, modify your Bolt.Sips config file like this (example):

```elixir
config :bolt_sips, Bolt,
  hostname: 'bolt://hobby-blah.dbs.graphenedb.com',
  basic_auth: [username: "wow", password: "of_course_this_is_the_password"],
  port: 24786,
  pool_size: 5,
  ssl: true,
  max_overflow: 1
```

Observe the new flag: `ssl: true`

Please note this is work in progress

## v0.1.2 (2016-11-06)

- integrate the Boltex code from https://github.com/mschae/boltex, and let the Bolt.Sips wrapper to manage the connectivity, using a simple Poolboy implementation for connection pooling

## v0.1.1 (2016-09-09)

- a temporary solution for dealing with negative values while extracting a graph walk-through from a Path. Dealing with this in Boltex instead, but this fix should work for now.

## v0.1.0 (2016-08-31)

First release!


================================================
FILE: ISSUE_TEMPLATE.md
================================================
# Precheck

* For bugs, do a quick search and make sure the bug has not yet been reported.
* Finally, be nice and have fun!

## Environment

* Elixir version (elixir -v):
* Neo4j and version (Neo4j 3.5.3, etc.):
* Connection scheme (`bolt://`, `bolt+routing://` or `neo4j://`):
* Bolt.Sips version (mix deps):
* Operating system:

## Current behavior

Include code samples, errors and stacktraces if appropriate.

## Expected behavior


================================================
FILE: LICENSE
================================================
                              Apache License
                        Version 2.0, January 2004
                     http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
       Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
       stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
       that You distribute, all copyright, patent, trademark, and
       attribution notices from the Source form of the Work,
       excluding those notices that do not pertain to any part of
       the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
       distribution, then any Derivative Works that You distribute must
       include a readable copy of the attribution notices contained
       within such NOTICE file, excluding those notices that do not
       pertain to any part of the Derivative Works, in at least one
       of the following places: within a NOTICE text file distributed
       as part of the Derivative Works; within the Source form or
       documentation, if provided along with the Derivative Works; or,
       within a display generated by the Derivative Works, if and
       wherever such third-party notices normally appear. The contents
       of the NOTICE file are for informational purposes only and
       do not modify the License. You may add Your own attribution
       notices within Derivative Works that You distribute, alongside
       or as an addendum to the NOTICE text from the Work, provided
       that such additional attribution notices cannot be construed
       as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

   To apply the Apache License to your work, attach the following
   boilerplate notice, with the fields enclosed by brackets "{}"
   replaced with your own identifying information. (Don't include
   the brackets!)  The text should be enclosed in the appropriate
   comment syntax for the file format. We also recommend that a
   file or class name and description of purpose be included on the
   same "printed page" as the copyright notice for easier
   identification within third-party archives.

Copyright {yyyy} {name of copyright owner}

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: README.md
================================================
<img src="assets/logo_transparent.png" alt="logo" width="240"/>

# Neo4j driver for Elixir.

[![Build Status](https://travis-ci.org/florinpatrascu/bolt_sips.svg?branch=master)](https://travis-ci.org/florinpatrascu/bolt_sips)
[![Hex.pm](https://img.shields.io/hexpm/dt/bolt_sips.svg?maxAge=2592000)](https://hex.pm/packages/bolt_sips)
[![Hexdocs.pm](https://img.shields.io/badge/api-hexdocs-brightgreen.svg)](https://hexdocs.pm/bolt_sips)

`Bolt.Sips` is an Elixir driver for [Neo4j](https://neo4j.com/developer/graph-database/), providing many useful features:

- using the Bolt protocol, the Elixir implementation - the Neo4j's newest network protocol, designed for high-performance; latest Bolt versions, are supported.
- Can connect to a standalone Neo4j server (`:direct` mode) or to a Neo4j causal cluster, using the `bolt+routing` or the newer `neo4j` schemes; connecting in `:routing` mode.
- Provides the user with the ability to create and manage distinct ad-hoc `role-based` connections to one or more Neo4j servers/databases
- Supports transactions, simple and complex Cypher queries with or w/o parameters
- Multi-tenancy
- Supports Neo4j versions: 3.0.x/3.1.x/3.2.x/3.4.x/3.5.x/4.0.x/4.1.x/4.2.x

Notes:

- Regarding Neo4j 4, stream capabilities are not yet supported.
- If you're seeking a substitute driver, here's a compilation of repositories:
  - https://github.com/sagastume/boltx

## Table of Contents

- [Installation](#installation)
  - [Getting Started](docs/getting-started.md#starting-the-driver)
  - [Basic usage](docs/getting-started.md#usage)
  - [Configuration](docs/features/configuration.md)
    - [Direct mode](docs/features/configuration.md#direct-mode)
    - [Routing](docs/features/configuration.md#routing-mode)
    - [Role-based connections](docs/features/configuration.md#role-based-connections)
    - [Multi tenancy](docs/features/configuration.md#multi-tenancy)
- [Using Cypher](docs/features/using-cypher.md)
- [Temporal and spatial types](docs/features/using-temporal-and-spatial-types.md)
- [Transactions](docs/features/about-transactions.md)
- [Encoding](docs/features/about-encoding.md)
- [Routing, in detail](docs/features/routing.md)
- [Multi tenancy, in detail](docs/features/multi-tenancy.md)
- [Using Bolt.Sips with Phoenix](docs/features/using-with-phoenix.md)
- [More examples](docs/examples/readme.md)

### Installation

[Available in Hex](https://hex.pm/packages/bolt_sips), the package can be added to your list of dependencies, in the: `mix.exs`:

```elixir
def deps do
  [{:bolt_sips, "~> 2.0"}]
end
```

### Basic usage

Provided you have access to a running Neo4j server, and a project where you just added the `:bolt_sips` dependency, run an `iex` session inside the project's folder, and once inside the shell, follow this simple step-by-step example.

Start an iex session:

```elixir
Erlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help)

iex> {:ok, _neo} = Bolt.Sips.start_link(url: "bolt://neo4j:test@localhost")
{:ok, #PID<0.237.0>}

iex> conn = Bolt.Sips.conn()
#PID<0.242.0>

iex> Bolt.Sips.query!(conn, "return 1 as n") |>
...> Bolt.Sips.Response.first()
%{"n" => 1}
```

Please see the docs for more examples and details about this driver.

### Testing

You'll need a running Neo4j server, for running the tests. Please verify that you do not store critical data on this server, as its contents will be wiped clean when the tests are running.

If you have docker available on your system, you can start an instance before running the test suite:

```shell
docker run --rm -p 7687:7687 -e 'NEO4J_AUTH=neo4j/test' neo4j:3.0.6
```

Neo4j versions used for test: 3.0, 3.1, 3.4, 3.5

```shell
mix test
```

For the stubs using [boltkit](https://github.com/neo4j-drivers/boltkit/), you will have to install Python 3.7 and run: `pip install boltkit`. After this you can run any tests tagged with `:boltkit`. Example:

```shell
mix test test/boltkit_test.exs --include boltkit
```

or:

```shell
mix test --only boltkit
```

### Special thanks

- Michael Schaefermeyer (@mschae), for the initial version of the Bolt protocol in Elixir: [mschae/boltex](https://github.com/mschae/boltex)

### Contributors

As reported by Github: [contributions to master, excluding merge commits](https://github.com/florinpatrascu/bolt_sips/graphs/contributors)

### Contributing

- [Fork it](https://github.com/florinpatrascu/bolt_sips/fork)
- Create your feature branch (`git checkout -b my-new-feature`)
- Test (`mix test`)
- Commit your changes (`git commit -am 'Add some feature'`)
- Push to the branch (`git push origin my-new-feature`)
- Create new Pull Request

### License

```txt
Copyright 2016-2020 the original author or authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```


================================================
FILE: benchees/conn_to_local_bench.exs
================================================
# benchmark the time it takes to open connections to a local neo4j server.
# Misc...
#
# What I want:
#
# - I want to measure the time it takes to open a connection and run
#   a simple query
# - I want to demonstrate how saving and reusing a connection (where
#   applicable) takes less time compared with a similar code where
#   I'm creating a new connection for every query
#
# Sample from a quick run:
#
#    $ mix run benchees/conn_to_local_bench.exs
#
#    Operating System: macOS
#    CPU Information: Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
#    Number of Available Cores: 8
#    Available memory: 17.179869184 GB
#    Elixir 1.5.0
#    Erlang 20.0
#    Benchmark suite executing with the following configuration:
#    warmup: 2.00 s
#    time: 1.00 s
#    parallel: 1
#    inputs: none specified
#    Estimated total run time: 6.00 s
#
#    Benchmarking  new conn...
#    Benchmarking same conn...
#
#    Name                ips        average  deviation         median
#    same conn        1.46 K        0.68 ms    ±20.73%        0.64 ms
#     new conn        0.47 K        2.15 ms    ±67.17%        1.98 ms
#
#    Comparison:
#    same conn        1.46 K
#     new conn        0.47 K - 3.14x slower

{:ok, _pid} = Bolt.Sips.start_link(url: "localhost")

simple_cypher = """
  MATCH (p:Person)-[r:WROTE]->(b:Book {title: 'The Name of the Wind'})
  RETURN p
"""

query = fn (conn, cypher) ->
  Bolt.Sips.Query.query(conn, cypher)
end

conn = Bolt.Sips.conn()

Benchee.run(
  %{
    "same conn" => fn -> query.(conn, simple_cypher) end,
    " new conn" => fn -> query.(Bolt.Sips.conn(), simple_cypher) end
  }, time: 1)


================================================
FILE: config/config.exs
================================================
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
import Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.

# You can configure for your application as:
#
#     config :bolt_sips, key: :value
#
# And access this configuration in your application as:
#
#     Application.get_env(:bolt_sips, :key)
#
# Or configure a 3rd-party app:
#
#     config :logger, level: :info
#

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
import_config "#{Mix.env()}.exs"


================================================
FILE: config/dev.exs
================================================
import Config

config :mix_test_watch,
  clear: true

level =
  if System.get_env("DEBUG") do
    :debug
  else
    :info
  end

config :bolt_sips,
  log: false,
  log_hex: false

config :logger, :console,
  level: level,
  format: "$date $time [$level] $metadata$message\n"

config :tzdata, :autoupdate, :disabled
config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase

config :eye_drops,
  tasks: [
    %{
      id: :docs,
      name: "docs",
      run_on_start: true,
      cmd: "mix docs",
      paths: ["lib/*", "README.md", "examples/*", "mix.exs"]
    }
  ]


================================================
FILE: config/test.exs
================================================
import Config

config :bolt_sips, Bolt,
  # default port considered to be: 7687
  url: "bolt://localhost",
  basic_auth: [username: "neo4j", password: "test"],
  pool_size: 10,
  max_overflow: 2,
  queue_interval: 500,
  queue_target: 1500,
  prefix: :default


level =
  if System.get_env("DEBUG") do
    :debug
  else
    :info
  end

config :bolt_sips,
  log: true,
  log_hex: false

config :logger, :console,
  level: level,
  format: "$date $time [$level] $metadata$message\n"

config :mix_test_watch,
  clear: true

config :tzdata, :autoupdate, :disabled
config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
config :porcelain, driver: Porcelain.Driver.Basic


================================================
FILE: docker-compose.yml
================================================
version: "3.0"

networks:
  lan:

services:
  core1:
    container_name: core1
    image: neo4j:3.5.3-enterprise
    networks:
      - lan
    ports:
      - 7474:7474
      - 6477:6477
      - 7687:7687
    environment:
      - NEO4J_AUTH=neo4j/test
      - NEO4J_dbms_mode=CORE
      - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
      - NEO4J_causalClustering_expectedCoreClusterSize=3
      - NEO4J_causalClustering_initialDiscoveryMembers=core1:5000,core2:5000,core3:5000
      - NEO4J_dbms_connector_http_listen__address=:7474
      - NEO4J_dbms_connector_https_listen__address=:6477
      - NEO4J_dbms_connector_bolt_listen__address=:7687
      - NEO4J_dbms_memory_heap_initial__size=300m
      - NEO4J_dbms_memory_heap_max__size=300m
      - NEO4J_dbms_logs_query_enabled=true
      - NEO4J_dbms_logs_query_page__logging__enabled=false
      - NEO4J_dbms_logs_query_parameter__logging__enabled=true
      - NEO4J_dbms_logs_query_threshold=0

  core2:
    container_name: core2
    image: neo4j:3.5.3-enterprise
    networks:
      - lan
    ports:
      - 7475:7475
      - 6478:6478
      - 7688:7688
    environment:
      - NEO4J_AUTH=neo4j/test
      - NEO4J_dbms_mode=CORE
      - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
      - NEO4J_causalClustering_expectedCoreClusterSize=3
      - NEO4J_causalClustering_initialDiscoveryMembers=core1:5000,core2:5000,core3:5000
      - NEO4J_dbms_connector_http_listen__address=:7475
      - NEO4J_dbms_connector_https_listen__address=:6478
      - NEO4J_dbms_connector_bolt_listen__address=:7688
      - NEO4J_dbms_memory_heap_initial__size=300m
      - NEO4J_dbms_memory_heap_max__size=300m
      - NEO4J_dbms_logs_query_enabled=true
      - NEO4J_dbms_logs_query_page__logging__enabled=false
      - NEO4J_dbms_logs_query_parameter__logging__enabled=true
      - NEO4J_dbms_logs_query_threshold=0

  core3:
    container_name: core3
    image: neo4j:3.5.3-enterprise
    networks:
      - lan
    ports:
      - 7476:7476
      - 6479:6479
      - 7689:7689
    environment:
      - NEO4J_AUTH=neo4j/test
      - NEO4J_dbms_mode=CORE
      - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
      - NEO4J_causalClustering_expectedCoreClusterSize=3
      - NEO4J_causalClustering_initialDiscoveryMembers=core1:5000,core2:5000,core3:5000
      - NEO4J_dbms_connector_http_listen__address=:7476
      - NEO4J_dbms_connector_https_listen__address=:6479
      - NEO4J_dbms_connector_bolt_listen__address=:7689
      - NEO4J_dbms_memory_heap_initial__size=300m
      - NEO4J_dbms_memory_heap_max__size=300m
      - NEO4J_dbms_logs_query_enabled=true
      - NEO4J_dbms_logs_query_page__logging__enabled=false
      - NEO4J_dbms_logs_query_parameter__logging__enabled=true
      - NEO4J_dbms_logs_query_threshold=0


================================================
FILE: docs/examples/readme.md
================================================


================================================
FILE: docs/features/about-encoding.md
================================================
# About encoding

Bolt.Sips provides support for encoding your query result in different formats.
For now, only JSON is supported.

There is two way of encoding data to json:

- By using the helpers provided by the module `Bolt.Sips.ResponseEncoder`
- Using your usual JSON encoding library. `Bolt.Sips` have implementation for: Jason and Poison. With this the query results can be automatically encoded by one of the libraries available: Jason or Poison. No further work is required when using a framework like: Phoenix, for example.

A few examples around the encoding suport:

```elixir
iex> query_result = [
   %{
     "t" => %Bolt.Sips.Types.Node{
       id: 26,
       labels: ["Test"],
       properties: %{
         "created_at" => "2019-08-03T12:34:56+01:00",
         "name" => "A test node",
         "uid" => 12345
       }
     }
   }
 ]

# Using Bolt.Sips.ResponseEncoder
 iex> Bolt.Sips.ResponseEncoder.encode(query_result, :json)
{:ok,
 "[{\"t\":{\"id\":26,\"labels\":[\"Test\"],\"properties\":{\"created_at\":\"2019-08-03T12:34:56+01:00\",\"name\":\"A test node\",\"uid\":12345}}}]"}
iex(11)> Bolt.Sips.ResponseEncoder.encode!(query_result, :json)
"[{\"t\":{\"id\":26,\"labels\":[\"Test\"],\"properties\":{\"created_at\":\"2019-08-03T12:34:56+01:00\",\"name\":\"A test node\",\"uid\":12345}}}]"

# Using Jason
iex(14)> Jason.encode!(query_result)
"[{\"t\":{\"id\":26,\"labels\":[\"Test\"],\"properties\":{\"created_at\":\"2019-08-03T12:34:56+01:00\",\"name\":\"A test node\",\"uid\":12345}}}]"

# Using Poison
iex(13)> Poison.encode!(query_result)
"[{\"t\":{\"properties\":{\"uid\":12345,\"name\":\"A test node\",\"created_at\":\"2019-08-03T12:34:56+01:00\"},\"labels\":[\"Test\"],\"id\":26}}]"
```

Both solutions rely on protocols, then they can be easily overridden if needed.
More info in the modules `Bolt.Sips.ResponseEncoder.Json`, `Bolt.Sips.ResponseEncoder.Json.Jason`, `Bolt.Sips.ResponseEncoder.Json.Poison`


================================================
FILE: docs/features/about-transactions.md
================================================
# About transactions

Transaction management in Neo4j 3.5+ differs from what it was in prior versions.
The cypher keyword `BEGIN`, `COMMIT` and `ROLLBACK` are no longer available.

In order to have a query that runs fine in all versions, you should use the following pattern:

```elixir
# Commit is performed automatically if everythings went fine
conn = Bolt.Sips.conn()
Bolt.Sips.transaction(conn, fn conn ->
  result = Bolt.Sips.query!(conn, "CREATE (m:Movie {title: "Matrix"}) RETURN m")
end)

# Rollback is performed automatically in case of error
Bolt.Sips.transaction(conn, fn conn ->
  result =Bolt.Sips.query!(conn, "Invalid query")
end)

# Rollback can stil be forced
Bolt.Sips.transaction(conn, fn conn ->
  result = Bolt.Sips.query!(conn, "CREATE (m:Movie {title: "Matrix"}) RETURN m")
  Bolt.Sips.rollback(conn, :dont_save)
end)
```


================================================
FILE: docs/features/configuration.md
================================================
# Configuration

Bolt.Sips can be configured using the well known Mix config files, or by using simple keyword lists.

This is the most basic configuration:

```elixir
config :bolt_sips, Bolt,
  url: "bolt://localhost:7687"
```

It tells Bolt.Sips your Neo4j server is available locally, and it listens on port 7687, expecting bolt commands.

These are the values you can configure, and their default values:

- `:url`- a full url to pointing to a running Neo4j server. Please remember you must specify the scheme used to connect to the server. Valid schemes:`bolt`,`bolt+routing`and`neo4j` - the last two being used for connecting to a Neo4j causal cluster.
- `:pool_size` - the size of the connection pool. Default: 15
- `:timeout` - a connection timeout value defined in milliseconds. Default: 15_000
- `:ssl`-`true`, if the connection must be encrypted. If the user wants to specify custom ssl options, just pass a keyword list with the options. More info [here](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/ssl) Default:`false`
- `:prefix`- used for differentiating between multiple connections available in the same app. Default:`:default`

## Examples of configurations

Connecting to remote (hosted) Neo4j servers, such as the ones available (also for free) at [Neo4j/Sandbox](https://neo4j.com/sandbox-v2/):

```elixir
config :bolt_sips, Bolt,
  url: "bolt://<ip_address>:<bolt_port>",
  basic_auth: [username: "neo4j", password: "#######"]
  ssl: true # or for example `[verify: :verify_none]` if custom ssl options
```

## Direct mode

Until this version, `Bolt.Sips` was used for connecting to a single Neo4j server from the moment the hosting app started, until the hosting app was terminated/restarted. This is known as the: `direct` mode. In `direct` mode, the `Bolt.Sips` driver has one configuration describing the connection to a single Neo4j server.

Since this connection mode is well known to our users, we'll not spend time on talking about it. It is sufficient to say that in direct mode, you have one configurable pool of connections, and the settings governing them i.e. timeout,  size, etc., are all about this single connection.

Because starting with version 2.0 `Bolt.Sips` is supporting a new type of connectivity: `routing`, for connecting to multiple servers or to a Neo4j causal cluster, you must specify the `scheme` in the `url` parameter, of your configuration. Example, for configuring a connection in `direct` mode:

    url: "bolt://localhost:7687"

We'll spend more ink on talking about the `routing` mode, next.

## Routing mode

With the 2.0 version, `Bolt.Sips` is implementing the ability to connect your app to a Neo4j causal cluster. You can read more about this, here: [Neo4j Causal Clustering](https://neo4j.com/docs/operations-manual/current/clustering/introduction/)

The features of using a causal cluster, in Neo4j's own words:

> Neo4j’s Causal Clustering provides three main features:
>
> - Safety: Core Servers provide a fault tolerant platform for transaction processing which will remain available while a simple majority of those Core Servers are functioning.
> - Scale: Read Replicas provide a massively scalable platform for graph queries that enables very large graph workloads to be executed in a widely distributed topology.
> - Causal consistency: when invoked, a client application is guaranteed to read at least its own writes.

To configure `Bolt.Sips` for connecting to a Neo4j Causal Cluster, you only need the specify the appropriate scheme, in the `url` configuration parameter:

    url: "bolt+routing://localhost:7687"

or:

    url: "neo4j://localhost:7687"

Prefer the latter, since `bolt+routing` appears to be soon deprecated, by Neo4j. We'll use `neo4j://` throughout the docs for referring to the `routing` mode, for brevity. Read more about `routing`, [here](routing.md).

## Role based connections

When we implemented the routing mode, we realized we could extend this ability to letting you define any number of connections, identified by a role name of your choice. For example, say your default configuration for `Bolt.Sips` looks like this:

```elixir
config :bolt_sips, Bolt,
  url: "bolt://localhost:7687",
  basic_auth: [username: "neo4j", password: "test"],
  pool_size: 10,
  max_overflow: 2,
```

`Bolt.Sips` will load it by default, when your application starts. And with a configuration like that, the default mode, you will continue to obtain connections using the default `Bolt.Sips.conn()` function.

However, if you require to have different connections, say: to a different Neo4j server that has some specific role, you could add a new configuration, for example:

```elixir
config :bolt_sips, :hidden_gems,
  url: "bolt://localhost:1234",
  pool_size: 50,
  role: :hidden_gems
```

You'd have to load this config separately, after the starting the `Bolt.Sips`driver. Like this:

```elixir
iex> Bolt.Sips.start_link(Application.get_env(:bolt_sips, :hidden_gems))
{:ok, #PID<0.266.0>}
```

and the you can use connections from this new configuration, as easy as this:

```elixir
iex> conn = Bolt.Sips.conn(:hidden_gems)
#PID<0.324.0>
```

while for obtaing the connections from your default configuration, is business as usual:

```elixir
iex> conn = Bolt.Sips.conn()
#PID<0.309.0>
```

The new connection pool is supervised by the main `Bolt.Sips.ConnectionSupervisor`, you don't have to do anythings special for that.

![](assets/role_based_connections.png?raw=true)

In the final release, we'll add a friendlier api for adding role-based connections. More details about role-based-connections, [here](role-based-connections.md)

## Multi tenancy

Another important feature of the 2.0 version, is: **multi-tenancy**.

Starting with this version, your app can connect to any number of Neo4j servers, in `direct` mode or `routing`.

We introduced a new configurable parameter, named: `prefix`.

At this time, the only way to configure the driver for multi-tenancy, is programmatically, not via the configuration file. Example:

```elixir
my_secret_cluster_config [
    url: "neo4j://localhost:9001",
    basic_auth: [username: "neo4j", password: "test"],
    pool_size: 10,
    max_overflow: 2,
    queue_interval: 500,
    queue_target: 1500,
    prefix: :secret_cluster
  ]

{:ok, _pid} = Bolt.Sips.start_link(@routing_connection_config)
conn = Bolt.Sips.conn(:write, prefix: :secret_cluster)
```

And you can start as many connections as needed, for as long as the `:prefix` has different names. These connections can be used for connecting to the same or different Neo4j servers.

More details about multi-tenancy, [here](multi-tenancy.md)


================================================
FILE: docs/features/multi-tenancy.md
================================================
# Multi tenancy

Very similar to the role-based connections, with multi-tenancy you will be able to connect to servers where the type of the server (role) is defined by the server itself, such as the Neo4j causal cluster. This setting is sill in its infancy, it works, but you'll have to be careful when using it.

For differentiating about Neo4j tenants, we introduced a new configurations parameter, named: `:prefix`. Example:

```elixir
monster_cluster_conf = [
  url: "neo4j://localhost",
  basic_auth: [username: "neo4j", password: "password"],
  pool_size: 50,
  prefix: :monster_cluster

baby_monster_cluster_conf = [
  url: "neo4j://raspberry_π",
  basic_auth: [username: "πs", password: "4VR"],
  pool_size: 50,
  prefix: :baby_monster_cluster
```

In the example above we defined two different connections, each of them pointing to different Neo4j clusters. As you know now, every cluster will have role-specific connections as defined by the routers, in those clusters. The connection roles will be: `:write`, `:read` and `:route`. To specify what connection you want and on what server, you will use the `:prefix` optional parameter of the new `Bolt.Sips.conn/2` method. Example:

```elixir
Bolt.Sips.conn(:read, prefix: :monster_cluster)
  |> Bolt.Sips.query!("MATCH (n) RETURN n.name AS name")
```

or:

```elixir
Bolt.Sips.conn(:read, prefix: :baby_monster_cluster)
  |> Bolt.Sips.query!("MATCH (n) RETURN n.name AS name")
```

(wip)


================================================
FILE: docs/features/role-based-connections.md
================================================
# Role-based connections

Starting with the 2.0 version, you can have distinct configurations that you can use in your app, concurrently. These configurations can connect to connect in `:direct` mode to different Neo4j servers, or the same but with different credentials, pool sizes, etc.

> This is not recommended for connecting to a causal cluster; the `:routing` mode, respectively.

To differentiate between multiple `:direct` configurations, you'll use a new parameter: `:role`. Let's see a some code examples, for brevity.

```elixir
frontend_config = [
    url: "bolt://localhost",
    basic_auth: [username: "neo4j", password: "test"],
    pool_size: 10,
    max_overflow: 2,
    role: :frontend
  ]

backend_config = [
    url: "bolt://not_my_localhost:12345",
    basic_auth: [username: "xxxxx", password: "yyyyy"],
    pool_size: 10,
    max_overflow: 2,
    role: :backend
  ]


{:ok, _pid} = Bolt.Sips.start_link(frontend_config)
{:ok, _pid} = Bolt.Sips.start_link(backend_config)

:frontend = Bolt.Sips.conn(:frontend)
:backend = Bolt.Sips.conn(:backend)

%Response{results: [%{"n" => 1}]} = Bolt.Sips.query!(:frontend, "RETURN 1 as n")
%Response{results: [%{"n" => 1}]} = Bolt.Sips.query!(:backend, "RETURN 1 as n")

```

The last two Cypher queries above will be executed on two different servers. And yes you can run them concurrently since their respective pools will not compete for the same resources.

If you desire to terminate a role-based connection, you can easily do so. Just like this: `:ok = Bolt.Sips.terminate_connections(:backend)`.


================================================
FILE: docs/features/routing.md
================================================
# Routing

When connecting to a Neo4j cluster, `Bolt.Sips` will create 3 distinct connection pools, each of them dedicated to one of the following connection types (**connection roles**):

- `:route` - used for getting information from the Neo4j router, such as: routing details about which server is handling what type of role: read/write, and more.
- `:read` - used for read-only connections
- `:write` - used for write-only connections.

Having the `Bolt.Sips` configured in `routing` mode, will enforce your code to clarify what type of connections you want, type you **must** specify when requesting a `Bolt.Sips` connection. Example:

```elixir
rconn = Bolt.Sips.conn(:read)
wconn = Bolt.Sips.conn(:write)
router_conn = Bolt.Sips.conn(:route)
```

Without being explicit about the connection type, you will receive errors, in case you'll attempt to execute a query that will say: create new nodes, on a server having the role: `read` or `route`. This is the only rule you must observe, when using the `Bolt.Sips` driver with a causal cluster.

## Routing walk-through

Let's walk-through a simple experiment with using `Bolt.Sips` in routing mode and a Neo4j cluster.

If you don't have a local server, or a remote Neo4j cluster available for your tests, you can easily setup your own local playground. All you need is Docker.

We'll use a [docker-compose.yml](../../docker-compose.yml) file that you can find in the `Bolt.Sips` main source repo.

If you have:

- [Docker](<https://en.wikipedia.org/wiki/Docker_(software)>) installed, and running. You can get Docker from here: https://docs.docker.com/installation/
- and a simple Elixir project having the `:bolt_sips` driver installer, as a dependency

### Start the Neo4j cluster

In a folder where you have the `docker-compose.yml` file, start a new shell session and run the following command:

    docker-compose up

If this is the first time you run this command, or use Neo4j as a Docker image, then based on the quality of your Internet connection, you'll wait a few seconds while Docker downloads a Neo4j Enterprise image. You'll see something like this:

```sh
Creating network "neo4j_lan" with the default driver
Pulling core1 (neo4j:3.5.3-enterprise)...
3.5.3-enterprise: Pulling from library/neo4j
e7c96db7181b: Pull complete
f910a506b6cb: Pull complete
b6abafe80f63: Pull complete
b95a7fd32595: Pull complete
6c09128ad074: Pull complete
648805e5f471: Pull complete
e2790f69a70d: Pull complete
Creating core2 ... done
Creating core3 ... done
Creating core1 ... done
Attaching to core3, core1, core2
core3    | Changed password for user 'neo4j'.
core1    | Changed password for user 'neo4j'.
core2    | Changed password for user 'neo4j'.
core3    | Active database: graph.db
core3    | Directories in use:
core3    |   home:         /var/lib/neo4j
core3    |   config:       /var/lib/neo4j/conf
...
```

and towards the end of the starting sequence, this:

```sh
core2    | 2019-06-17 12:37:59.078+0000 INFO  Remote interface available at http://localhost:7475/
core3    | 2019-06-17 12:37:59.165+0000 INFO  Remote interface available at http://localhost:7476/
```

Check to see if you can connect to your local Neo4j cluster, as simple as pointing your Internet browser to this url: `http://localhost:7474`, and if everything was executed successfully, you'll be seeing the familiar Neo4j web interface.

Now let's play with the `Bolt.Sips`driver and our local Neo4j cluster.

Change your elixir test project configuration and modify the `config/config.exs` file like this (excerpt):

```elixir
use Mix.Config

config :bolt_sips, Bolt,
  # bolt+routing will be deprecated?!
  # url: "bolt+routing://localhost:7687",
  url: "neo4j://localhost:7687",
  basic_auth: [username: "neo4j", password: "test"],
  pool_size: 10
```

then start a IEx shell session, from the projects'r main folder: `iex -S mix`. While inside the IEx session, let's see if our configuration is sound?

```elixir
iex> Bolt.Sips.info()
%{
  default: %{
    connections: %{
      read: %{"localhost:7688" => 0, "localhost:7689" => 0},
      route: %{
        "localhost:7687" => 0,
        "localhost:7688" => 0,
        "localhost:7689" => 0
      },
      write: %{"localhost:7687" => 0},
      routing_query: %{...},
      ttl: 300,
      updated_at: 1560775628
    },
    user_options: [
      url: "neo4j://localhost:7687",
      pool_size: 10,
      ....
    ]
  }
}
```

if you see the response above, it means your settings are ready. Without going into much details about the data structure above, the routing details are these:

```elixir
  read: %{"localhost:7688" => 0, "localhost:7689" => 0},
  write: %{"localhost:7687" => 0},
  route: %{
    "localhost:7687" => 0,
    "localhost:7688" => 0,
    "localhost:7689" => 0
  ttl: 300,
  updated_at: ...
```

According to the routing information returned by our cluster, we have:

- two nodes accepting `:read` commands: `localhost:7688` and`localhost:7689`
- three nodes capabale of responding with routing specific details: `localhost:7687` `localhost:7688` and `localhost:7689`
- one node accepting `:write` commands; the `localhost:7687`, respectively.

But don't worry about the gory details, we got you covered :)

Let's run some Cypher queries.

```elixir
iex> alias Bolt.Sips.Response
iex> alias Bolt.Sips, as: Neo

# obtaining a read(only) connection:
iex> rconn = Neo.conn(:read)
#PID<0.324.0>

# checking if there are any Person nodes "named": Bob?
iex> %Response{results: r} = Neo.query!(rconn, "MATCH (p:Person{name: 'Bob'}) RETURN p")
%Bolt.Sips.Response{
  bookmark: "neo4j:bookmark:v1:tx2",
  fields: ["p"],
  notifications: [],
  plan: nil,
  profile: nil,
  records: [],
  results: [],
  stats: [],
  type: "r"
}

# r is [], meaning: our query found none. So let's create one.
# First we obtain a connection suitable for `write` operations:
iex> wconn = Neo.conn(:write)
#PID<0.384.0>

# and now we can use it for creating a new node:

iex> %Response{results: r} = Neo.query!(wconn, "CREATE (p:Person{name:'Bob'})")
%Bolt.Sips.Response{
  ...
  stats: %{"labels-added" => 1, "nodes-created" => 1, "properties-set" => 1},
  type: "w"
}

# our node was created and has one property set,  w⦿‿⦿t!
# but can we find it? Rerun the previous query using the `read` connection:

iex> Neo.query!(rconn, "MATCH (p:Person{name: 'Bob'}) RETURN p") |> Response.first()
%{
  "p" => %Bolt.Sips.Types.Node{
    id: 20,
    labels: ["Person"],
    properties: %{"name" => "Bob"}
  }
}

# and yessss, our new Person node is in the cluster!
# Do you need its json form, instead? Easy:
iex> Neo.query!(rconn, "MATCH (p:Person{name: 'Bob'}) RETURN p") |>
...> Response.first() |>
...> Bolt.Sips.ResponseEncoder.encode!(:json)
"{\"p\":{\"id\":20,\"labels\":[\"Person\"],\"properties\":{\"name\":\"Bob\"}}}"

```

But what happens if we try to create a new Person, using our `read` connection?

```elixir
iex> Neo.query!(rconn, "CREATE (p:Person{name:'Alice'})")
** (Bolt.Sips.Exception) ... No write operations are allowed directly on this database. Writes must pass through the leader. The role of this server is: FOLLOWER
```

Neo4j will promptly let us know we can't use that connection for write operations. This is the main difference that you must consider when coding.

Same command executed on the proper (write) connection, will be successful:

```elixir
iex> Neo.query!(wconn, "CREATE (p:Person{name:'Alice'})")
%Bolt.Sips.Response{
  ...
  stats: %{"labels-added" => 1, "nodes-created" => 1, "properties-set" => 1},
  type: "w"
}
```


================================================
FILE: docs/features/using-cypher.md
================================================
# Using Bolt.Sips to query the Neo4j server

Let's talk about the basics of querying a Neo4j server, using `Bolt.Sips`, and a few methods you could use for using the data returned by the server, using the `Bolt.Sips.Response`. You can learn so much more from the official docs, available at [Neo4j](https://neo4j.com/developer/graph-database/), you should start from there, if you want to get a deep understanding about the Neo4j graph database and its query language: Cypher.

## What you need?

- a [Neo4j](https://neo4j.com/download/) server running locally and available at this `url`: `bolt://neo4j:test@localhost`
- a mix project with `:bolt_sips` available


## Simple queries, using Cypher

With the above prerequisites, let's drop into the IEx shell and start experimenting.

```sh
cd my_neo4j
iex -S mix
Erlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help)

iex>
```

First we need to start the driver with a minimalist configuration (unless it is already started by your project?):

```elixir
iex> {:ok, _neo} = Bolt.Sips.start_link(url: "bolt://neo4j:test@localhost")
{:ok, #PID<0.243.0>}
iex>

```

Presuming your database is empty, you can still test your setup by running a simple Cypher query:

```elixir
iex> conn = Bolt.Sips.conn()
#PID<0.248.0>
iex> Bolt.Sips.query!(conn, "RETURN 1 as n") |>
...> Bolt.Sips.Response.first()
%{"n" => 1}
```

and we obtained our first response from the server: `%{"n" => 1}`, w⦿‿⦿t!! Now let's try some more complicated Cypher queries. We'll use examples that you may want to paste them in your `.exs/.ex` files rather than into the IEx shell, for readability.

While most of the Cypher querier fit on a simple row, and they look compact, you might encounter situations where you need to send multiple queries in a single trip, to the server. `Bolt.Sips` allows you do that.

Let's initialize our **test** database with some data.

```elixir
cypher = """
  CREATE (BoltSips:BoltSips {title:'Elixir sipping from Neo4j, using Bolt', released:2016, license:'MIT', bolt_sips: true})
  CREATE (TNOTW:Book {title:'The Name of the Wind', released:2007, genre:'fantasy', bolt_sips: true})
  CREATE (Patrick:Person {name:'Patrick Rothfuss', bolt_sips: true})
  CREATE (Kvothe:Person {name:'Kote', bolt_sips: true})
  CREATE (Denna:Person {name:'Denna', bolt_sips: true})
  CREATE (Chandrian:Deamon {name:'Chandrian', bolt_sips: true})

  CREATE
    (Kvothe)-[:ACTED_IN {roles:['sword fighter', 'magician', 'musician']}]->(TNOTW),
    (Denna)-[:ACTED_IN {roles:['many talents']}]->(TNOTW),
    (Chandrian)-[:ACTED_IN {roles:['killer']}]->(TNOTW),
    (Patrick)-[:WROTE]->(TNOTW)
"""

{:ok, response} =
  Bolt.Sips.conn()
  |> Bolt.Sips.query(cypher)
```

According to the response from the server, this is what we did:

```elixir
iex> response
%Bolt.Sips.Response{
  results: [],
  stats: %{
    "labels-added" => 6,
    "nodes-created" => 6,
    "properties-set" => 19,
    "relationships-created" => 4
  },
  type: "w"
}
```

we have 6 new Nodes, 6 new labels and 4 new relationships.

At any time, if you want to clean up the data we're creating, you can use this query:

`MATCH (n {bolt_sips: true}) OPTIONAL MATCH (n)-[r]-() DELETE n,r`

Observe we're adding a `bolt_sips` property to the Nodes we're adding, so that it's easier to refer them in our tests.

Let's see how many nodes of "type" (`label`, according to Cypher's official terminology) `Person` having the property `bolt_sips` true, we have in our database:

```elixir
iex> query = """
...>   MATCH (n:Person {bolt_sips: true})
...>   RETURN n.name AS Name
...>   ORDER BY Name DESC
...>   LIMIT 5
...>  """

iex> %Bolt.Sips.Response{} = response = Bolt.Sips.query!(conn, query)
%Bolt.Sips.Response{
  bookmark: "neo4j:bookmark:v1:tx21613",
  fields: ["Name"],
  notifications: [],
  plan: nil,
  profile: nil,
  records: [["Patrick Rothfuss"], ["Kote"], ["Denna"]],
  results: [
    %{"Name" => "Patrick Rothfuss"},
    %{"Name" => "Kote"},
    %{"Name" => "Denna"}
  ],
  stats: [],
  type: "r"
}
```

We have 3 of them, and we're only showing the `name` property! Above you see the full `Bolt.Sips.Response` returned by our driver based on the raw data returned by the Neo4j server. The `:results` key, contains the aggregated response you will use most of the time, and for that the `Bolt.Sips.Response` module has some useful helpers, for example:

```elixir
iex> response |>
...> Bolt.Sips.Response.first()
%{"Name" => "Patrick Rothfuss"}
```

and much more. Check the `Bolt.Sips.Response`'s own docs, for more.


================================================
FILE: docs/features/using-temporal-and-spatial-types.md
================================================
# Using temporal and spatial types

Temporal and spatial types are supported since Neo4J 3.4.
You can used the elixir structs: Time, NaiveDateTime, DateTime,
as well as the Bolt Sips structs: DateTimeWithTZOffset, TimeWithTZOffset, Duration, Point.

```elixir
$ MIX_ENV=test iex -S mix
Erlang/OTP 21 [erts-10.0.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex> alias Bolt.Sips.Types.{Duration, DateTimeWithTZOffset, Point, TimeWithTZOffset}
[Bolt.Sips.Types.Duration, Bolt.Sips.Types.DateTimeWithTZOffset,
 Bolt.Sips.Types.Point, Bolt.Sips.Types.TimeWithTZOffset]

iex> alias Bolt.Sips.TypesHelper
Bolt.Sips.TypesHelper

iex> {:ok, pid} = Bolt.Sips.start_link(url: "localhost", basic_auth: [username: "neo4j", password: "test"])
{:ok, #PID<0.236.0>}

iex> conn = Bolt.Sips.conn
:bolt_sips_pool

# Date without timezone with Date
iex(8)> Bolt.Sips.query!(conn, "RETURN date($d) AS d", %{d: ~D[2019-02-04]})
[%{"d" => ~D[2019-02-04]}]

# Time without timezone with Time
iex> Bolt.Sips.query!(conn, "RETURN localtime($t) AS t", %{t: ~T[13:26:08.543440]})
[%{"t" => ~T[13:26:08.543440]}]

# Datetime without timezone with Naive DateTime
iex> Bolt.Sips.query!(conn, "RETURN localdatetime($ldt) AS ldt", %{ldt: ~N[2016-05-24 13:26:08.543]})
[%{"ldt" => ~N[2016-05-24 13:26:08.543]}]

# Datetime with timezone ID with DateTime (through Calendar)
iex> date_time_with_tz_id = TypesHelper.datetime_with_micro(~N[2016-05-24 13:26:08.543], "Europe/Paris")
#DateTime<2016-05-24 13:26:08.543+02:00 CEST Europe/Paris>
iex> Bolt.Sips.query!(conn, "RETURN datetime($dt) AS dt", %{dt: date_time_with_tz_id})
[%{"dt" => #DateTime<2016-05-24 13:26:08.543+02:00 CEST Europe/Paris>}]

# Datetime with timezone offset (seconds) with DateTimeWithTZOffset
iex(17)> date_time_with_tz = DateTimeWithTZOffset.create(~N[2016-05-24 13:26:08.543], 7200)
%Bolt.Sips.Types.DateTimeWithTZOffset{
  naive_datetime: ~N[2016-05-24 13:26:08.543],
  timezone_offset: 7200
}
iex(18)> Bolt.Sips.query!(conn, "RETURN datetime($dt) AS dt", %{dt: date_time_with_tz})
[
  %{
    "dt" => %Bolt.Sips.Types.DateTimeWithTZOffset{
      naive_datetime: ~N[2016-05-24 13:26:08.543],
      timezone_offset: 7200
    }
  }
]


# Datetime with timezone offset (seconds) with TimeWithTZOffset
iex> time_with_tz = TimeWithTZOffset.create(~T[12:45:30.250000], 3600)
%Bolt.Sips.Types.TimeWithTZOffset{
  time: ~T[12:45:30.250000],
  timezone_offset: 3600
}
iex> Bolt.Sips.query!(conn, "RETURN time($t) AS t", %{t: time_with_tz})
[
  %{
    "t" => %Bolt.Sips.Types.TimeWithTZOffset{
      time: ~T[12:45:30.250000],
      timezone_offset: 3600
    }
  }
]

# Cartesian 2D point with Point
iex> point_cartesian_2D = Point.create(:cartesian, 50, 60.5)
%Bolt.Sips.Types.Point{
  crs: "cartesian",
  height: nil,
  latitude: nil,
  longitude: nil,
  srid: 7203,
  x: 50.0,
  y: 60.5,
  z: nil
}
iex> Bolt.Sips.query!(conn, "RETURN point($pt) AS pt", %{pt: point_cartesian_2D})
[
  %{
    "pt" => %Bolt.Sips.Types.Point{
      crs: "cartesian",
      height: nil,
      latitude: nil,
      longitude: nil,
      srid: 7203,
      x: 50.0,
      y: 60.5,
      z: nil
    }
  }
]

# Geographic 2D point with Point
iex> point_geo_2D = Point.create(:wgs_84, 50, 60.5)
%Bolt.Sips.Types.Point{
  crs: "wgs-84",
  height: nil,
  latitude: 60.5,
  longitude: 50.0,
  srid: 4326,
  x: 50.0,
  y: 60.5,
  z: nil
}
iex> Bolt.Sips.query!(conn, "RETURN point($pt) AS pt", %{pt: point_geo_2D})
[
  %{
    "pt" => %Bolt.Sips.Types.Point{
      crs: "wgs-84",
      height: nil,
      latitude: 60.5,
      longitude: 50.0,
      srid: 4326,
      x: 50.0,
      y: 60.5,
      z: nil
    }
  }
]

# Cartesian 3D point with Point
iex> point_cartesian_3D = Point.create(:cartesian, 50, 60.5, 12.34)
%Bolt.Sips.Types.Point{
  crs: "cartesian-3d",
  height: nil,
  latitude: nil,
  longitude: nil,
  srid: 9157,
  x: 50.0,
  y: 60.5,
  z: 12.34
}
iex> Bolt.Sips.query!(conn, "RETURN point($pt) AS pt", %{pt: point_cartesian_3D})
[
  %{
    "pt" => %Bolt.Sips.Types.Point{
      crs: "cartesian-3d",
      height: nil,
      latitude: nil,
      longitude: nil,
      srid: 9157,
      x: 50.0,
      y: 60.5,
      z: 12.34
    }
  }
]

# Geographic 2D point with Point
iex> point_geo_3D = Point.create(:wgs_84, 50, 60.5, 12.34)
%Bolt.Sips.Types.Point{
  crs: "wgs-84-3d",
  height: 12.34,
  latitude: 60.5,
  longitude: 50.0,
  srid: 4979,
  x: 50.0,
  y: 60.5,
  z: 12.34
}
iex> Bolt.Sips.query!(conn, "RETURN point($pt) AS pt", %{pt: point_geo_2D})
[
  %{
    "pt" => %Bolt.Sips.Types.Point{
      crs: "wgs-84",
      height: nil,
      latitude: 60.5,
      longitude: 50.0,
      srid: 4326,
      x: 50.0,
      y: 60.5,
      z: nil
    }
  }
]
```


================================================
FILE: docs/features/using-with-phoenix.md
================================================
# Using Bolt.Sips with Phoenix, or similar

Don't forget to start the `Bolt.Sips` driver in your supervision tree. Example:

```elixir
defmodule MoviesElixirPhoenix do
  use Application

  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    # Define workers and child supervisors to be supervised
    children = [
      # Start the endpoint when the application starts
      {Bolt.Sips, Application.get_env(:bolt_sips, Bolt)},
      %{
        id: MoviesElixirPhoenix.Endpoint,
        start: {MoviesElixirPhoenix.Endpoint, :start_link, []}
      }
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: MoviesElixirPhoenix.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    MoviesElixirPhoenix.Endpoint.config_change(changed, removed)
    :ok
  end
end
```

The code above was extracted from [the Neo4j Movies Demo](https://github.com/florinpatrascu/bolt_movies_elixir_phoenix), a Phoenix web application using this driver and the well known [Dataset - Movie Database](https://neo4j.com/developer/movie-database/).

Note: as explained below, you don't need to convert your query result before having it  encoded in JSON. BoltSips provides Jason and Poison implementation to tackle this problem automatically.


================================================
FILE: docs/getting-started.md
================================================
# Getting Started

Let's start by creating a simple Elixir project, as a playground for our tests.

```sh
mix new neo4j_demo --sup --app n4d --module N4D
cd neo4j_demo
```

Open the `mix.exs` and add the bolt_sips dependency.

```elixir
defmodule N4D.MixProject do
  use Mix.Project

  def project do
    [
      app: :n4d,
      version: "0.1.0",
      elixir: "~> 1.8",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger],
      mod: {N4D.Application, []}
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      {:bolt_sips, "~> 2.0.0-rc"},
      {:jason, "~> 1.1"}
    ]
  end
end
```

we added the [jason](https://hex.pm/packages/jason) library too, for converting the server responses to json. And then run:

```sh
mix do deps.get, compile
```

## Starting the driver

And our simple project is ready for us to start experimenting with it.

Let's first configure the connection to a running Neo4j server. We presume a standalone community edition server is started and available on the `localhost` interface, and having its Bolt port open at: `7687`. For simplicity edit the `config/config.exs`, and modify it to look like this:

```elixir
use Mix.Config

config :bolt_sips, Bolt,
  url: "bolt://localhost:7687",
  basic_auth: [username: "neo4j", password: "test"],
  pool_size: 10

```

With the project configured to connect to a Neo4j server, in direct mode, we can add `Bolt.Sips` to the app's main supervision tree, and let the OTP manage it.

```elixir
# lib/n4_d/application.ex

defmodule N4D.Application do
  @moduledoc false

  use Application

  def start(_type, _args) do
    children = [
      {Bolt.Sips, Application.get_env(:bolt_sips, Bolt)}
    ]

    opts = [strategy: :one_for_one, name: N4D.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
```

There are a couple of different other ways to start the driver but let's keep it simple for now.

The easiest way to start playing with the driver, in the current configuration, is to drop into the IEx shell and run simple Cypher commands through it.

```sh
cd neo4j_demo
iex -S mix
```

## Usage

A few examples:

```elixir
iex> alias Bolt.Sips, as: Neo
iex> alias Bolt.Sips.Response

# check the driver is up and running:

iex> Neo.info()
%{
  default: %{
    connections: %{direct: %{"localhost:7687" => 0}, routing_query: nil},
    user_options: [
      socket: Bolt.Sips.Socket,
      port: 7687,
      url: "bolt://localhost:7687",
      # ...
      basic_auth: [username: "neo4j", password: "test"],
      pool_size: 10
    ]
  }
}

# in direct mode, our current configuration, all the operations such as: read/write or
# delete, are sent to the Neo4j server using a common connection (pool).
# Let's obtain a connection:

iex> conn = Neo.conn()
#PID<0.308.0>

# a few examples:

iex> response = Neo.query!(conn, "CREATE (p:Person)-[:LIKES]->(t:Technology)")
%Response{
  bookmark: nil,
  fields: [],
  notifications: [],
  plan: nil,
  profile: nil,
  records: [],
  results: [],
  stats: %{
    "labels-added" => 2,
    "nodes-created" => 2,
    "relationships-created" => 1
  },
  type: "w"
}

# query with undirected relationship unless sure of direction
%Bolt.Sips.Response{results: results} = response =  Neo.query!(conn, "MATCH (p:Person)-[:LIKES]-(t:Technology) RETURN p")

# where `results` contain:
[%{"p" => %Bolt.Sips.Types.Node{id: 355, labels: ["Person"], properties: %{}}}]

# and we can also encode them to json, as simple as this:

iex> Jason.encode!(results)
"[{\"p\":{\"id\":355,\"labels\":[\"Person\"],\"properties\":{}}}]"

# of course you can do more:

iex> Bolt.Sips.query!(Bolt.Sips.conn(), "RETURN [10,11,21] AS arr", %{}, timeout: 19_000) |>
...> Enum.reduce(0, &(Enum.sum(&1["arr"]) + &2))
42

# see more examples and the tests, for getting familiar with what is possible.

# Enjoy!
```

Follow this link: [Cypher Basics](https://neo4j.com/developer/cypher-query-language/), for a gentle introduction to Cypher; Neo4j's query language. Throughout the code snippets we are often using examples copied from the original documentation published by Neo4j, so that you can feel comfortable with them.


================================================
FILE: lib/bolt_sips/application.ex
================================================
defmodule Bolt.Sips.Application do
  @moduledoc false

  use Application

  alias Bolt.Sips

  def start(_, start_args) do
    Sips.start_link(start_args)
  end

  def stop(_state) do
    :ok
  end
end


================================================
FILE: lib/bolt_sips/enumerable_response.ex
================================================
defimpl Enumerable, for: Bolt.Sips.Response do
  alias Bolt.Sips.Response

  def count(%Response{results: nil}), do: {:ok, 0}
  def count(%Response{results: []}), do: {:ok, 0}
  def count(%Response{results: results}), do: {:ok, length(results)}

  def member?(%Response{fields: fields}, field), do: {:ok, Enum.member?(fields, field)}
  def slice(_response), do: {:error, __MODULE__}

  def reduce(%Response{results: []}, acc, _fun), do: acc

  def reduce(%Response{results: results}, acc, fun) when is_list(results),
    do: reduce_list(results, acc, fun)

  defp reduce_list(_, {:halt, acc}, _fun), do: {:halted, acc}
  defp reduce_list(list, {:suspend, acc}, fun), do: {:suspended, acc, &reduce_list(list, &1, fun)}
  defp reduce_list([], {:cont, acc}, _fun), do: {:done, acc}
  defp reduce_list([h | t], {:cont, acc}, fun), do: reduce_list(t, fun.(h, acc), fun)

  @doc false
  def slice(%Response{results: []}, _start, _count), do: []
  def slice(_response, _start, 0), do: []
  def slice(%Response{results: [head | tail]}, 0, count), do: [head | slice(tail, 0, count - 1)]
  def slice(%Response{results: [_head | tail]}, start, count), do: slice(tail, start - 1, count)
end


================================================
FILE: lib/bolt_sips/error.ex
================================================
defmodule Bolt.Sips.Error do
  @moduledoc """
  represents an error message
  """
  alias __MODULE__
  @type t :: %__MODULE__{}

  defstruct [:code, :message]

  def new(%Bolt.Sips.Internals.Error{
        code: code,
        connection_id: cid,
        function: f,
        message: message,
        type: t
      }) do
    {:error,
     %Error{
       code: code,
       message:
         "Details: #{message}; connection_id: #{inspect(cid)}, function: #{inspect(f)}, type: #{
           inspect(t)
         }"
     }}
  end

  def new({:ignored, f} = _r), do: new({:error, f})

  def new({:failure, %{"code" => code, "message" => message}} = _r) do
    {:error, %Error{code: code, message: message}}
  end

  def new(r), do: r
end


================================================
FILE: lib/bolt_sips/exception.ex
================================================
defmodule Bolt.Sips.Exception do
  @moduledoc """
  This module defines a `Bolt.Sips.Exception` structure containing two fields:

  * `code` - the error code
  * `message` - the error details
  """
  @type t :: %Bolt.Sips.Exception{}

  defexception [:code, :message]
end


================================================
FILE: lib/bolt_sips/internals/bolt_protocol.ex
================================================
defmodule Bolt.Sips.Internals.BoltProtocol do
  @moduledoc false
  # A library that handles Bolt Protocol (v1 and v2).
  # Note that for now, only Neo4j implements Bolt v2.

  # It handles all the protocol specific steps (i.e.
  # handshake, init) as well as sending and receiving messages and wrapping
  # them in chunks.

  # It abstracts transportation, expecting the transport layer to define
  # `send/2` and `recv/3` analogous to `:gen_tcp`.

  # ## Logging configuration
  # Logging can be enable / disable via config files (e.g, `config/config.exs`).
  #   - `:log`: (bool) wether Bolt.Sips.Internals. should produce logs or not. Defaults to `false`
  #   - `:log_hex`: (bool) wether Bolt.Sips.Internals. should produce logs hexadecimal counterparts. While this may be interesting,
  #   note that all the hexadecimal data will be written and this can be very long, and thus can seriously impact performances. Defaults to `false`

  # For example, configuration to see the logs and their hexadecimal counterparts:
  # ```
  #   config :Bolt.Sips.Internals.,
  #     log: true,
  #     log_hex: true
  # ```
  #   # #### Examples of logging (without log_hex)

  #     iex> Bolt.Sips.Internals.test('localhost', 7687, "RETURN 1 as num", %{}, {"neo4j", "password"})
  #     C: HANDSHAKE ~ "<<0x60, 0x60, 0xB0, 0x17>> [2, 1, 0, 0]"
  #     S: HANDSHAKE ~ 2
  #     C: INIT ~ ["BoltSips/1.1.0.rc2", %{credentials: "password", principal: "neo4j", scheme: "basic"}]
  #     S: SUCCESS ~ %{"server" => "Neo4j/3.4.1"}
  #     C: RUN ~ ["RETURN 1 as num", %{}]
  #     S: SUCCESS ~ %{"fields" => ["num"], "result_available_after" => 1}
  #     C: PULL_ALL ~ []
  #     S: RECORD ~ [1]
  #     S: SUCCESS ~ %{"result_consumed_after" => 0, "type" => "r"}
  #     [
  #       success: %{"fields" => ["num"], "result_available_after" => 1},
  #       record: [1],
  #       success: %{"result_consumed_after" => 0, "type" => "r"}
  #     ]

  # #### Examples of logging (with log_hex)

  #     iex> Bolt.Sips.Internals.test('localhost', 7687, "RETURN 1 as num", %{}, {"neo4j", "password"})
  #     13:32:23.882 [debug] C: HANDSHAKE ~ "<<0x60, 0x60, 0xB0, 0x17>> [2, 1, 0, 0]"
  #     S: HANDSHAKE ~ <<0x0, 0x0, 0x0, 0x2>>
  #     S: HANDSHAKE ~ 2
  #     C: INIT ~ ["BoltSips/1.1.0.rc2", %{credentials: "password", principal: "neo4j", scheme: "basic"}]
  #     C: INIT ~ <<0x0, 0x42, 0xB2, 0x1, 0x8C, 0x42, 0x6F, 0x6C, 0x74, 0x65, 0x78, 0x2F, 0x30, 0x2E, 0x35, 0x2E, 0x30, 0xA3, 0x8B, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x61, 0x6C, 0x73, 0x88, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x89, 0x70, 0x72, 0x69, 0x6E, 0x63, 0x69, 0x70, 0x61, 0x6C, 0x85, 0x6E, 0x65, 0x6F, 0x34, 0x6A, 0x86, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x65, 0x85, 0x62, 0x61, 0x73, 0x69, 0x63, 0x0, 0x0>>
  #     S: SUCCESS ~ <<0xA1, 0x86, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x8B, 0x4E, 0x65, 0x6F, 0x34, 0x6A, 0x2F, 0x33, 0x2E, 0x34, 0x2E, 0x31>>
  #     S: SUCCESS ~ %{"server" => "Neo4j/3.4.1"}
  #     C: RUN ~ ["RETURN 1 as num", %{}]
  #     C: RUN ~ <<0x0, 0x13, 0xB2, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x31, 0x20, 0x61, 0x73, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0x0, 0x0>>
  #     S: SUCCESS ~ <<0xA2, 0xD0, 0x16, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x5F, 0x61, 0x76, 0x61, 0x69, 0x6C, 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x61, 0x66, 0x74, 0x65, 0x72, 0x1, 0x86, 0x66, 0x69, 0x65, 0x6C, 0x64, 0x73, 0x91, 0x83, 0x6E, 0x75, 0x6D>>
  #     S: SUCCESS ~ %{"fields" => ["num"], "result_available_after" => 1}
  #     C: PULL_ALL ~ []
  #     C: PULL_ALL ~ <<0x0, 0x2, 0xB0, 0x3F, 0x0, 0x0>>
  #     S: RECORD ~ <<0x91, 0x1>>
  #     S: RECORD ~ [1]
  #     S: SUCCESS ~ <<0xA2, 0xD0, 0x15, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x5F, 0x63, 0x6F, 0x6E, 0x73, 0x75, 0x6D, 0x65, 0x64, 0x5F, 0x61, 0x66, 0x74, 0x65, 0x72, 0x0, 0x84, 0x74, 0x79, 0x70, 0x65, 0x81, 0x72>>
  #     S: SUCCESS ~ %{"result_consumed_after" => 0, "type" => "r"}
  #     [
  #       success: %{"fields" => ["num"], "result_available_after" => 1},
  #       record: [1],
  #       success: %{"result_consumed_after" => 0, "type" => "r"}
  #     ]

  # ## Shared options

  # Functions that allow for options accept these default options:

  #   * `recv_timeout`: The timeout for receiving a response from the Neo4J s
  #     server (default: #{@recv_timeout})

  alias Bolt.Sips.Metadata
  alias Bolt.Sips.Internals.BoltProtocolV1
  alias Bolt.Sips.Internals.BoltProtocolV3

  defdelegate handshake(transport, port, options \\ []), to: BoltProtocolV1
  defdelegate init(transport, port, version, auth \\ {}, options \\ []), to: BoltProtocolV1
  defdelegate hello(transport, port, version, auth \\ {}, options \\ []), to: BoltProtocolV3
  defdelegate goodbye(transport, port, version), to: BoltProtocolV3

  defdelegate ack_failure(transport, port, bolt_version, options \\ []), to: BoltProtocolV1
  defdelegate reset(transport, port, bolt_version, options \\ []), to: BoltProtocolV1
  defdelegate discard_all(transport, port, bolt_version, options \\ []), to: BoltProtocolV1

  defdelegate begin(transport, port, bolt_version, metadata \\ %Metadata{}, options \\ []),
    to: BoltProtocolV3

  defdelegate commit(transport, port, bolt_version, options \\ []), to: BoltProtocolV3
  defdelegate rollback(transport, port, bolt_version, options \\ []), to: BoltProtocolV3

  defdelegate pull_all(transport, port, bolt_version, options \\ []), to: BoltProtocolV1

  @doc """
  run for all Bolt version, but call differs.
  For Bolt <= 2, use: run_statement(transport, port, bolt_version, statement, params, options)
  For Bolt >=3: run_statement(transport, port, bolt_version, statement, params, metadata, options)

  Note that Bolt V2 calls works with Bolt V3, but it is preferrable to update them.
  """
  @spec run(
          atom(),
          port(),
          integer(),
          String.t(),
          map(),
          nil | Keyword.t() | Bolt.Sips.Metadata.t(),
          nil | Keyword.t()
        ) ::
          {:ok, tuple()}
          | Bolt.Sips.Internals.Error.t()
  def run(
        transport,
        port,
        bolt_version,
        statement,
        params \\ %{},
        options_or_metadata \\ [],
        options \\ []
      )

  def run(transport, port, bolt_version, statement, params, options_or_metadata, _)
      when bolt_version <= 2 do
    BoltProtocolV1.run(
      transport,
      port,
      bolt_version,
      statement,
      params,
      options_or_metadata || []
    )
  end

  def run(transport, port, bolt_version, statement, params, metadata, options)
      when bolt_version >= 2 do
    metadata =
      case metadata do
        [] -> %{}
        metadata -> metadata
      end

    {metadata, options} = manage_metadata_and_options(metadata, options)

    BoltProtocolV3.run(transport, port, bolt_version, statement, params, metadata, options)
  end

  defp manage_metadata_and_options([], options) do
    {:ok, empty_metadata} = Metadata.new(%{})
    {empty_metadata, options}
  end

  defp manage_metadata_and_options([_ | _] = metadata, options) do
    {:ok, empty_metadata} = Metadata.new(%{})
    {empty_metadata, metadata ++ options}
  end

  defp manage_metadata_and_options(metadata, options) do
    {metadata, options}
  end

  @doc """
  run_statement for all Bolt version, but call differs.
  For Bolt <= 2, use: run_statement(transport, port, bolt_version, statement, params, options)
  For Bolt >=3: run_statement(transport, port, bolt_version, statement, params, metadata, options)

  Note that Bolt V2 calls works with Bolt V3, but it is preferrable to update them.
  """
  @spec run_statement(
          atom(),
          port(),
          integer(),
          String.t(),
          map(),
          nil | Keyword.t() | Bolt.Sips.Metadata.t(),
          nil | Keyword.t()
        ) ::
          list()
          | Bolt.Sips.Internals.Error.t()
  def run_statement(
        transport,
        port,
        bolt_version,
        statement,
        params \\ %{},
        options_v2_or_metadata_v3 \\ [],
        options_v3 \\ []
      )

  def run_statement(transport, port, bolt_version, statement, params, options_or_metadata, _)
      when bolt_version <= 2 do
    BoltProtocolV1.run_statement(
      transport,
      port,
      bolt_version,
      statement,
      params,
      options_or_metadata || []
    )
  end

  def run_statement(transport, port, bolt_version, statement, params, metadata, options)
      when bolt_version >= 2 do
    metadata =
      case metadata do
        [] -> %{}
        metadata -> metadata
      end

    BoltProtocolV3.run_statement(
      transport,
      port,
      bolt_version,
      statement,
      params,
      metadata,
      options
    )
  end
end


================================================
FILE: lib/bolt_sips/internals/bolt_protocol_helper.ex
================================================
defmodule Bolt.Sips.Internals.BoltProtocolHelper do
  @moduledoc false

  alias Bolt.Sips.Internals.PackStream.Message
  alias Bolt.Sips.Internals.Error

  @recv_timeout :infinity #10_000
  @zero_chunk <<0x00, 0x00>>
  @summary ~w(success ignored failure)a

  @doc """
  Sends a message using the Bolt protocol and PackStream encoding.

  Message have to be in the form of {message_type, [data]}.
  """
  @spec send_message(atom(), port(), integer(), Bolt.Sips.Internals.PackStream.Message.raw()) ::
          :ok | {:error, any()}
  def send_message(transport, port, bolt_version, message) do
    message
    |> Message.encode(bolt_version)
    |> (fn data -> transport.send(port, data) end).()
  end

  @doc """
  Receives data.

  This function is supposed to be called after a request to the server has been
  made. It receives data chunks, mends them (if they were split between frames)
  and decodes them using PackStream.

  When just a single message is received (i.e. to acknowledge a command), this
  function returns a tuple with two items, the first being the signature and the
  second being the message(s) itself. If a list of messages is received it will
  return a list of the former.

  The same goes for the messages: If there was a single data point in a message
  said data point will be returned by itself. If there were multiple data
  points, the list will be returned.

  The signature is represented as one of the following:

  * `:success`
  * `:record`
  * `:ignored`
  * `:failure`

  ## Options

  See "Shared options" in the documentation of this module.
  """
  @spec receive_data(atom(), port(), integer(), Keyword.t(), list()) ::
          {atom(), Bolt.Sips.Internals.PackStream.value()}
          | {:error, any()}
          | Bolt.Sips.Internals.Error.t()
  def receive_data(transport, port, bolt_version, options \\ [], previous \\ []) do
    with {:ok, data} <- do_receive_data(transport, port, options) do
      case Message.decode(data, bolt_version) do
        {:record, _} = data ->
          receive_data(transport, port, bolt_version, options, [data | previous])

        {status, _} = data when status in @summary and previous == [] ->
          data

        {status, _} = data when status in @summary ->
          Enum.reverse([data | previous])

        other ->
          {:error, Error.exception(other, port, :receive_data)}
      end
    else
      other ->
        # Should be the line below to have a cleaner typespec
        # Keep the old return value to not break usage
        # {:error, Error.exception(other, port, :receive_data)}
        Error.exception(other, port, :receive_data)
    end
  end

  @spec do_receive_data(atom(), port(), Keyword.t()) :: {:ok, binary()}
  defp do_receive_data(transport, port, options) do
    #recv_timeout = get_recv_timeout(options)

    case transport.recv(port, 2, :infinity) do
      {:ok, <<chunk_size::16>>} ->
        do_receive_data_(transport, port, chunk_size, options, <<>>)

      other ->
        other
    end
  end

  @spec do_receive_data_(atom(), port(), integer(), Keyword.t(), binary()) :: {:ok, binary()}
  defp do_receive_data_(transport, port, chunk_size, options, old_data) do
    recv_timeout = get_recv_timeout(options)

    with {:ok, data} <- transport.recv(port, chunk_size, recv_timeout),
         {:ok, marker} <- transport.recv(port, 2, recv_timeout) do
      case marker do
        @zero_chunk ->
          {:ok, <<old_data::binary, data::binary>>}

        <<chunk_size::16>> ->
          data = <<old_data::binary, data::binary>> 
          do_receive_data_(transport, port, chunk_size, options, data)
      end
    else
      other ->
        Error.exception(other, port, :recv)
    end
  end

  @doc """
  Define timeout
  """
  @spec get_recv_timeout(Keyword.t()) :: integer()
  def get_recv_timeout(options) do
    Keyword.get(options, :recv_timeout, @recv_timeout)
  end

  @doc """
  Deal with message without data.

  ## Example

      iex> BoltProtocolHelper.treat_simple_message(:reset, :gen_tcp, port, 1, [])
      :ok
  """
  @spec treat_simple_message(
          Bolt.Sips.Internals.Message.out_signature(),
          atom(),
          port(),
          integer(),
          Keyword.t()
        ) :: :ok | Error.t()
  def treat_simple_message(message, transport, port, bolt_version, options) do
    send_message(transport, port, bolt_version, {message, []})

    case receive_data(transport, port, bolt_version, options) do
      {:success, %{}} ->
        :ok

      error ->
        Error.exception(error, port, message)
    end
  end
end


================================================
FILE: lib/bolt_sips/internals/bolt_protocol_v1.ex
================================================
defmodule Bolt.Sips.Internals.BoltProtocolV1 do
  @moduledoc false
  alias Bolt.Sips.Internals.BoltProtocolHelper
  alias Bolt.Sips.Internals.BoltVersionHelper
  alias Bolt.Sips.Internals.Error

  @hs_magic <<0x60, 0x60, 0xB0, 0x17>>

  @doc """
  Initiates the handshake between the client and the server.

  See [http://boltprotocol.org/v1/#handshake](http://boltprotocol.org/v1/#handshake)

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV1.handshake(:gen_tcp, port, [])
      {:ok, bolt_version}
  """
  @spec handshake(atom(), port(), Keyword.t()) ::
          {:ok, integer()} | {:error, Bolt.Sips.Internals.Error.t()}
  def handshake(transport, port, options \\ [recv_timeout: 15_000]) do
    recv_timeout = BoltProtocolHelper.get_recv_timeout(options)
    max_version = BoltVersionHelper.last()

    # Define version list. Should be a 4 integer list
    # Example: [1, 0, 0, 0]
    versions =
      ((max_version..0
        |> Enum.into([])) ++ [0, 0, 0])
      |> Enum.take(4)

    Bolt.Sips.Internals.Logger.log_message(
      :client,
      :handshake,
      "#{inspect(@hs_magic, base: :hex)} #{inspect(versions)}"
    )

    data = @hs_magic <> Enum.into(versions, <<>>, fn version_ -> <<version_::32>> end)
    transport.send(port, data)

    case transport.recv(port, 4, recv_timeout) do
      {:ok, <<version::32>> = packet} when version <= max_version ->
        Bolt.Sips.Internals.Logger.log_message(:server, :handshake, packet, :hex)
        Bolt.Sips.Internals.Logger.log_message(:server, :handshake, version)
        {:ok, version}

      {:ok, other} ->
        {:error, Error.exception(other, port, :handshake)}

      other ->
        {:error, Error.exception(other, port, :handshake)}
    end
  end

  @doc """
  Initialises the connection.

  Expects a transport module (i.e. `gen_tcp`) and a `Port`. Accepts
  authorisation params in the form of {username, password}.

  See [https://boltprotocol.org/v1/#message-init](https://boltprotocol.org/v1/#message-init)

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Examples

      iex> Bolt.Sips.Internals.BoltProtocol.init(:gen_tcp, port, 1, {}, [])
      {:ok, info}

      iex> Bolt.Sips.Internals.BoltProtocol.init(:gen_tcp, port, 1, {"username", "password"}, [])
      {:ok, info}
  """
  @spec init(atom(), port(), integer(), tuple(), Keyword.t()) ::
          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}
  def init(transport, port, bolt_version, auth, options \\ [recv_timeout: 15_000]) do
    BoltProtocolHelper.send_message(transport, port, bolt_version, {:init, [auth]})

    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
      {:success, info} ->
        {:ok, info}

      {:failure, response} ->
        {:error, Error.exception(response, port, :init)}

      other ->
        {:error, Error.exception(other, port, :init)}
    end
  end

  @doc """
  Implementation of Bolt's RUN. It passes a statement for execution on the server.

  Note that this message doesn't return the statemetn result. For this purpose, use PULL_ALL.

  See [https://boltprotocol.org/v1/#message-run](https://boltprotocol.org/v1/#message-run)

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV1.run(:gen_tcp, port, 1, "RETURN $num AS num", %{num: 5}, [])
      {:ok, {:success, %{"fields" => ["num"]}}}
  """
  @spec run(atom(), port(), integer(), String.t(), map(), Keyword.t()) ::
          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}
  def run(transport, port, bolt_version, statement, params, options) do
    BoltProtocolHelper.send_message(transport, port, bolt_version, {:run, [statement, params]})

    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
      {:success, _} = result ->
        {:ok, result}

      {:failure, response} ->
        {:error, Error.exception(response, port, :run)}

      %Error{} = error ->
        {:error, error}

      other ->
        {:error, Error.exception(other, port, :run)}
    end
  end

  @doc """
  Implementation of Bolt's PULL_ALL. It retrieves all remaining items from the active result
  stream.

  See [https://boltprotocol.org/v1/#message-run](https://boltprotocol.org/v1/#message-run)

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV1.run(:gen_tcp, port, 1, "RETURN $num AS num", %{num: 5}, [])
      {:ok, {:success, %{"fields" => ["num"]}}}
      iex> BoltProtocolV1.pull_all(:gen_tcp, port_, 1, [])
      {:ok,
        [
          record: [5],
          success: %{"type" => "r"}
        ]}
  """
  @spec pull_all(atom(), port(), integer(), Keyword.t()) ::
          {:ok, list()}
          | {:failure, Bolt.Sips.Internals.Error.t()}
          | {:failure, Bolt.Sips.Internals.Error.t()}
  def pull_all(transport, port, bolt_version, options) do
    BoltProtocolHelper.send_message(transport, port, bolt_version, {:pull_all, []})

    with data <- BoltProtocolHelper.receive_data(transport, port, bolt_version, options),
         data <- List.wrap(data),
         {:success, _} <- List.last(data) do
      {:ok, data}
    else
      {:failure, response} ->
        {:failure, Error.exception(response, port, :pull_all)}

      other ->
        {:error, Error.exception(other, port, :pull_all)}
    end
  end

  @doc """
  Runs a statement (most likely Cypher statement) and returns a list of the
  records and a summary (Act as as a RUN + PULL_ALL).

  Records are represented using PackStream's record data type. Their Elixir
  representation is a Keyword with the indexes `:sig` and `:fields`.

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Examples

      iex> Bolt.Sips.Internals.BoltProtocol.run_statement(:gen_tcp, port, 1, "MATCH (n) RETURN n")
      [
        {:success, %{"fields" => ["n"]}},
        {:record, [sig: 1, fields: [1, "Example", "Labels", %{"some_attribute" => "some_value"}]]},
        {:success, %{"type" => "r"}}
      ]
  """
  @spec run_statement(atom(), port(), integer(), String.t(), map(), Keyword.t()) ::
          [
            Bolt.Sips.Internals.PackStream.Message.decoded()
          ]
          | Bolt.Sips.Internals.Error.t()
  def run_statement(transport, port, bolt_version, statement, params, options) do
    with {:ok, run_data} <- run(transport, port, bolt_version, statement, params, options),
         {:ok, result} <- pull_all(transport, port, bolt_version, options) do
      [run_data | result]
    else
      {:error, %Error{} = error} ->
        error

      other ->
        Error.exception(other, port, :run_statement)
    end
  end

  @doc """
  Implementation of Bolt's DISCARD_ALL. It discards all remaining items from the active result
  stream.

  See [https://boltprotocol.org/v1/#message-discard-all](https://boltprotocol.org/v1/#message-discard-all)

  See http://boltprotocol.org/v1/#message-ack-failure

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV1.discard_all(:gen_tcp, port, 1, [])
      :ok
  """
  @spec discard_all(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()
  def discard_all(transport, port, bolt_version, options) do
    BoltProtocolHelper.treat_simple_message(:discard_all, transport, port, bolt_version, options)
  end

  @doc """
  Implementation of Bolt's ACK_FAILURE. It acknowledges a failure while keeping
  transactions alive.

  See [http://boltprotocol.org/v1/#message-ack-failure](http://boltprotocol.org/v1/#message-ack-failure)

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV1.ack_failure(:gen_tcp, port, 1, [])
      :ok
  """
  @spec ack_failure(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()
  def ack_failure(transport, port, bolt_version, options) do
    BoltProtocolHelper.treat_simple_message(:ack_failure, transport, port, bolt_version, options)
  end

  @doc """
  Implementation of Bolt's RESET message. It resets a session to a "clean"
  state.

  See [http://boltprotocol.org/v1/#message-reset](http://boltprotocol.org/v1/#message-reset)

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV1.reset(:gen_tcp, port, 1, [])
      :ok
  """
  @spec reset(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()
  def reset(transport, port, bolt_version, options) do
    BoltProtocolHelper.treat_simple_message(:reset, transport, port, bolt_version, options)
  end
end


================================================
FILE: lib/bolt_sips/internals/bolt_protocol_v2.ex
================================================
defmodule Bolt.Sips.Internals.BoltProtocolV2 do
  @moduledoc false
  # There's no specific messagee for Bolt V2
  # This file exists only to fill the gap between the 2 bolt protocol versions
end


================================================
FILE: lib/bolt_sips/internals/bolt_protocol_v3.ex
================================================
defmodule Bolt.Sips.Internals.BoltProtocolV3 do
  alias Bolt.Sips.Internals.BoltProtocol
  alias Bolt.Sips.Internals.BoltProtocolHelper
  alias Bolt.Sips.Internals.Error

  @doc """
  Implementation of Bolt's HELLO. It initialises the connection.

  Expects a transport module (i.e. `gen_tcp`) and a `Port`. Accepts
  authorisation params in the form of {username, password}.


  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Examples

      iex> Bolt.Sips.Internals.BoltProtocolV3.hello(:gen_tcp, port, 3, {}, [])
      {:ok, info}

      iex> Bolt.Sips.Internals.BoltProtocolV3.hello(:gen_tcp, port, 3, {"username", "password"}, [])
      {:ok, info}
  """
  @spec hello(atom(), port(), integer(), tuple(), Keyword.t()) ::
          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}
  def hello(transport, port, bolt_version, auth, options \\ [recv_timeout: 15_000]) do
    BoltProtocolHelper.send_message(transport, port, bolt_version, {:hello, [auth]})

    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
      {:success, info} ->
        {:ok, info}

      {:failure, response} ->
        {:error, Error.exception(response, port, :hello)}

      other ->
        {:error, Error.exception(other, port, :hello)}
    end
  end

  @doc """
  Implementation of Bolt's RUN. It closes the connection.


  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Examples

      iex> Bolt.Sips.Internals.BoltProtocolV3.goodbye(:gen_tcp, port, 3)
      :ok

      iex> Bolt.Sips.Internals.BoltProtocolV3.goodbye(:gen_tcp, port, 3)
      :ok
  """
  def goodbye(transport, port, bolt_version) do
    BoltProtocolHelper.send_message(transport, port, bolt_version, {:goodbye, []})

    try do
      Port.close(port)
      :ok
    rescue
      ArgumentError -> Error.exception("Can't close port", port, :goodbye)
    end
  end

  @doc """
  Implementation of Bolt's RUN. It passes a statement for execution on the server.

  Note that this message doesn't return the statement result. For this purpose, use PULL_ALL.
  In bolt >= 3, run has an additional paramters; metadata

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV1.run(:gen_tcp, port, 1, "RETURN $num AS num", %{num: 5}, %{}, [])
      {:ok, {:success, %{"fields" => ["num"]}}}
  """
  @spec run(atom(), port(), integer(), String.t(), map(), Bolt.Sips.Metadata.t(), Keyword.t()) ::
          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}
  def run(transport, port, bolt_version, statement, params, metadata, options) do
    BoltProtocolHelper.send_message(
      transport,
      port,
      bolt_version,
      {:run, [statement, params, metadata]}
    )

    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
      {:success, _} = result ->
        {:ok, result}

      {:failure, response} ->
        {:error, Error.exception(response, port, :run)}

      %Error{} = error ->
        {:error, error}

      other ->
        {:error, Error.exception(other, port, :run)}
    end
  end

  @doc """
  Runs a statement (most likely Cypher statement) and returns a list of the
  records and a summary (Act as as a RUN + PULL_ALL).

  Records are represented using PackStream's record data type. Their Elixir
  representation is a Keyword with the indexes `:sig` and `:fields`.

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Examples

      iex> Bolt.Sips.Internals.BoltProtocol.run_statement(:gen_tcp, port, 1, "MATCH (n) RETURN n")
      [
        {:success, %{"fields" => ["n"]}},
        {:record, [sig: 1, fields: [1, "Example", "Labels", %{"some_attribute" => "some_value"}]]},
        {:success, %{"type" => "r"}}
      ]
  """
  @spec run_statement(
          atom(),
          port(),
          integer(),
          String.t(),
          map(),
          Bolt.Sips.Metadata.t(),
          Keyword.t()
        ) ::
          [
            Bolt.Sips.Internals.PackStream.Message.decoded()
          ]
          | Bolt.Sips.Internals.Error.t()
  def run_statement(transport, port, bolt_version, statement, params, metadata, options) do
    with {:ok, run_data} <-
           run(transport, port, bolt_version, statement, params, metadata, options),
         {:ok, result} <- BoltProtocol.pull_all(transport, port, bolt_version, options) do
      [run_data | result]
    else
      {:error, %Error{} = error} ->
        error

      other ->
        Error.exception(other, port, :run_statement)
    end
  end

  @doc """
  Implementation of Bolt's BEGIN. It opens a transaction.

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV3.begin(:gen_tcp, port, 3, [])
      {:ok, metadata}
  """
  @spec begin(atom(), port(), integer(), Bolt.Sips.Metadata.t() | map(), Keyword.t()) ::
          {:ok, any()} | Bolt.Sips.Internals.Error.t()
  def begin(transport, port, bolt_version, metadata, options) do
    BoltProtocolHelper.send_message(transport, port, bolt_version, {:begin, [metadata]})

    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
      {:success, info} ->
        {:ok, info}

      {:failure, response} ->
        {:error, Error.exception(response, port, :begin)}

      other ->
        {:error, Error.exception(other, port, :begin)}
    end
  end

  @doc """
  Implementation of Bolt's COMMIT. It commits the open transaction.

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV3.commit(:gen_tcp, port, 3, [])
      :ok
  """
  @spec commit(atom(), port(), integer(), Keyword.t()) ::
          {:ok, any()} | Bolt.Sips.Internals.Error.t()
  def commit(transport, port, bolt_version, options) do
    BoltProtocolHelper.send_message(transport, port, bolt_version, {:commit, []})

    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do
      {:success, info} ->
        {:ok, info}

      {:failure, response} ->
        {:error, Error.exception(response, port, :commit)}

      other ->
        {:error, Error.exception(other, port, :commit)}
    end
  end

  @doc """
  Implementation of Bolt's ROLLBACK. It rollbacks the open transaction.

  ## Options

  See "Shared options" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.

  ## Example

      iex> BoltProtocolV3.rollback(:gen_tcp, port, 3, [])
      :ok
  """
  @spec rollback(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()
  def rollback(transport, port, bolt_version, options) do
    BoltProtocolHelper.treat_simple_message(:rollback, transport, port, bolt_version, options)
  end
end


================================================
FILE: lib/bolt_sips/internals/bolt_version_helper.ex
================================================
defmodule Bolt.Sips.Internals.BoltVersionHelper do
  @moduledoc false
  @available_bolt_versions [1, 2, 3]

  @doc """
  List bolt versions.
  Only bolt version that have specific encoding functions are listed.

  """
  @spec available_versions() :: [integer()]
  def available_versions(), do: @available_bolt_versions

  @doc """
  Retrieve previous valid version.
  Return nil if there is no previous version.

  ## Example

      iex> Bolt.Sips.Internals.BoltVersionHelper.previous(2)
      1
      iex> Bolt.Sips.Internals.BoltVersionHelper.previous(1)
      nil
      iex> Bolt.Sips.Internals.BoltVersionHelper.previous(15)
      3
  """
  @spec previous(integer()) :: nil | integer()
  def previous(version) do
    @available_bolt_versions
    |> Enum.take_while(&(&1 < version))
    |> List.last()
  end

  @doc """
  Return the last available bolt version.

  ## Example:

      iex> Bolt.Sips.Internals.BoltVersionHelper.last()
      3
  """
  def last() do
    List.last(@available_bolt_versions)
  end
end


================================================
FILE: lib/bolt_sips/internals/error.ex
================================================
defmodule Bolt.Sips.Internals.Error do
  @moduledoc false
  defexception [:message, :code, :connection_id, :function, :type]

  @type t :: %__MODULE__{
          message: String.t(),
          code: nil | any(),
          connection_id: nil | integer(),
          function: atom(),
          type: atom()
        }

  @doc false
  # Produce a Bolt.Sips.Internals.Error depending on the context.
  @spec exception(any(), nil | port(), atom()) :: Bolt.Sips.Internals.Error.t()
  def exception(%{"message" => message, "code" => code}, pid, function) do
    %Bolt.Sips.Internals.Error{
      message: message,
      code: code,
      connection_id: get_id(pid),
      function: function,
      type: :cypher_error
    }
  end

  def exception({:error, :closed}, pid, function) do
    %Bolt.Sips.Internals.Error{
      message: "Port #{inspect(pid)} is closed",
      connection_id: get_id(pid),
      function: function,
      type: :connection_error
    }
  end

  def exception({:failure, %Bolt.Sips.Internals.Error{message: _message, code:  _code} = err}, _pid, _function) do
    err
  end

  def exception(%Bolt.Sips.Internals.Error{} = err, _pid, _function), do: err
  
  def exception(message, pid, function) do
    %Bolt.Sips.Internals.Error{
      message: message_for(function, message),
      connection_id: get_id(pid),
      function: function,
      type: :protocol_error
    }
  end

  @spec message_for(nil | atom(), any()) :: String.t()
  defp message_for(:handshake, "HTTP") do
    """
    Handshake failed.
    The port expected a HTTP request.
    This happens when trying to Neo4J using the REST API Port (default: 7474)
    instead of the Bolt Port (default: 7687).
    """
  end

  defp message_for(:handshake, bin) when is_binary(bin) do
    """
    Handshake failed.
    Expected 01:00:00:00 as a result, received: #{inspect(bin, base: :hex)}.
    """
  end

  defp message_for(:handshake, other) do
    """
    Handshake failed.
    Expected 01:00:00:00 as a result, received: #{inspect(other)}.
    """
  end

  defp message_for(nil, message) do
    """
    Unknown failure: #{inspect(message)}
    """
  end

  defp message_for(_function, {:error, error}) do
    case error |> :inet.format_error() |> to_string do
      "unknown POSIX error" -> to_string(error)
      other -> other
    end
  end

  defp message_for(_function, {:ignored, []}) do
    """
    The session is in a failed state and ignores further messages. You need to
    `ACK_FAILURE` or `RESET` in order to send new messages.
    """
  end

  defp message_for(function, message) do
    """
    #{function}: Unknown failure: #{inspect(message)}
    """
  end

  @spec get_id(any()) :: nil | integer()
  defp get_id({:sslsocket, {:gen_tcp, port, _tls, _unused_yet}, _pid}) do
    get_id(port)
  end

  defp get_id(port) when is_port(port) do
    case Port.info(port, :id) do
      {:id, id} -> id
      nil -> nil
    end
  end

  defp get_id(_), do: nil
end


================================================
FILE: lib/bolt_sips/internals/logger.ex
================================================
defmodule Bolt.Sips.Internals.Logger do
  @moduledoc false
  # Designed to log Bolt protocol message between Client and Server.
  #
  # The `from` parameter must be a atom, either `:client` or `:server`
  require Logger

  @doc """
  Produces a formatted Log for a message

  ## Example
      iex> Logger.log_message(:client, {:init, []})
  """
  def log_message(from, {type, data}) do
    msg_type = type |> Atom.to_string() |> String.upcase()
    do_log_message(from, fn -> "#{msg_type} ~ #{inspect(data)}" end)
  end

  @doc """
  Produces a formatted Log

  ## Example
      iex> Logger.log_message(:server, :handshake, 2)
  """
  def log_message(from, type, data) do
    if Application.get_env(:bolt_sips, :log) do
      log_message(from, {type, data})
    end
  end

  @doc """
  Produces a formatted Log for a message
  Data will be output in hexadecimal

  ## Example
      iex> Logger.log_message(:server, :handshake, <<0x02>>)
  """
  def log_message(from, type, data, :hex) do
    if Application.get_env(:bolt_sips, :log_hex, false) do
      msg_type = type |> Atom.to_string() |> String.upcase()

      do_log_message(from, fn ->
        "#{msg_type} ~ #{inspect(data, base: :hex, limit: :infinity)}"
      end)
    end
  end

  defp do_log_message(from, func) when is_function(func) do
    from_txt =
      case from do
        :server -> "S"
        :client -> "C"
      end

    Logger.debug(fn -> "#{from_txt}: #{func.()}" end)
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/decoder.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Decoder do
  @moduledoc false
  _moduledoc = """
  This module is responsible for dispatching decoding amongst decoder depending on the
  used bolt version.

  Most of the documentation regarding Bolt binary format can be found in
  `Bolt.Sips.Internals.PackStream.EncoderV1` and `Bolt.Sips.Internals.PackStream.EncoderV2`.

  Here will be found ocumenation about data that are only availalbe for decoding::
  - Node
  - Relationship
  - Unbound relationship
  - Path
  """

  use Bolt.Sips.Internals.PackStream.DecoderImplV1
  use Bolt.Sips.Internals.PackStream.DecoderImplV2
  use Bolt.Sips.Internals.PackStream.DecoderUtils
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/decoder_impl_v1.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.DecoderImplV1 do
  alias Bolt.Sips.Types

  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)

      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()

      # Null
      @null_marker 0xC0

      # Boolean
      @true_marker 0xC3
      @false_marker 0xC2

      # String
      @tiny_bitstring_marker 0x8
      @bitstring8_marker 0xD0
      @bitstring16_marker 0xD1
      @bitstring32_marker 0xD2

      # Integer
      @int8_marker 0xC8
      @int16_marker 0xC9
      @int32_marker 0xCA
      @int64_marker 0xCB

      # Float
      @float_marker 0xC1

      # List
      @tiny_list_marker 0x9
      @list8_marker 0xD4
      @list16_marker 0xD5
      @list32_marker 0xD6

      # Map
      @tiny_map_marker 0xA
      @map8_marker 0xD8
      @map16_marker 0xD9
      @map32_marker 0xDA

      # Structure
      @tiny_struct_marker 0xB
      @struct8_marker 0xDC
      @struct16_marker 0xDD

      # Node
      @node_marker 0x4E

      # Relationship
      @relationship_marker 0x52

      # Unbounded relationship
      @unbounded_relationship_marker 0x72

      # Path
      @path_marker 0x50

      @spec decode(binary() | {integer(), binary(), integer()}, integer()) ::
              list() | {:error, :not_implemented}
      def decode(<<@null_marker, rest::binary>>, bolt_version) when bolt_version <= @last_version do
        [nil | decode(rest, bolt_version)]
      end

      # Boolean
      def decode(<<@true_marker, rest::binary>>, bolt_version) when bolt_version <= @last_version do
        [true | decode(rest, bolt_version)]
      end

      def decode(<<@false_marker, rest::binary>>, bolt_version) when bolt_version <= @last_version do
        [false | decode(rest, bolt_version)]
      end

      # Float
      def decode(<<@float_marker, number::float, rest::binary>>, bolt_version)
          when bolt_version <= @last_version do
        [number | decode(rest, bolt_version)]
      end

      # Strings
      def decode(<<@tiny_bitstring_marker::4, str_length::4, rest::bytes>>, bolt_version)
          when bolt_version <= @last_version do
        decode_string(rest, str_length, bolt_version)
      end

      def decode(<<@bitstring8_marker, str_length, rest::bytes>>, bolt_version)
          when bolt_version <= @last_version do
        decode_string(rest, str_length, bolt_version)
      end

      def decode(<<@bitstring16_marker, str_length::16, rest::bytes>>, bolt_version)
          when bolt_version <= @last_version do
        decode_string(rest, str_length, bolt_version)
      end

      def decode(<<@bitstring32_marker, str_length::32, rest::binary>>, bolt_version)
          when bolt_version <= @last_version do
        decode_string(rest, str_length, bolt_version)
      end

      # Lists
      def decode(<<@tiny_list_marker::4, list_size::4>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_list(bin, list_size, bolt_version)
      end

      def decode(<<@list8_marker, list_size::8>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_list(bin, list_size, bolt_version)
      end

      def decode(<<@list16_marker, list_size::16>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_list(bin, list_size, bolt_version)
      end

      def decode(<<@list32_marker, list_size::32>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_list(bin, list_size, bolt_version)
      end

      # Maps
      def decode(<<@tiny_map_marker::4, entries::4>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_map(bin, entries, bolt_version)
      end

      def decode(<<@map8_marker, entries::8>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_map(bin, entries, bolt_version)
      end

      def decode(<<@map16_marker, entries::16>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_map(bin, entries, bolt_version)
      end

      def decode(<<@map32_marker, entries::32>> <> bin, bolt_version)
          when bolt_version <= @last_version do
        decode_map(bin, entries, bolt_version)
      end

      # Struct
      def decode(<<@tiny_struct_marker::4, struct_size::4, sig::8>> <> struct, bolt_version)
          when bolt_version <= @last_version do
        decode({sig, struct, struct_size}, bolt_version)
      end

      def decode(<<@struct8_marker, struct_size::8, sig::8>> <> struct, bolt_version)
          when bolt_version <= @last_version do
        decode({sig, struct, struct_size}, bolt_version)
      end

      def decode(<<@struct16_marker, struct_size::16, sig::8>> <> struct, bolt_version)
          when bolt_version <= @last_version do
        decode({sig, struct, struct_size}, bolt_version)
      end

      ######### SPECIAL STRUCTS

      # Node
      def decode({@node_marker, struct, struct_size}, bolt_version)
          when bolt_version <= @last_version do
        {[id, labels, props], rest} = decode_struct(struct, struct_size, bolt_version)

        node = %Types.Node{id: id, labels: labels, properties: props}

        [node | rest]
      end

      # Relationship
      def decode({@relationship_marker, struct, struct_size}, bolt_version)
          when bolt_version <= @last_version do
        {[id, start_node, end_node, type, props], rest} =
          decode_struct(struct, struct_size, bolt_version)

        relationship = %Types.Relationship{
          id: id,
          start: start_node,
          end: end_node,
          type: type,
          properties: props
        }

        [relationship | rest]
      end

      # UnboundedRelationship
      def decode({@unbounded_relationship_marker, struct, struct_size}, bolt_version)
          when bolt_version <= @last_version do
        {[id, type, props], rest} = decode_struct(struct, struct_size, bolt_version)

        unbounded_relationship = %Types.UnboundRelationship{
          id: id,
          type: type,
          properties: props
        }

        [unbounded_relationship | rest]
      end

      # Path
      def decode({@path_marker, struct, struct_size}, bolt_version)
          when bolt_version <= @last_version do
        {[nodes, relationships, sequence], rest} =
          decode_struct(struct, struct_size, bolt_version)

        path = %Types.Path{
          nodes: nodes,
          relationships: relationships,
          sequence: sequence
        }

        [path | rest]
      end

      # Manage the end of data
      def decode("", bolt_version) when bolt_version <= @last_version do
        []
      end

      # Integers
      def decode(<<@int8_marker, int::signed-integer, rest::binary>>, bolt_version)
          when bolt_version <= @last_version do
        [int | decode(rest, bolt_version)]
      end

      def decode(<<@int16_marker, int::signed-integer-16, rest::binary>>, bolt_version)
          when bolt_version <= @last_version do
        [int | decode(rest, bolt_version)]
      end

      def decode(<<@int32_marker, int::signed-integer-32, rest::binary>>, bolt_version)
          when bolt_version <= @last_version do
        [int | decode(rest, bolt_version)]
      end

      def decode(<<@int64_marker, int::signed-integer-64, rest::binary>>, bolt_version)
          when bolt_version <= @last_version do
        [int | decode(rest, bolt_version)]
      end

      def decode(<<int::signed-integer, rest::binary>>, bolt_version)
          when bolt_version <= @last_version do
        [int | decode(rest, bolt_version)]
      end
    end
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/decoder_impl_v2.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.DecoderImplV2 do
  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration, Point}

  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)

      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()

      # Null
      @null_marker 0xC0

      # Boolean
      @true_marker 0xC3
      @false_marker 0xC2

      # String
      @tiny_bitstring_marker 0x8
      @bitstring8_marker 0xD0
      @bitstring16_marker 0xD1
      @bitstring32_marker 0xD2

      # Integer
      @int8_marker 0xC8
      @int16_marker 0xC9
      @int32_marker 0xCA
      @int64_marker 0xCB

      # Float
      @float_marker 0xC1

      # List
      @tiny_list_marker 0x9
      @list8_marker 0xD4
      @list16_marker 0xD5
      @list32_marker 0xD6

      # Map
      @tiny_map_marker 0xA
      @map8_marker 0xD8
      @map16_marker 0xD9
      @map32_marker 0xDA

      # Structure
      @tiny_struct_marker 0xB
      @struct8_marker 0xDC
      @struct16_marker 0xDD

      # Node
      @node_marker 0x4E

      # Relationship
      @relationship_marker 0x52

      # Unbounded relationship
      @unbounded_relationship_marker 0x72

      # Path
      @path_marker 0x50

      # Local Time
      @local_time_signature 0x74
      @local_time_struct_size 1

      # Time With TZ Offset
      @time_with_tz_signature 0x54
      @time_with_tz_struct_size 2

      # Date
      @date_signature 0x44
      @date_struct_size 1

      # Local DateTime
      @local_datetime_signature 0x64
      @local_datetime_struct_size 2

      # Datetime with TZ offset
      @datetime_with_zone_offset_signature 0x46
      @datetime_with_zone_offset_struct_size 3

      # Datetime with TZ id
      @datetime_with_zone_id_signature 0x66
      @datetime_with_zone_id_struct_size 3

      # Duration
      @duration_signature 0x45
      @duration_struct_size 4

      # Point 2D
      @point2d_signature 0x58
      @point2d_struct_size 3

      # Point 3D
      @point3d_signature 0x59
      @point3d_struct_size 4

      # Local Date
      def decode({@date_signature, struct, @date_struct_size}, bolt_version)
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[date], rest} = decode_struct(struct, @date_struct_size, bolt_version)
        [Date.add(~D[1970-01-01], date) | rest]
      end

      # Local Time
      def decode({@local_time_signature, struct, @local_time_struct_size}, bolt_version)
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[time], rest} = decode_struct(struct, @local_time_struct_size, bolt_version)

        [Time.add(~T[00:00:00.000000], time, :nanosecond) | rest]
      end

      # Local DateTime
      def decode({@local_datetime_signature, struct, @local_datetime_struct_size}, bolt_version)
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[seconds, nanoseconds], rest} =
          decode_struct(struct, @local_datetime_struct_size, bolt_version)

        ndt =
          NaiveDateTime.add(
            ~N[1970-01-01 00:00:00.000000000],
            seconds * 1_000_000_000 + nanoseconds,
            :nanosecond
          )

        [ndt | rest]
      end

      # Time with Zone Offset
      def decode({@time_with_tz_signature, struct, @time_with_tz_struct_size}, bolt_version)
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[time, offset], rest} = decode_struct(struct, @time_with_tz_struct_size, bolt_version)

        t = TimeWithTZOffset.create(Time.add(~T[00:00:00.000000], time, :nanosecond), offset)
        [t | rest]
      end

      # Datetime with zone Id
      def decode(
            {@datetime_with_zone_id_signature, struct, @datetime_with_zone_id_struct_size},
            bolt_version
          )
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[seconds, nanoseconds, zone_id], rest} =
          decode_struct(struct, @datetime_with_zone_id_struct_size, bolt_version)

        naive_dt =
          NaiveDateTime.add(
            ~N[1970-01-01 00:00:00.000000],
            seconds * 1_000_000_000 + nanoseconds,
            :nanosecond
          )

        dt = Bolt.Sips.TypesHelper.datetime_with_micro(naive_dt, zone_id)
        [dt | rest]
      end

      # Datetime with zone offset
      def decode(
            {@datetime_with_zone_offset_signature, struct,
             @datetime_with_zone_offset_struct_size},
            bolt_version
          )
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[seconds, nanoseconds, zone_offset], rest} =
          decode_struct(struct, @datetime_with_zone_id_struct_size, bolt_version)

        naive_dt =
          NaiveDateTime.add(
            ~N[1970-01-01 00:00:00.000000],
            seconds * 1_000_000_000 + nanoseconds,
            :nanosecond
          )

        dt = DateTimeWithTZOffset.create(naive_dt, zone_offset)
        [dt | rest]
      end

      # Duration
      def decode({@duration_signature, struct, @duration_struct_size}, bolt_version)
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[months, days, seconds, nanoseconds], rest} =
          decode_struct(struct, @duration_struct_size, bolt_version)

        duration = Duration.create(months, days, seconds, nanoseconds)
        [duration | rest]
      end

      # Point2D
      def decode({@point2d_signature, struct, @point2d_struct_size}, bolt_version)
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[srid, x, y], rest} = decode_struct(struct, @point2d_struct_size, bolt_version)
        point = Point.create(srid, x, y)

        [point | rest]
      end

      # Point3D
      def decode({@point3d_signature, struct, @point3d_struct_size}, bolt_version)
          when bolt_version >= 2 and bolt_version <= @last_version do
        {[srid, x, y, z], rest} = decode_struct(struct, @point3d_struct_size, bolt_version)
        point = Point.create(srid, x, y, z)

        [point | rest]
      end
    end
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/decoder_utils.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.DecoderUtils do
  alias Bolt.Sips.Internals.PackStreamError

  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)

      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()

      def decode(data, bolt_version) when is_integer(bolt_version) do
        if bolt_version > @last_version do
          decode(data, @last_version)
        else
          raise PackStreamError,
            data: data,
            bolt_version: bolt_version,
            message: "Unsupported decoder version"
        end
      end

      def decode(_, _) do
        {:error, :not_implemented}
      end

      @doc """
      Decodes a struct
      """
      @spec decode_struct(binary(), integer(), integer()) :: {list(), list()}
      def decode_struct(struct, struct_size, bolt_version) do
        struct
        |> decode(bolt_version)
        |> Enum.split(struct_size)
      end

      @spec to_map(list()) :: map()
      defp to_map(map) do
        map
        |> Enum.chunk_every(2)
        |> Enum.map(&List.to_tuple/1)
        |> Map.new()
      end

      @spec decode_string(binary(), integer(), integer()) :: list()
      defp decode_string(bytes, str_length, bolt_version) do
        <<string::binary-size(str_length), rest::binary>> = bytes

        [string | decode(rest, bolt_version)]
      end

      @spec decode_list(binary(), integer(), integer()) :: list()
      defp decode_list(list, list_size, bolt_version) do
        {list, rest} = list |> decode(bolt_version) |> Enum.split(list_size)
        [list | rest]
      end

      @spec decode_map(binary(), integer(), integer()) :: list()
      defp decode_map(map, entries, bolt_version) do
        {map, rest} = map |> decode(bolt_version) |> Enum.split(entries * 2)

        [to_map(map) | rest]
      end
    end
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/decoder_v1.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.DecoderV1 do
  @moduledoc false
  _moduledoc = """
  Bolt V1 can decode:
  - Null
  - Boolean
  - Integer
  - Float
  - String
  - List
  - Map
  - Struct

  Functions from this module are not meant to be used directly.
  Use `Decoder.decode(data, bolt_version)` for all decoding purposes.
  """

  use Bolt.Sips.Internals.PackStream.Markers
  alias Bolt.Sips.Internals.PackStream.Decoder

  @spec decode(binary() | {integer(), binary(), integer()}, integer()) ::
          list() | {:error, :not_implemented}
  def decode(data, bolt_version), do: Decoder.decode(data, bolt_version)
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/decoder_v2.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.DecoderV2 do
  @moduledoc false
  _module_doc = """
  Bolt V2 has specification for decoding:
  - Temporal types:
    - Local Date
    - Local Time
    - Local DateTime
    - Time with Timezone Offset
    - DateTime with Timezone Id
    - DateTime with Timezone Offset
    - Duration
  - Spatial types:
    - Point2D
    - Point3D

  For documentation about those typs representation in Bolt binary,
  please see `Bolt.Sips.Internals.PackStream.EncoderV2`.

  Functions from this module are not meant to be used directly.
  Use `Decoder.decode(data, bolt_version)` for all decoding purposes.
  """

  use Bolt.Sips.Internals.PackStream.Markers
  alias Bolt.Sips.Internals.PackStream.Decoder

  # Local Date
  @spec decode({integer(), binary(), integer()}, integer()) :: list() | {:error, :not_implemented}
  def decode(data, bolt_version), do: Decoder.decode(data, bolt_version)

end


================================================
FILE: lib/bolt_sips/internals/pack_stream/decoder_v3.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.DecoderV3 do
  def decode(_, _) do
    {:error, :not_implemented}
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/encoder.ex
================================================
alias Bolt.Sips.Internals.PackStream
alias Bolt.Sips.Internals.PackStream.EncoderHelper

defprotocol Bolt.Sips.Internals.PackStream.Encoder do
  @moduledoc false

  # Encodes an item to its binary PackStream Representation
  #
  # Implementation exists for following types:
  #   - Integer
  #   - Float
  #   - List
  #   - Map
  #   - Struct (defined in the Bolt protocol)
  @fallback_to_any true

  @doc """
  Encode entity into its Bolt binary represenation depending of the used bolt version
  """

  @spec encode(any(), integer()) :: binary()
  def encode(entity, bolt_version)
end

defimpl PackStream.Encoder, for: Atom do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:atom, data, bolt_version)
end

defimpl PackStream.Encoder, for: BitString do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:string, data, bolt_version)
end

defimpl PackStream.Encoder, for: Integer do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:integer, data, bolt_version)
end

defimpl PackStream.Encoder, for: Float do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:float, data, bolt_version)
end

defimpl PackStream.Encoder, for: List do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:list, data, bolt_version)
end

defimpl PackStream.Encoder, for: Map do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:map, data, bolt_version)
end

defimpl PackStream.Encoder, for: Time do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:local_time, data, bolt_version)
end

defimpl PackStream.Encoder, for: Bolt.Sips.Types.TimeWithTZOffset do
  def encode(data, bolt_version) do
    EncoderHelper.call_encode(:time_with_tz, data, bolt_version)
  end
end

defimpl PackStream.Encoder, for: Date do
  def encode(data, bolt_version), do: EncoderHelper.call_encode(:date, data, bolt_version)
end

defimpl PackStream.Encoder, for: NaiveDateTime do
  def encode(data, bolt_version) do
    EncoderHelper.call_encode(:local_datetime, data, bolt_version)
  end
end

defimpl PackStream.Encoder, for: DateTime do
  def encode(data, version) do
    EncoderHelper.call_encode(:datetime_with_tz_id, data, version)
  end
end

defimpl PackStream.Encoder, for: Bolt.Sips.Types.DateTimeWithTZOffset do
  def encode(data, version) do
    EncoderHelper.call_encode(:datetime_with_tz_offset, data, version)
  end
end

defimpl PackStream.Encoder, for: Bolt.Sips.Types.Duration do
  def encode(data, version), do: EncoderHelper.call_encode(:duration, data, version)
end

defimpl PackStream.Encoder, for: Bolt.Sips.Types.Point do
  def encode(data, version), do: EncoderHelper.call_encode(:point, data, version)
end

defimpl PackStream.Encoder, for: Any do
  @spec encode({integer(), list()} | %{:__struct__ => String.t()}, integer()) ::
          Bolt.Sips.Internals.PackStream.value() | <<_::16, _::_*8>>
  def encode({signature, data}, bolt_version) when is_list(data) do
    valid_signatures =
      PackStream.Message.Encoder.valid_signatures(bolt_version) ++
        Bolt.Sips.Internals.PackStream.MarkersHelper.valid_signatures()

    if signature in valid_signatures do
      EncoderHelper.call_encode(:struct, {signature, data}, bolt_version)
    else
      raise Bolt.Sips.Internals.PackStreamError,
        message: "Unable to encode",
        data: data,
        bolt_version: bolt_version
    end
  end

  # Elixir structs just need to be convertedd to map befoare being encoded
  def encode(%{__struct__: _} = data, bolt_version) do
    map = Map.from_struct(data)
    PackStream.Encoder.encode(map, bolt_version)
  end

  def encode(data, bolt_version) do
    raise Bolt.Sips.Internals.PackStreamError,
      message: "Unable to encode",
      data: data,
      bolt_version: bolt_version
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/encoder_helper.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.EncoderHelper do
  @moduledoc false
  alias Bolt.Sips.Internals.BoltVersionHelper
  alias Bolt.Sips.Internals.PackStreamError

  use Bolt.Sips.Internals.PackStream.V1
  use Bolt.Sips.Internals.PackStream.V2
  use Bolt.Sips.Internals.PackStream.Utils

  @available_bolt_versions BoltVersionHelper.available_versions()
  @last_version BoltVersionHelper.last()


  @doc """
  For the given `data_type` and `bolt_version`, determine the right enconding function
  and call it agains `data`
  """
  @spec call_encode(atom(), any(), any()) :: binary() | PackStreamError.t()
  def call_encode(data_type, data, bolt_version)
      when is_integer(bolt_version) and bolt_version in @available_bolt_versions do
    do_call_encode(data_type, data, bolt_version)
  end

  def call_encode(data_type, data, bolt_version) when is_integer(bolt_version) do
    if bolt_version > @last_version do
      call_encode(data_type, data, @last_version)
    else
      raise PackStreamError,
        data_type: data_type,
        data: data,
        bolt_version: bolt_version,
        message: "Unsupported encoder version"
    end
  end

  def call_encode(data_type, data, bolt_version) do
    raise PackStreamError,
      data_type: data_type,
      data: data,
      bolt_version: bolt_version,
      message: "Unsupported encoder version"
  end

end


================================================
FILE: lib/bolt_sips/internals/pack_stream/encoder_v1.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.EncoderV1 do
  @moduledoc false
  alias Bolt.Sips.Internals.PackStream.EncoderHelper
  use Bolt.Sips.Internals.PackStream.Markers


  @doc """
  Encode an atom into Bolt binary format.

  Encoding:
  `Marker`

  with

  | Value | Marker |
  | ------- | -------- |
  | nil | `0xC0` |
  | false | `0xC2` |
  | true | `0xC3` |

  Other atoms are converted to string before encoding.

  ## Example

      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1
      iex> :erlang.iolist_to_binary(EncoderV1.encode_atom(nil, 1))
      <<0xC0>>
      iex> :erlang.iolist_to_binary(EncoderV1.encode_atom(true, 1))
      <<0xC3>>
      iex> :erlang.iolist_to_binary(EncoderV1.encode_atom(:guten_tag, 1))
      <<0x89, 0x67, 0x75, 0x74, 0x65, 0x6E, 0x5F, 0x74, 0x61, 0x67>>
  """
  @spec encode_atom(atom(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_atom(atom , bolt_version), do: EncoderHelper.call_encode(:atom, atom, bolt_version) 

  @doc """
  Encode a string into Bolt binary format.

  Encoding:
  `Marker` `Size` `Content`

  with

  | Marker | Size | Max data size |
  |--------|------|---------------|
  | `0x80`..`0x8F` | None (contained in marker) | 15 bytes |
  | `0xD0` | 8-bit integer | 255 bytes |
  | `0xD1` | 16-bit integer | 65_535 bytes |
  | `0xD2` | 32-bit integer | 4_294_967_295 bytes |

  ## Example

      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1
      iex> :erlang.iolist_to_binary(EncoderV1.encode_string("guten tag", 1))
      <<0x89, 0x67, 0x75, 0x74, 0x65, 0x6E, 0x20, 0x74, 0x61, 0x67>>
  """
  @spec encode_string(String.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_string(string, bolt_version), do: EncoderHelper.call_encode(:string, string, bolt_version)


  @doc """
  Encode an integer into Bolt binary format.

  Encoding:
  `Marker` `Value`

  with

  |   | Marker |
  |---|--------|
  | tiny int | `0x2A` |
  | int8 | `0xC8` |
  | int16 | `0xC9` |
  | int32 | `0xCA` |
  | int64 | `0xCB` |

  ## Example

      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1
      iex> :erlang.iolist_to_binary(EncoderV1.encode_integer(74, 1))
      <<0x4A>>
      iex> :erlang.iolist_to_binary(EncoderV1.encode_integer(-74_789, 1))
      <<0xCA, 0xFF, 0xFE, 0xDB, 0xDB>>
  """
  @spec encode_integer(integer(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_integer(integer, bolt_version), do: EncoderHelper.call_encode(:integer, integer, bolt_version)

  @doc """
  Encode a float into Bolt binary format.

  Encoding: `Marker` `8 byte Content`.

  Marker: `0xC1`

  Formated according to the IEEE 754 floating-point "double format" bit layout.

  ## Example

      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1
      iex> :erlang.iolist_to_binary(EncoderV1.encode_float(42.42, 1))
      <<0xC1, 0x40, 0x45, 0x35, 0xC2, 0x8F, 0x5C, 0x28, 0xF6>>
  """
  @spec encode_float(float(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_float(number, bolt_version), do: EncoderHelper.call_encode(:float, number, bolt_version) 

  @doc """
  Encode a list into Bolt binary format.

  Encoding:
  `Marker` `Size` `Content`

  with

  | Marker | Size | Max list size |
  |--------|------|---------------|
  | `0x90`..`0x9F` | None (contained in marker) | 15 bytes |
  | `0xD4` | 8-bit integer | 255 items |
  | `0xD5` | 16-bit integer | 65_535 items |
  | `0xD6` | 32-bit integer | 4_294_967_295 items |

  ## Example

      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1
      iex> :erlang.iolist_to_binary(EncoderV1.encode_list(["hello", "world"], 1))
      <<0x92, 0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x85, 0x77, 0x6F, 0x72, 0x6C, 0x64>>
  """
  @spec encode_list(list(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_list(list, bolt_version), do: EncoderHelper.call_encode(:list, list, bolt_version)

  @doc """
  Encode a map into Bolt binary format.

  Note that Elixir structs are converted to map for encoding purpose.

  Encoding:
  `Marker` `Size` `Content`

  with

  | Marker | Size | Max map size |
  |--------|------|---------------|
  | `0xA0`..`0xAF` | None (contained in marker) | 15 entries |
  | `0xD8` | 8-bit integer | 255 entries |
  | `0xD9` | 16-bit integer | 65_535 entries |
  | `0xDA` | 32-bit integer | 4_294_967_295 entries |

  ## Example

      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1
      iex> :erlang.iolist_to_binary(EncoderV1.encode_map(%{id: 345, value: "hello world"}, 1))
      <<0xA2, 0x82, 0x69, 0x64, 0xC9, 0x1, 0x59, 0x85, 0x76, 0x61, 0x6C, 0x75,
      0x65, 0x8B, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64>>
  """
  @spec encode_map(map(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_map(map, bolt_version), do: EncoderHelper.call_encode(:map, map, bolt_version)


  @doc """
  Encode a struct into Bolt binary format.
  This concerns Bolt Structs as defined in []().
  Elixir structs are just converted to regular maps before encoding

  Encoding:
  `Marker` `Size` `Signature` `Content`

  with

  | Marker | Size | Max structure size |
  |--------|------|---------------|
  | `0xB0`..`0xBF` | None (contained in marker) | 15 fields |
  | `0xDC` | 8-bit integer | 255 fields |
  | `0xDD` | 16-bit integer | 65_535 fields |

  ## Example

      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1
      iex> :erlang.iolist_to_binary(EncoderV1.encode_struct({0x01, ["two", "params"]}, 1))
      <<0xB2, 0x1, 0x83, 0x74, 0x77, 0x6F, 0x86, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73>>

  """
  @spec encode_struct({integer(), list()}, integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_struct(struct, bolt_version) , do: EncoderHelper.call_encode(:struct, struct, bolt_version)
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/encoder_v2.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.EncoderV2 do
  @moduledoc false
  use Bolt.Sips.Internals.PackStream.Markers
  alias Bolt.Sips.Internals.PackStream.EncoderHelper
  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration, Point}

  @doc """
  Encode a Time (represented by Time) into Bolt binary format.
  Encoded in a structure.

  Signature: `0x74`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `Nanoseconds_from_00:00:00`

  ## Example

      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_local_time(~T[06:54:32.453], 2))
      <<0xB1, 0x74, 0xCB, 0x0, 0x0, 0x16, 0x9F, 0x11, 0xB9, 0xCB, 0x40>>
  """
  @spec encode_local_time(Time.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_local_time(local_time, bolt_version), do: EncoderHelper.call_encode(:local_time, local_time, bolt_version)

  @doc """
  Encode a TIME WITH TIMEZONE OFFSET (represented by TimeWithTZOffset) into Bolt binary format.
  Encoded in a structure.

  Signature: `0x54`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `Nanoseconds_from_00:00:00` `Offset_in_seconds`

  ## Example

      iex> time_with_tz = Bolt.Sips.Types.TimeWithTZOffset.create(~T[06:54:32.453], 3600)
      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_time_with_tz(time_with_tz, 2))
      <<0xB2, 0x54, 0xCB, 0x0, 0x0, 0x16, 0x9F, 0x11, 0xB9, 0xCB, 0x40, 0xC9, 0xE, 0x10>>
  """
  def encode_time_with_tz(%TimeWithTZOffset{time: time, timezone_offset: offset}, bolt_version), do: EncoderHelper.call_encode(:time_with_tz, %TimeWithTZOffset{time: time, timezone_offset: offset}, bolt_version )

  @doc """
  Encode a DATE (represented by Date) into Bolt binary format.
  Encoded in a structure.

  Signature: `0x44`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `Nb_days_since_epoch`

  ## Example

      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_date(~D[2019-04-23], 2))
      <<0xB1, 0x44, 0xC9, 0x46, 0x59>>

  """
  @spec encode_date(Date.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_date(date, bolt_version), do: EncoderHelper.call_encode(:date, date, bolt_version)

  @doc """
  Encode a LOCAL DATETIME (Represented by NaiveDateTime) into Bolt binary format.
  Encoded in a structure.

  WARNING: Nanoseconds are left off as NaiveDateTime doesn't handle them.
  A new Calendar should be implemented to manage them.

  Signature: `0x64`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `Nb_seconds_since_epoch` `Remainder_in_nanoseconds`

  ## Example

      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_local_datetime(~N[2019-04-23 13:45:52.678], 2))
      <<0xB2, 0x64, 0xCA, 0x5C, 0xBF, 0x17, 0x10, 0xCA, 0x28, 0x69, 0x75, 0x80>>

  """
  @spec encode_local_datetime(Calendar.naive_datetime(), integer()) ::
          Bolt.Sips.Internals.PackStream.value()
  def encode_local_datetime(local_datetime, bolt_version), do: EncoderHelper.call_encode(:local_datetime, local_datetime, bolt_version)

  @doc """
  Encode DATETIME WITH TIMEZONE ID (represented by Calendar.DateTime) into Bolt binary format.
  Encoded in a structure.

  WARNING: Nanoseconds are left off as NaiveDateTime doesn't handle them.
  A new Calendar should be implemented to manage them.

  Signature: `0x66`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `Nb_seconds_since_epoch` `Remainder_in_nanoseconds` `Zone_id`

  ## Example

      iex> d = Bolt.Sips.TypesHelper.datetime_with_micro(~N[2013-11-12 07:32:02.003],
      ...> "Europe/Berlin")
      #DateTime<2013-11-12 07:32:02.003+01:00 CET Europe/Berlin>
      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_datetime_with_tz_id(d, 2))
      <<0xB3, 0x66, 0xCA, 0x52, 0x81, 0xD9, 0x72, 0xCA, 0x0, 0x2D, 0xC6, 0xC0, 0x8D, 0x45, 0x75,
      0x72, 0x6F, 0x70, 0x65, 0x2F, 0x42, 0x65, 0x72, 0x6C, 0x69, 0x6E>>

  """
  @spec encode_datetime_with_tz_id(Calendar.datetime(), integer()) ::
          Bolt.Sips.Internals.PackStream.value()
  def encode_datetime_with_tz_id(datetime, bolt_version), do: EncoderHelper.call_encode(:datetime_with_tz_id, datetime, bolt_version)

  @doc """
  Encode DATETIME WITH TIMEZONE OFFSET (represented by DateTimeWithTZOffset) into Bolt binary format.
  Encoded in a structure.

  WARNING: Nanoseconds are left off as NaiveDateTime doesn't handle them.
  A new Calendar should be implemented to manage them.

  Signature: `0x46`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `Nb_seconds_since_epoch` `Remainder_in_nanoseconds` `Zone_offset`

  ## Example

      iex> d = Bolt.Sips.Types.DateTimeWithTZOffset.create(~N[2013-11-12 07:32:02.003], 7200)
      %Bolt.Sips.Types.DateTimeWithTZOffset{
              naive_datetime: ~N[2013-11-12 07:32:02.003],
              timezone_offset: 7200
            }
      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_datetime_with_tz_offset(d, 2))
      <<0xB3, 0x46, 0xCA, 0x52, 0x81, 0xD9, 0x72, 0xCA, 0x0, 0x2D, 0xC6, 0xC0, 0xC9, 0x1C, 0x20>>

  """
  @spec encode_datetime_with_tz_offset(DateTimeWithTZOffset.t(), integer()) ::
          Bolt.Sips.Internals.PackStream.value()
  def encode_datetime_with_tz_offset(
        %DateTimeWithTZOffset{naive_datetime: ndt, timezone_offset: tz_offset},
        bolt_version
  ), do: EncoderHelper.call_encode(:datetime_with_tz_offset,

        %DateTimeWithTZOffset{naive_datetime: ndt, timezone_offset: tz_offset},
        bolt_version
  )


  @doc """
  Encode DURATION (represented by Duration) into Bolt binary format.
  Encoded in a structure.

  Signature: `0x45`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `Months` `Days` `Seconds` `Nanoseconds`

  ## Example

      iex(60)> d = %Bolt.Sips.Types.Duration{
      ...(60)>   years: 3,
      ...(60)>   months: 1,
      ...(60)>   weeks: 7,
      ...(60)>   days: 4,
      ...(60)>   hours: 13,
      ...(60)>   minutes: 2,
      ...(60)>   seconds: 21,
      ...(60)>   nanoseconds: 554
      ...(60)> }
      %Bolt.Sips.Types.Duration{
        days: 4,
        hours: 13,
        minutes: 2,
        months: 1,
        nanoseconds: 554,
        seconds: 21,
        weeks: 7,
        years: 3
      }
      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_duration(d, 2))
      <<0xB4, 0x45, 0x25, 0x35, 0xCA, 0x0, 0x0, 0xB7, 0x5D, 0xC9, 0x2, 0x2A>>
  """
  @spec encode_duration(Duration.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_duration(%Duration{} = duration, bolt_version), do: EncoderHelper.call_encode(:duration, duration, bolt_version)


  @doc """
  Encode POINT 2D & 3D (represented by Point) into Bolt binary format.
  Encoded in a structure.


  ## Point 2D
  Signature: `0x58`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `SRID` `x_or_longitude` `y_or_latitude`

  ## Example

      iex> p = Bolt.Sips.Types.Point.create(:wgs_84, 65.43, 12.54)
      %Bolt.Sips.Types.Point{
              crs: "wgs-84",
              height: nil,
              latitude: 12.54,
              longitude: 65.43,
              srid: 4326,
              x: 65.43,
              y: 12.54,
              z: nil
            }
      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_point(p, 2))
      <<0xB3, 0x58, 0xC9, 0x10, 0xE6, 0xC1, 0x40, 0x50, 0x5B, 0x85, 0x1E, 0xB8, 0x51, 0xEC, 0xC1,
      0x40, 0x29, 0x14, 0x7A, 0xE1, 0x47, 0xAE, 0x14>>

  ## Point 3D
  Signature: `0x58`

  Encoding:
  `Marker` `Size` `Signature` ` Content`

  where `Content` is:
  `SRID` `x_or_longitude` `y_or_latitude` `z_or_height`

  ## Example

      iex> p = Bolt.Sips.Types.Point.create(:wgs_84, 45.0003, 40.3245, 23.1)
      %Bolt.Sips.Types.Point{
              crs: "wgs-84-3d",
              height: 23.1,
              latitude: 40.3245,
              longitude: 45.0003,
              srid: 4979,
              x: 45.0003,
              y: 40.3245,
              z: 23.1
            }
      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_point(p, 2))
      <<0xB4, 0x59, 0xC9, 0x13, 0x73, 0xC1, 0x40, 0x46, 0x80, 0x9, 0xD4, 0x95, 0x18, 0x2B, 0xC1,
      0x40, 0x44, 0x29, 0x89, 0x37, 0x4B, 0xC6, 0xA8, 0xC1, 0x40, 0x37, 0x19, 0x99, 0x99, 0x99,
      0x99, 0x9A>>

  """
  @spec encode_point(Point.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()
  def encode_point(%Point{z: nil} = point, bolt_version), do: EncoderHelper.call_encode(:point, point, bolt_version) 

  def encode_point(%Point{} = point, bolt_version), do: EncoderHelper.call_encode(:point, point, bolt_version)
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/encoder_v3.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.EncoderV3 do
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/error.ex
================================================
defmodule Bolt.Sips.Internals.PackStreamError do
  @moduledoc false

  # Represents an error when encoding data for the Bolt protocol.

  defexception data_type: nil, data: nil, message: nil, bolt_version: nil

  @typedoc """
  Send back the `item` that cannot be encoded with a `message` explaining the  reason why it
  can't be successfully encoded.
  """
  @type t :: %__MODULE__{
          data_type: atom(),
          data: any(),
          message: String.t(),
          bolt_version: integer()
        }

  def message(%{data_type: nil, data: data, message: message, bolt_version: bolt_version}) do
    "#{message} [bolt_version: #{inspect(bolt_version)}, data: #{inspect(data)}]"
  end

  def message(%{data_type: data_type, data: data, message: message, bolt_version: bolt_version}) do
    "#{message} [bolt_version: #{inspect(bolt_version)}, data_type: #{data_type}, data: #{
      inspect(data)
    }]"
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/markers.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Markers do
  @moduledoc false
  defmacro __using__(_opts) do
    quote do
      # Null
      @null_marker 0xC0

      # Boolean
      @true_marker 0xC3
      @false_marker 0xC2

      # String
      @tiny_bitstring_marker 0x8
      @bitstring8_marker 0xD0
      @bitstring16_marker 0xD1
      @bitstring32_marker 0xD2

      # Integer
      @int8_marker 0xC8
      @int16_marker 0xC9
      @int32_marker 0xCA
      @int64_marker 0xCB

      # Float
      @float_marker 0xC1

      # List
      @tiny_list_marker 0x9
      @list8_marker 0xD4
      @list16_marker 0xD5
      @list32_marker 0xD6

      # Map
      @tiny_map_marker 0xA
      @map8_marker 0xD8
      @map16_marker 0xD9
      @map32_marker 0xDA

      # Structure
      @tiny_struct_marker 0xB
      @struct8_marker 0xDC
      @struct16_marker 0xDD

      # Node
      @node_marker 0x4E

      # Relationship
      @relationship_marker 0x52

      # Unbounded relationship
      @unbounded_relationship_marker 0x72

      # Path
      @path_marker 0x50

      # Local Time
      @local_time_signature 0x74
      @local_time_struct_size 1

      # Time With TZ Offset
      @time_with_tz_signature 0x54
      @time_with_tz_struct_size 2

      # Date
      @date_signature 0x44
      @date_struct_size 1

      # Local DateTime
      @local_datetime_signature 0x64
      @local_datetime_struct_size 2

      # Datetime with TZ offset
      @datetime_with_zone_offset_signature 0x46
      @datetime_with_zone_offset_struct_size 3

      # Datetime with TZ id
      @datetime_with_zone_id_signature 0x66
      @datetime_with_zone_id_struct_size 3

      # Duration
      @duration_signature 0x45
      @duration_struct_size 4

      # Point 2D
      @point2d_signature 0x58
      @point2d_struct_size 3

      # Point 3D
      @point3d_signature 0x59
      @point3d_struct_size 4
    end
  end
end

defmodule Bolt.Sips.Internals.PackStream.MarkersHelper do
  @moduledoc false
  use Bolt.Sips.Internals.PackStream.Markers

  @doc """
  Return the list of valid signatures (for data encoding).
  """
  @spec valid_signatures() :: [integer()]
  def valid_signatures() do
    [
      @local_time_signature,
      @time_with_tz_signature,
      @date_signature,
      @local_datetime_signature,
      @datetime_with_zone_offset_signature,
      @datetime_with_zone_id_signature,
      @duration_signature,
      @point2d_signature,
      @point3d_signature
    ]
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/message/decoder.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Message.Decoder do
  @moduledoc false

  @tiny_struct_marker 0xB

  @success_signature 0x70
  @failure_signature 0x7F
  @record_signature 0x71
  @ignored_signature 0x7E

  # Decode SUCCESS message
  @spec decode(Bolt.Sips.Internals.PackStream.Message.encoded(), integer()) ::
          Bolt.Sips.Internals.PackStream.Message.decoded()
  def decode(
        <<@tiny_struct_marker::4, nb_entries::4, @success_signature, data::binary>>,
        bolt_version
      ) do
    build_response(:success, data, nb_entries, bolt_version)
  end

  # Decode FAILURE message
  def decode(
        <<@tiny_struct_marker::4, nb_entries::4, @failure_signature, data::binary>>,
        bolt_version
      ) do
    build_response(:failure, data, nb_entries, bolt_version)
  end

  # Decode RECORD message
  def decode(
        <<@tiny_struct_marker::4, nb_entries::4, @record_signature, data::binary>>,
        bolt_version
      ) do
    build_response(:record, data, nb_entries, bolt_version)
  end

  # Decode IGNORED message
  def decode(
        <<@tiny_struct_marker::4, nb_entries::4, @ignored_signature, data::binary>>,
        bolt_version
      ) do
    build_response(:ignored, data, nb_entries, bolt_version)
  end

  @spec build_response(
          Bolt.Sips.Internals.PackStream.Message.in_signature(),
          any(),
          integer(),
          integer()
        ) ::
          Bolt.Sips.Internals.PackStream.Message.decoded()
  defp build_response(message_type, data, nb_entries, bolt_version) do
    Bolt.Sips.Internals.Logger.log_message(:server, message_type, data, :hex)

    response =
      case Bolt.Sips.Internals.PackStream.decode(data, bolt_version) do
        response when nb_entries == 1 ->
          List.first(response)

        responses ->
          responses
      end

    Bolt.Sips.Internals.Logger.log_message(:server, message_type, response)
    {message_type, response}
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/message/encoder.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Message.Encoder do
  @moduledoc false
  _module_doc = """
  Manages the message encoding.

  A mesage is a tuple formated as:
  `{message_type, data}`
  with:
  - message_type: atom amongst the valid message type (:init, :discard_all, :pull_all,
  :ack_failure, :reset, :run)
  - data: a list of data to be used by the message

  Messages are passed in one or more chunk. The structure of a chunk is as follow: `chunk_size` `data`
  with `chunk_size` beign a 16-bit integer.
  A message always ends with the end marker `0x00 0x00`.
  Thus the possible typologies of messages are:
  - One-chunk message:
  `chunk_size` `message_data` `end_marker`
  - multiple-chunk message:
  `chunk_1_size` `message_data` `chunk_n_size` `message_data`...`end_marker`
  More documentation on message transfer encoding:
  [https://boltprotocol.org/v1/#message_transfer_encoding](https://boltprotocol.org/v1/#message_transfer_encoding)

  All messages are serialized structures. See `Bolt.Sips.Internals.PackStream.EncoderV1` for
  more information about structure encoding).

  An extensive documentation on messages can be found here:
  [https://boltprotocol.org/v1/#messages](https://boltprotocol.org/v1/#messages)
  """

  alias Bolt.Sips.Metadata

  @max_chunk_size 65_535
  @end_marker <<0x00, 0x00>>

  @ack_failure_signature 0x0E
  @begin_signature 0x11
  @commit_signature 0x12
  @discard_all_signature 0x2F
  @goodbye_signature 0x02
  @hello_signature 0x01
  @init_signature 0x01
  @pull_all_signature 0x3F
  @reset_signature 0x0F
  @rollback_signature 0x13
  @run_signature 0x10

  # OUT Signature

  # TODO improve using macros?
  @valid_signatures [
    @ack_failure_signature,
    @begin_signature,
    @commit_signature,
    @discard_all_signature,
    @goodbye_signature,
    @hello_signature,
    @pull_all_signature,
    @reset_signature,
    @rollback_signature,
    @run_signature
  ]

  @valid_v1_signatures [
    @ack_failure_signature,
    @discard_all_signature,
    @init_signature,
    @pull_all_signature,
    @reset_signature,
    @run_signature
  ]

  @valid_message_types [
    :ack_failure,
    :begin,
    :commit,
    :discard_all,
    :goodbye,
    :hello,
    :rollback,
    :pull_all,
    :reset,
    :run
  ]

  @valid_v1_message_types [
    :ack_failure,
    :discard_all,
    :init,
    :pull_all,
    :reset,
    :run
  ]

  @last_bolt_version 3

  @spec signature(Bolt.Sips.Internals.PackStream.Message.out_signature()) :: integer()
  defp signature(:ack_failure), do: @ack_failure_signature
  defp signature(:discard_all), do: @discard_all_signature
  defp signature(:pull_all), do: @pull_all_signature
  defp signature(:reset), do: @reset_signature
  defp signature(:begin), do: @begin_signature
  defp signature(:commit), do: @commit_signature
  defp signature(:goodbye), do: @goodbye_signature
  defp signature(:hello), do: @hello_signature
  defp signature(:rollback), do: @rollback_signature
  defp signature(:run), do: @run_signature
  defp signature(:init), do: @init_signature

  @doc """
  Return client name (based on bolt_sips version)
  """
  def client_name() do
    "BoltSips/" <> to_string(Application.spec(:bolt_sips, :vsn))
  end

  @doc """
  Return the valid message signatures depending on the Bolt version
  """
  @spec valid_signatures(integer()) :: [integer()]
  def valid_signatures(bolt_version) when bolt_version <= 2 do
    @valid_v1_signatures
  end

  def valid_signatures(3) do
    @valid_signatures
  end

  # Encode messages for bolt version 3

  # Encode HELLO message without auth token
  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::
          Bolt.Sips.Internals.PackStream.Message.encoded()
          | {:error, :not_implemented}
          | {:error, :invalid_message}
  def encode({:hello, []}, 3) do
    encode({:hello, [{}]}, 3)
  end

  # Encode INIT message with a valid auth token.
  # The auth token is tuple formated as: {user, password}
  def encode({:hello, [auth]}, 3) do
    do_encode(:hello, [auth_params(auth)], 3)
  end

  # Encode BEGIN message without metadata.

  # BEGIN is used to open a transaction.
  def encode({:begin, []}, 3) do
    encode({:begin, [%{}]}, 3)
  end

  # Encode BEGIN message with metadata
  def encode({:begin, [%Metadata{} = metadata]}, 3) do
    do_encode(:begin, [Metadata.to_map(metadata)], 3)
  end

  def encode({:begin, [%{} = map]}, 3) when map_size(map) == 0 do
    {:ok, metadata} = Metadata.new(%{})
    encode({:begin, [metadata]}, 3)
  end

  def encode({:begin, _}, _) do
    {:error, :invalid_data}
  end

  # Encode RUN without params nor metadata
  def encode({:run, [statement]}, 3) do
    do_encode(:run, [statement, %{}, %{}], 3)
  end

  # Encode RUN message with its data: statement and parameters
  def encode({:run, [statement]}, bolt_version) when bolt_version <= 2 do
    do_encode(:run, [statement, %{}], bolt_version)
  end

  # Encode RUN with params but without metadata
  def encode({:run, [statement, params]}, 3) do
    do_encode(:run, [statement, params, %{}], 3)
  end

  # Encode RUN with params and metadata
  def encode({:run, [statement, params, %Metadata{} = metadata]}, 3) do
    do_encode(:run, [statement, params, Metadata.to_map(metadata)], 3)
  end

  # INIT is no more a valid message in Bolt V3
  def encode({:init, _}, 3) do
    {:error, :invalid_message}
  end

  # Encode INIT message without auth token
  def encode({:init, []}, bolt_version) when bolt_version <= 2 do
    encode({:init, [{}]}, bolt_version)
  end

  # Encode INIT message with a valid auth token.
  # The auth token is tuple formated as: {user, password}
  def encode({:init, [auth]}, bolt_version) when bolt_version <= 2 do
    do_encode(:init, [client_name(), auth_params_v1(auth)], bolt_version)
  end

  # Encode messages that don't need any data formating
  def encode({message_type, data}, 3) when message_type in @valid_message_types do
    do_encode(message_type, data, 3)
  end

  # Encode messages that don't need any data formating
  def encode({message_type, data}, bolt_version)
      when bolt_version <= 2 and message_type in @valid_v1_message_types do
    do_encode(message_type, data, bolt_version)
  end

  @doc """
  Encode Bolt V3 messages

  Not that INIT is not valid in bolt V3, it is replaced by HELLO

  ## HELLO
  Usage: intialize the session.

  Signature: `0x01` (Same as INIT in previous bolt version)

  Struct: `auth_parms`

  with:

  | data | type |
  |-----|-----|
  |auth_token | map: {scheme: string, principal: string, credentials: string, user_agent: string}|

  Note: `user_agent` is equivalent to `client_name` in bolt previous version.

  Examples (excluded from doctest because client_name changes at each bolt_sips version)

      # without auth token
      diex> :erlang.iolist_to_binary(Encoder.encode({:hello, []}, 3))
      <<0x0, 0x1D, 0xB1, 0x1, 0xA1, 0x8A, 0x75, 0x73, 0x65, 0x72, 0x5F, 0x61, 0x67, 0x65, 0x6E,
      0x74, 0x8E, 0x42, 0x6F, 0x6C, 0x74, 0x53, 0x69, 0x70, 0x73, 0x2F, 0x31, 0x2E, 0x34, 0x2E,
      0x30, 0x0, 0x0>>

      # with auth token
      diex(20)> :erlang.iolist_to_binary(Encoder.encode({:hello, [{"neo4j", "test"}]}, 3))
      <<0x0, 0x4B, 0xB1, 0x1, 0xA4, 0x8B, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x61,
      0x6C, 0x73, 0x84, 0x74, 0x65, 0x73, 0x74, 0x89, 0x70, 0x72, 0x69, 0x6E, 0x63, 0x69, 0x70,
      0x61, 0x6C, 0x85, 0x6E, 0x65, 0x6F, 0x34, 0x6A, 0x86, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x65,
      0x85, 0x62, 0x61, 0x73, 0x69, ...>>

  ## GOODBYE
  Usage: close the connection with the server

  Signature: `0x02`

  Struct: no data

  Example

      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:goodbye, []}, 3))
      <<0x0, 0x2, 0xB0, 0x2, 0x0, 0x0>>

  ## BEGIN
  Usage: Open a transaction

  Signature: `0x11`

  Struct: `metadata`

  with:

  | data | type |
  |------|------|
  | metadata | See Bolt.Sips.Metadata

  Example

      # without metadata
      # iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      # iex> :erlang.iolist_to_binary(Encoder.encode({:begin, []}, 3))
      # <<0x0, 0x3, 0xB1, 0x11, 0xA0, 0x0, 0x0>>

      # # with metadata
      # iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      # iex> alias Bolt.Sips.Metadata
      # iex> {:ok, metadata} = Metadata.new(%{tx_timeout: 5000})
      # {:ok,
      # %Bolt.Sips.Metadata{
      #   bookmarks: nil,
      #   metadata: nil,
      #   tx_timeout: 5000
      # }}
      # iex> :erlang.iolist_to_binary(Encoder.encode({:begin, [metadata]}, 3))
      # <<0x0, 0x11, 0xB1, 0x11, 0xA1, 0x8A, 0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75,
      # 0x74, 0xC9, 0x13, 0x88, 0x0, 0x0>>

  ## COMMIT
  Usage: commit the currently open transaction

  Signature: `0x12`

  Struct: no data

  Example

      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:commit, []}, 3))
      <<0x0, 0x2, 0xB0, 0x12, 0x0, 0x0>>

  ## ROLLBACK
  Usage: rollback the currently open transaction

  Signature: `0x13`

  Struct: no data

  Example

      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:rollback, []}, 3))
      <<0x0, 0x2, 0xB0, 0x13, 0x0, 0x0>>

  ## RUN
  Usage: pass statement for execution to the server. Same as in bolt previous version.
  The only difference: `metadata` are passed as well since bolt v3.

  Signature: `0x10`

  Struct: `statement` `parameters` `metadata`

  with:

  | data | type |
  |-----|-----|
  | statement | string |
  | parameters | map |
  | metadata | See Bolt.Sips.Metadata

  Example

      # without params nor metadata
      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:run, ["RETURN 'hello' AS str"]}, 3))
      <<0x0, 0x1B, 0xB3, 0x10, 0xD0, 0x15, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x27, 0x68,
      0x65, 0x6C, 0x6C, 0x6F, 0x27, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA0, 0xA0, 0x0,
      0x0>>

      # without params but with metadata
      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> alias Bolt.Sips.Metadata
      iex> {:ok, metadata} = Metadata.new(%{tx_timeout: 4500})
      {:ok,
      %Bolt.Sips.Metadata{
        bookmarks: nil,
        metadata: nil,
        tx_timeout: 4500
      }}
      iex> :erlang.iolist_to_binary(Encoder.encode({:run, ["RETURN 'hello' AS str", %{}, metadata]}, 3))
      <<0x0, 0x29, 0xB3, 0x10, 0xD0, 0x15, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x27, 0x68,
      0x65, 0x6C, 0x6C, 0x6F, 0x27, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA0, 0xA1, 0x8A,
      0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0xC9, 0x11, 0x94, 0x0, 0x0>>

      # with params but without metadata
      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:run, ["RETURN $str AS str", %{str: "hello"}]}, 3))
      <<0x0, 0x22, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,
      0x24, 0x73, 0x74, 0x72, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA1, 0x83,
      0x73, 0x74, 0x72, 0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0xA0, 0x0, 0x0>>

      # with params and metadata
      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> alias Bolt.Sips.Metadata
      iex> {:ok, metadata} = Metadata.new(%{tx_timeout: 4500})
      {:ok,
      %Bolt.Sips.Metadata{
        bookmarks: nil,
        metadata: nil,
        tx_timeout: 4500
      }}
      iex> :erlang.iolist_to_binary(Encoder.encode({:run, ["RETURN $str AS str", %{str: "hello"}, metadata]}, 3))
      <<0x0, 0x30, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,
      0x24, 0x73, 0x74, 0x72, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA1, 0x83,
      0x73, 0x74, 0x72, 0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0xA1, 0x8A, 0x74, 0x78,
      0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0xC9, 0x11, 0x94, 0x0, 0x0>>

   #   Encode  messages v1

  # Supported messages

  ## INIT
  Usage: intialize the session.

  Signature: `0x01`

  Struct: `client_name` `auth_token`

  with:

  | data | type |
  |-----|-----|
  |client_name | string|
  |auth_token | map: {scheme: string, principal: string, credentials: string}|

  Examples (excluded from doctest because client_name changes at each bolt_sips version)

      # without auth token
      diex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      :erlang.iolist_to_binary(Encoder.encode({:init, []}, 1))
      <<0x0, 0x10, 0xB2, 0x1, 0x8C, 0x42, 0x6F, 0x6C, 0x74, 0x65, 0x78, 0x2F, 0x30, 0x2E, 0x34,
      0x2E, 0x30, 0xA0, 0x0, 0x0>>

      # with auth token
      # The auth token is tuple formated as: {user, password}
      diex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      diex> :erlang.iolist_to_binary(Encoder.encode({:init, [{"neo4j", "password"}]}))
      <<0x0, 0x42, 0xB2, 0x1, 0x8C, 0x42, 0x6F, 0x6C, 0x74, 0x65, 0x78, 0x2F, 0x30, 0x2E, 0x34,
      0x2E, 0x30, 0xA3, 0x8B, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x61, 0x6C, 0x73,
      0x88, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x89, 0x70, 0x72, 0x69, 0x6E, 0x63,
      0x69, 0x70, 0x61, 0x6C, 0x85, ...>>


  ## RUN
  Usage: pass statement for execution to the server.

  Signature: `0x10`

  Struct: `statement` `parameters`

  with:

  | data | type |
  |-----|-----|
  | statement | string |
  | parameters | map |

  Examples
      # without parameters
      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:run, ["RETURN 1 AS num"]}, 1))
      <<0x0, 0x13, 0xB2, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x31, 0x20, 0x41,
      0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0x0, 0x0>>
      # with parameters
      iex> :erlang.iolist_to_binary(Encoder.encode({:run, ["RETURN $num AS num", %{num: 1}]}, 1))
      <<0x0, 0x1C, 0xB2, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,
      0x24, 0x6E, 0x75, 0x6D, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA1, 0x83,
      0x6E, 0x75, 0x6D, 0x1, 0x0, 0x0>>

  ## ACK_FAILURE
  Usage: Acknowledge a failure the server has sent.

  Signature: `0x0E`

  Struct: no data

  Example

      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:ack_failure, []}, 1))
      <<0x0, 0x2, 0xB0, 0xE, 0x0, 0x0>>

  ## DISCARD_ALL
  Uage: Discard all remaining items from the active result stream.

  Signature: `0x2F`

  Struct: no data

  Example

      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:discard_all, []}, 1))
      <<0x0, 0x2, 0xB0, 0x2F, 0x0, 0x0>>

  ## PULL_ALL
  Usage: Retrieve all remaining items from the active result stream.

  Signature: `0x3F`

  Struct: no data

  Example

      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:pull_all, []}, 1))
      <<0x0, 0x2, 0xB0, 0x3F, 0x0, 0x0>>

  ## RESET
  Usage: Return the current session to a "clean" state.

  Signature: `0x0F`

  Struct: no data

  Example

      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder
      iex> :erlang.iolist_to_binary(Encoder.encode({:reset, []}, 1))
      <<0x0, 0x2, 0xB0, 0xF, 0x0, 0x0>>


  Check if the encoder for the given bolt version is capable of encoding the given message
  If it is the case, the encoding function will be called
  If not, fallback to previous bolt version

  If encoding function is not present in any of the bolt  version, an error will be raised
  """

  def encode(data, bolt_version)
      when is_integer(bolt_version) and bolt_version > @last_bolt_version do
    encode(data, @last_bolt_version)
  end

  def encode(_data, _bolt_version) do
    {:error, :not_implemented}
  end

  defp do_encode(message_type, data, bolt_version) do
    signature = signature(message_type)
    encode_message(message_type, signature, data, bolt_version)
  end

  # Format the auth params for v1 to v2
  @spec auth_params_v1({} | {String.t(), String.t()}) :: map()
  defp auth_params_v1({}), do: %{}

  defp auth_params_v1({username, password}) do
    %{
      scheme: "basic",
      principal: username,
      credentials: password
    }
  end

  # Format the auth params
  @spec auth_params({} | {String.t(), String.t()}) :: map()
  defp auth_params({}), do: user_agent()

  defp auth_params({username, password}) do
    %{
      scheme: "basic",
      principal: username,
      credentials: password
    }
    |> Map.merge(user_agent())
  end

  defp user_agent() do
    %{user_agent: client_name()}
  end

  @doc """
  Perform the final message:
  - add header
  - manage chunk if necessary
  - add end marker
  """
  @spec encode_message(
          Bolt.Sips.Internals.PackStream.Message.out_signature(),
          integer(),
          list(),
          integer()
        ) ::
          [[Bolt.Sips.Internals.PackStream.Message.encoded()]]

  def encode_message(message_type, signature, data, bolt_version) do
    Bolt.Sips.Internals.Logger.log_message(:client, message_type, data)

    encoded =
      {signature, data}
      |> Bolt.Sips.Internals.PackStream.encode(bolt_version)
      |> generate_chunks([])

    Bolt.Sips.Internals.Logger.log_message(:client, message_type, encoded, :hex)
    encoded
  end

  @spec generate_chunks(Bolt.Sips.Internals.PackStream.value() | <<>>, list()) ::
          [[Bolt.Sips.Internals.PackStream.Message.encoded()]]
  defp generate_chunks(<<>>, chunks) do
    [chunks, [@end_marker], []]
  end

  defp generate_chunks(data, chunks) do
    data_size = :erlang.iolist_size(data)

    case data_size > @max_chunk_size do
      true ->
        bindata = :erlang.iolist_to_binary(data)
        <<chunk::binary-@max_chunk_size, rest::binary>> = bindata
        new_chunk = format_chunk(chunk)
        # [new_chunk, generate_chunks(rest,[])]
        generate_chunks(rest, [chunks, new_chunk])

      # generate_chunks(<<rest>>, [new_chunk, chunks])

      _ ->
        generate_chunks(<<>>, [chunks, format_chunk(data)])
    end
  end

  @spec format_chunk(Bolt.Sips.Internals.PackStream.value()) ::
          [Bolt.Sips.Internals.PackStream.Message.encoded()]
  defp format_chunk(chunk) do
    [<<:erlang.iolist_size(chunk)::16>>, chunk]
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/message/encoder_v1.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV1 do
  @moduledoc false
  use Bolt.Sips.Internals.PackStream.Message.Signatures
  alias Bolt.Sips.Internals.PackStream.Message.Encoder


  @doc """
  Encode INIT message without auth token
  """
  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::
          Bolt.Sips.Internals.PackStream.Message.encoded() | {:error, :not_implemented}
  def encode(data, bolt_version) do
    Encoder.encode(data, bolt_version)
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/message/encoder_v2.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV2 do
  def encode(_, _) do
    {:error, :not_implemented}
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/message/encoder_v3.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV3 do
  @moduledoc false
  use Bolt.Sips.Internals.PackStream.Message.Signatures
  alias Bolt.Sips.Internals.PackStream.Message.Encoder

  @valid_signatures [
    @begin_signature,
    @commit_signature,
    @discard_all_signature,
    @goodbye_signature,
    @hello_signature,
    @pull_all_signature,
    @reset_signature,
    @rollback_signature,
    @run_signature
  ]


  @doc """
  Return the valid signatures for bolt V1
  """
  @spec valid_signatures() :: [integer()]
  def valid_signatures() do
    @valid_signatures
  end


  @doc """
  Encode HELLO message without auth token
  """
  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::
          Bolt.Sips.Internals.PackStream.Message.encoded()
          | {:error, :not_implemented}
          | {:error, :invalid_message}
  def encode(data, bolt_version) do
    Encoder.encode(data, bolt_version)
  end

end


================================================
FILE: lib/bolt_sips/internals/pack_stream/message/signatures.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Message.Signatures do
  @moduledoc false
  defmacro __using__(_opts) do
    quote do
      # Message OUT
      @ack_failure_signature 0x0E
      @begin_signature 0x11
      @commit_signature 0x12
      @discard_all_signature 0x2F
      @goodbye_signature 0x02
      @hello_signature 0x01
      @init_signature 0x01
      @pull_all_signature 0x3F
      @reset_signature 0x0F
      @rollback_signature 0x13
      @run_signature 0x10

      # Message IN
      @success_signature 0x70
      @failure_signature 0x7F
      @record_signature 0x71
      @ignored_signature 0x7E
    end
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/message.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Message do
  @moduledoc false

  # Manage the message encoding and decoding.
  #
  # Message encoding / decoding is the first step of encoding / decoding.
  # The next step is the message data encoding /decoding (which is handled by packstream.ex)

  alias Bolt.Sips.Internals.PackStream.Message.Encoder
  alias Bolt.Sips.Internals.PackStream.Message.Decoder

  @type in_signature :: :failure | :ignored | :record | :success
  @type out_signature ::
          :ack_failure
          | :begin
          | :commit
          | :discard_all
          | :goodbye
          | :hello
          | :init
          | :pull_all
          | :reset
          | :rollback
          | :run
  @type raw :: {out_signature, list()}
  @type decoded :: {in_signature(), any()}
  @type encoded :: <<_::16, _::_*8>>

  @doc """
  Encode a message depending on its type
  """
  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::
          Bolt.Sips.Internals.PackStream.Message.encoded()
  def encode(message, bolt_version) do
    Encoder.encode(message, bolt_version)
  end

  @doc """
  Decode a message
  """
  @spec decode(Bolt.Sips.Internals.PackStream.Message.encoded(), integer()) ::
          Bolt.Sips.Internals.PackStream.Message.decoded()
  def decode(message, bolt_version) do
    Decoder.decode(message, bolt_version)
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/utils.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.Utils do
  alias Bolt.Sips.Internals.PackStream.Encoder
  alias Bolt.Sips.Types.Duration
  alias Bolt.Sips.Internals.PackStreamError

  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)

      # catch all clause for encoding implementation
      defp do_call_encode(data_type, data, original_version) do
        raise PackStreamError,
          data_type: data_type,
          data: data,
          bolt_version: original_version,
          message: "Encoding function not implemented for"
      end

      @spec encode_list_data(list(), integer()) :: [any()]
      defp encode_list_data(data, bolt_version) do
        Enum.map(
          data,
          &Encoder.encode(&1, bolt_version)
        )
      end

      @spec encode_kv(map(), integer()) :: binary()
      defp encode_kv(map, bolt_version) do
        Enum.reduce(map, <<>>, fn data, acc -> [acc, do_reduce_kv(data, bolt_version)] end)
      end

      @spec do_reduce_kv({atom(), any()}, integer()) :: [binary()]
      defp do_reduce_kv({key, value}, bolt_version) do
        [
          Encoder.encode(
            key,
            bolt_version
          ),
          Encoder.encode(value, bolt_version)
        ]
      end

      @spec day_time(Time.t()) :: integer()
      defp day_time(time) do
        Time.diff(time, ~T[00:00:00.000], :nanosecond)
      end

      @spec decompose_datetime(Calendar.naive_datetime()) :: [integer()]
      defp decompose_datetime(%NaiveDateTime{} = datetime) do
        datetime_micros = NaiveDateTime.diff(datetime, ~N[1970-01-01 00:00:00.000], :microsecond)

        seconds = div(datetime_micros, 1_000_000)
        nanoseconds = rem(datetime_micros, 1_000_000) * 1_000

        [seconds, nanoseconds]
      end

      @spec compact_duration(Duration.t()) :: [integer()]
      defp compact_duration(%Duration{} = duration) do
        months = 12 * duration.years + duration.months
        days = 7 * duration.weeks + duration.days
        seconds = 3600 * duration.hours + 60 * duration.minutes + duration.seconds

        [months, days, seconds, duration.nanoseconds]
      end
    end
  end
end


================================================
FILE: lib/bolt_sips/internals/pack_stream/v1.ex
================================================
defmodule Bolt.Sips.Internals.PackStream.V1 do
  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)

      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()

      @int8 -128..-17
      @int16_low -32_768..-129
      @int16_high 128..32_767
      @int32_low -2_147_483_648..-32_769
      @int32_high 32_768..2_147_483_647
      @int64_low -9_223_372_036_854_775_808..-2_147_483_649
      @int64_high 2_147_483_648..9_223_372_036_854_775_807
      # Null
      @null_marker 0xC0

      # Boolean
      @true_marker 0xC3
      @false_marker 0xC2

      # String
      @tiny_bitstring_marker 0x8
      @bitstring8_marker 0xD0
      @bitstring16_marker 0xD1
      @bitstring32_marker 0xD2

      # Integer
      @int8_marker 0xC8
      @int16_marker 0xC9
      @int32_marker 0xCA
      @int64_marker 0xCB

      # Float
      @float_marker 0xC1

      # List
      @tiny_list_marker 0x9
      @list8_marker 0xD4
      @list16_marker 0xD5
   
Download .txt
gitextract_6ctcdb7g/

├── .credo.exs
├── .dialyzer_ignore.exs
├── .formatter.exs
├── .gitattributes
├── .gitignore
├── .iex.exs
├── .markdownlint.json
├── .prettierrc.yaml
├── .tool-versions
├── .travis.yml
├── CHANGELOG.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.md
├── benchees/
│   └── conn_to_local_bench.exs
├── config/
│   ├── config.exs
│   ├── dev.exs
│   └── test.exs
├── docker-compose.yml
├── docs/
│   ├── examples/
│   │   └── readme.md
│   ├── features/
│   │   ├── about-encoding.md
│   │   ├── about-transactions.md
│   │   ├── configuration.md
│   │   ├── multi-tenancy.md
│   │   ├── role-based-connections.md
│   │   ├── routing.md
│   │   ├── using-cypher.md
│   │   ├── using-temporal-and-spatial-types.md
│   │   └── using-with-phoenix.md
│   └── getting-started.md
├── lib/
│   ├── bolt_sips/
│   │   ├── application.ex
│   │   ├── enumerable_response.ex
│   │   ├── error.ex
│   │   ├── exception.ex
│   │   ├── internals/
│   │   │   ├── bolt_protocol.ex
│   │   │   ├── bolt_protocol_helper.ex
│   │   │   ├── bolt_protocol_v1.ex
│   │   │   ├── bolt_protocol_v2.ex
│   │   │   ├── bolt_protocol_v3.ex
│   │   │   ├── bolt_version_helper.ex
│   │   │   ├── error.ex
│   │   │   ├── logger.ex
│   │   │   ├── pack_stream/
│   │   │   │   ├── decoder.ex
│   │   │   │   ├── decoder_impl_v1.ex
│   │   │   │   ├── decoder_impl_v2.ex
│   │   │   │   ├── decoder_utils.ex
│   │   │   │   ├── decoder_v1.ex
│   │   │   │   ├── decoder_v2.ex
│   │   │   │   ├── decoder_v3.ex
│   │   │   │   ├── encoder.ex
│   │   │   │   ├── encoder_helper.ex
│   │   │   │   ├── encoder_v1.ex
│   │   │   │   ├── encoder_v2.ex
│   │   │   │   ├── encoder_v3.ex
│   │   │   │   ├── error.ex
│   │   │   │   ├── markers.ex
│   │   │   │   ├── message/
│   │   │   │   │   ├── decoder.ex
│   │   │   │   │   ├── encoder.ex
│   │   │   │   │   ├── encoder_v1.ex
│   │   │   │   │   ├── encoder_v2.ex
│   │   │   │   │   ├── encoder_v3.ex
│   │   │   │   │   └── signatures.ex
│   │   │   │   ├── message.ex
│   │   │   │   ├── utils.ex
│   │   │   │   ├── v1.ex
│   │   │   │   └── v2.ex
│   │   │   └── pack_stream.ex
│   │   ├── metadata.ex
│   │   ├── protocol.ex
│   │   ├── query.ex
│   │   ├── query_statement.ex
│   │   ├── response.ex
│   │   ├── response_encoder/
│   │   │   ├── json/
│   │   │   │   ├── jason.ex
│   │   │   │   └── poison.ex
│   │   │   └── json.ex
│   │   ├── response_encoder.ex
│   │   ├── router.ex
│   │   ├── routing/
│   │   │   ├── connection_supervisor.ex
│   │   │   ├── load_balancer.ex
│   │   │   └── routing_table.ex
│   │   ├── socket.ex
│   │   ├── types.ex
│   │   ├── types_helper.ex
│   │   └── utils.ex
│   ├── bolt_sips.ex
│   └── mix/
│       └── tasks/
│           └── cypher.ex
├── mix.exs
├── requirements.txt
└── test/
    ├── bolt_sips/
    │   ├── internals/
    │   │   ├── bolt_protocol_all_bolt_version_test.exs
    │   │   ├── bolt_protocol_bolt_v1_test.exs
    │   │   ├── bolt_protocol_bolt_v2_test.exs
    │   │   ├── bolt_protocol_bolt_v3_test.exs
    │   │   ├── bolt_protocol_v1_test.exs
    │   │   ├── bolt_protocol_v3_test.exs
    │   │   ├── bolt_version_helper_test.exs
    │   │   ├── logger_test.exs
    │   │   └── pack_stream/
    │   │       ├── decoder_test.exs
    │   │       ├── decoder_v1_test.exs
    │   │       ├── decoder_v2_test.exs
    │   │       ├── encoder_helper_test.exs
    │   │       ├── encoder_test.exs
    │   │       ├── encoder_v1_test.exs
    │   │       ├── encoder_v2_test.exs
    │   │       ├── message/
    │   │       │   ├── decoder_test.exs
    │   │       │   ├── encoder_test.exs
    │   │       │   ├── encoder_v1_test.exs
    │   │       │   └── encoder_v3_test.exs
    │   │       └── message_test.exs
    │   ├── metadata_test.exs
    │   ├── performance_test.exs
    │   ├── protocol_test.exs
    │   ├── response_encoder/
    │   │   ├── json_implementations_test.exs
    │   │   └── json_test.exs
    │   ├── response_encoder_test.exs
    │   ├── types_helpers_test.exs
    │   └── types_test.exs
    ├── boltkit_test.exs
    ├── config_test.exs
    ├── errors_test.exs
    ├── invalid_param_type_test.exs
    ├── one_test.exs
    ├── query_bolt_v2_test.exs
    ├── query_test.exs
    ├── response_test.exs
    ├── router_test.exs
    ├── routing/
    │   ├── connections_test.exs
    │   ├── crud_test.exs
    │   ├── routing_table_parser_test.exs
    │   ├── routing_test.exs
    │   └── transaction_test.exs
    ├── scripts/
    │   ├── count.bolt
    │   ├── create_a.script
    │   ├── forbidden_on_read_only_database.script
    │   ├── get_routing_table.script
    │   ├── get_routing_table_with_context.script
    │   ├── non_router.script
    │   ├── return_1.script
    │   ├── return_1_in_tx_twice.script
    │   ├── return_1_twice.script
    │   ├── return_x.bolt
    │   ├── router.script
    │   ├── router_no_readers.script
    │   └── router_no_writers.script
    ├── support/
    │   ├── boltkit_case.ex
    │   ├── conn_case.ex
    │   ├── conn_routing_case.ex
    │   ├── database.ex
    │   ├── fixture.ex
    │   └── internal_case.ex
    ├── test_helper.exs
    ├── test_large_param_set.exs
    ├── test_support.exs
    └── transaction_test.exs
Download .txt
SYMBOL INDEX (452 symbols across 104 files)

FILE: lib/bolt_sips.ex
  class Bolt.Sips (line 1) | defmodule Bolt.Sips
    method start_link (line 84) | def start_link(opts) do
    method init (line 99) | def init(opts) do
    method conn (line 113) | def conn(role \\ :direct, opts \\ [prefix: :default])
    method conn (line 115) | def conn(role, opts) do
    method info (line 241) | def info(), do: sanitized_info(Bolt.Sips.Router.info())
    method routing_table (line 247) | def routing_table(prefix \\ :default)
    method routing_table (line 249) | def routing_table(prefix) do
    method registry_name (line 257) | def registry_name(), do: @registry_name
    method sanitized_info (line 266) | defp sanitized_info(info), do: info

FILE: lib/bolt_sips/application.ex
  class Bolt.Sips.Application (line 1) | defmodule Bolt.Sips.Application
    method start (line 8) | def start(_, start_args) do
    method stop (line 12) | def stop(_state) do

FILE: lib/bolt_sips/error.ex
  class Bolt.Sips.Error (line 1) | defmodule Bolt.Sips.Error
    method new (line 10) | def new(%Bolt.Sips.Internals.Error{
    method new (line 27) | def new({:ignored, f} = _r), do: new({:error, f})
    method new (line 29) | def new({:failure, %{"code" => code, "message" => message}} = _r) do
    method new (line 33) | def new(r), do: r

FILE: lib/bolt_sips/exception.ex
  class Bolt.Sips.Exception (line 1) | defmodule Bolt.Sips.Exception

FILE: lib/bolt_sips/internals/bolt_protocol.ex
  class Bolt.Sips.Internals.BoltProtocol (line 1) | defmodule Bolt.Sips.Internals.BoltProtocol
    method run (line 115) | def run(
    method manage_metadata_and_options (line 150) | defp manage_metadata_and_options([], options) do
    method manage_metadata_and_options (line 155) | defp manage_metadata_and_options([_ | _] = metadata, options) do
    method manage_metadata_and_options (line 160) | defp manage_metadata_and_options(metadata, options) do
    method run_statement (line 182) | def run_statement(

FILE: lib/bolt_sips/internals/bolt_protocol_helper.ex
  class Bolt.Sips.Internals.BoltProtocolHelper (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolHelper
    method send_message (line 18) | def send_message(transport, port, bolt_version, message) do
    method receive_data (line 55) | def receive_data(transport, port, bolt_version, options \\ [], previou...
    method do_receive_data (line 80) | defp do_receive_data(transport, port, options) do
    method do_receive_data_ (line 93) | defp do_receive_data_(transport, port, chunk_size, options, old_data) do
    method get_recv_timeout (line 116) | def get_recv_timeout(options) do
    method treat_simple_message (line 135) | def treat_simple_message(message, transport, port, bolt_version, optio...

FILE: lib/bolt_sips/internals/bolt_protocol_v1.ex
  class Bolt.Sips.Internals.BoltProtocolV1 (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolV1
    method handshake (line 25) | def handshake(transport, port, options \\ [recv_timeout: 15_000]) do
    method init (line 81) | def init(transport, port, bolt_version, auth, options \\ [recv_timeout...
    method run (line 114) | def run(transport, port, bolt_version, statement, params, options) do
    method pull_all (line 157) | def pull_all(transport, port, bolt_version, options) do
    method run_statement (line 198) | def run_statement(transport, port, bolt_version, statement, params, op...
    method discard_all (line 229) | def discard_all(transport, port, bolt_version, options) do
    method ack_failure (line 249) | def ack_failure(transport, port, bolt_version, options) do
    method reset (line 269) | def reset(transport, port, bolt_version, options) do

FILE: lib/bolt_sips/internals/bolt_protocol_v2.ex
  class Bolt.Sips.Internals.BoltProtocolV2 (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolV2

FILE: lib/bolt_sips/internals/bolt_protocol_v3.ex
  class Bolt.Sips.Internals.BoltProtocolV3 (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolV3
    method hello (line 27) | def hello(transport, port, bolt_version, auth, options \\ [recv_timeou...
    method goodbye (line 58) | def goodbye(transport, port, bolt_version) do
    method run (line 86) | def run(transport, port, bolt_version, statement, params, metadata, op...
    method run_statement (line 142) | def run_statement(transport, port, bolt_version, statement, params, me...
    method begin (line 170) | def begin(transport, port, bolt_version, metadata, options) do
    method commit (line 199) | def commit(transport, port, bolt_version, options) do
    method rollback (line 227) | def rollback(transport, port, bolt_version, options) do

FILE: lib/bolt_sips/internals/bolt_version_helper.ex
  class Bolt.Sips.Internals.BoltVersionHelper (line 1) | defmodule Bolt.Sips.Internals.BoltVersionHelper
    method available_versions (line 11) | def available_versions(), do: @available_bolt_versions
    method previous (line 27) | def previous(version) do
    method last (line 41) | def last() do

FILE: lib/bolt_sips/internals/error.ex
  class Bolt.Sips.Internals.Error (line 1) | defmodule Bolt.Sips.Internals.Error
    method exception (line 16) | def exception(%{"message" => message, "code" => code}, pid, function) do
    method exception (line 26) | def exception({:error, :closed}, pid, function) do
    method exception (line 35) | def exception({:failure, %Bolt.Sips.Internals.Error{message: _message,...
    method exception (line 39) | def exception(%Bolt.Sips.Internals.Error{} = err, _pid, _function), do...
    method exception (line 41) | def exception(message, pid, function) do
    method message_for (line 51) | defp message_for(:handshake, "HTTP") do
    method message_for (line 67) | defp message_for(:handshake, other) do
    method message_for (line 74) | defp message_for(nil, message) do
    method message_for (line 80) | defp message_for(_function, {:error, error}) do
    method message_for (line 87) | defp message_for(_function, {:ignored, []}) do
    method message_for (line 94) | defp message_for(function, message) do
    method get_id (line 101) | defp get_id({:sslsocket, {:gen_tcp, port, _tls, _unused_yet}, _pid}) do
    method get_id (line 112) | defp get_id(_), do: nil

FILE: lib/bolt_sips/internals/logger.ex
  class Bolt.Sips.Internals.Logger (line 1) | defmodule Bolt.Sips.Internals.Logger
    method log_message (line 14) | def log_message(from, {type, data}) do
    method log_message (line 25) | def log_message(from, type, data) do
    method log_message (line 38) | def log_message(from, type, data, :hex) do

FILE: lib/bolt_sips/internals/pack_stream.ex
  class Bolt.Sips.Internals.PackStream (line 1) | defmodule Bolt.Sips.Internals.PackStream
    method encode (line 25) | def encode(item, bolt_version) do
    method decode (line 38) | def decode(data, bolt_version) do

FILE: lib/bolt_sips/internals/pack_stream/decoder.ex
  class Bolt.Sips.Internals.PackStream.Decoder (line 1) | defmodule Bolt.Sips.Internals.PackStream.Decoder

FILE: lib/bolt_sips/internals/pack_stream/decoder_impl_v1.ex
  class Bolt.Sips.Internals.PackStream.DecoderImplV1 (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderImplV1

FILE: lib/bolt_sips/internals/pack_stream/decoder_impl_v2.ex
  class Bolt.Sips.Internals.PackStream.DecoderImplV2 (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderImplV2

FILE: lib/bolt_sips/internals/pack_stream/decoder_utils.ex
  class Bolt.Sips.Internals.PackStream.DecoderUtils (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderUtils

FILE: lib/bolt_sips/internals/pack_stream/decoder_v1.ex
  class Bolt.Sips.Internals.PackStream.DecoderV1 (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderV1
    method decode (line 23) | def decode(data, bolt_version), do: Decoder.decode(data, bolt_version)

FILE: lib/bolt_sips/internals/pack_stream/decoder_v2.ex
  class Bolt.Sips.Internals.PackStream.DecoderV2 (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderV2
    method decode (line 29) | def decode(data, bolt_version), do: Decoder.decode(data, bolt_version)

FILE: lib/bolt_sips/internals/pack_stream/decoder_v3.ex
  class Bolt.Sips.Internals.PackStream.DecoderV3 (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderV3
    method decode (line 2) | def decode(_, _) do

FILE: lib/bolt_sips/internals/pack_stream/encoder_helper.ex
  class Bolt.Sips.Internals.PackStream.EncoderHelper (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderHelper
    method call_encode (line 36) | def call_encode(data_type, data, bolt_version) do

FILE: lib/bolt_sips/internals/pack_stream/encoder_v1.ex
  class Bolt.Sips.Internals.PackStream.EncoderV1 (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderV1
    method encode_atom (line 34) | def encode_atom(atom , bolt_version), do: EncoderHelper.call_encode(:a...
    method encode_string (line 58) | def encode_string(string, bolt_version), do: EncoderHelper.call_encode...
    method encode_integer (line 86) | def encode_integer(integer, bolt_version), do: EncoderHelper.call_enco...
    method encode_float (line 104) | def encode_float(number, bolt_version), do: EncoderHelper.call_encode(...
    method encode_list (line 128) | def encode_list(list, bolt_version), do: EncoderHelper.call_encode(:li...
    method encode_map (line 155) | def encode_map(map, bolt_version), do: EncoderHelper.call_encode(:map,...
    method encode_struct (line 182) | def encode_struct(struct, bolt_version) , do: EncoderHelper.call_encod...

FILE: lib/bolt_sips/internals/pack_stream/encoder_v2.ex
  class Bolt.Sips.Internals.PackStream.EncoderV2 (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderV2
    method encode_local_time (line 25) | def encode_local_time(local_time, bolt_version), do: EncoderHelper.cal...
    method encode_time_with_tz (line 45) | def encode_time_with_tz(%TimeWithTZOffset{time: time, timezone_offset:...
    method encode_date (line 66) | def encode_date(date, bolt_version), do: EncoderHelper.call_encode(:da...
    method encode_local_datetime (line 91) | def encode_local_datetime(local_datetime, bolt_version), do: EncoderHe...
    method encode_datetime_with_tz_id (line 120) | def encode_datetime_with_tz_id(datetime, bolt_version), do: EncoderHel...
    method encode_datetime_with_tz_offset (line 150) | def encode_datetime_with_tz_offset(
    method encode_duration (line 198) | def encode_duration(%Duration{} = duration, bolt_version), do: Encoder...
    method encode_point (line 261) | def encode_point(%Point{z: nil} = point, bolt_version), do: EncoderHel...
    method encode_point (line 263) | def encode_point(%Point{} = point, bolt_version), do: EncoderHelper.ca...

FILE: lib/bolt_sips/internals/pack_stream/encoder_v3.ex
  class Bolt.Sips.Internals.PackStream.EncoderV3 (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderV3

FILE: lib/bolt_sips/internals/pack_stream/error.ex
  class Bolt.Sips.Internals.PackStreamError (line 1) | defmodule Bolt.Sips.Internals.PackStreamError
    method message (line 19) | def message(%{data_type: nil, data: data, message: message, bolt_versi...
    method message (line 23) | def message(%{data_type: data_type, data: data, message: message, bolt...

FILE: lib/bolt_sips/internals/pack_stream/markers.ex
  class Bolt.Sips.Internals.PackStream.Markers (line 1) | defmodule Bolt.Sips.Internals.PackStream.Markers
  class Bolt.Sips.Internals.PackStream.MarkersHelper (line 95) | defmodule Bolt.Sips.Internals.PackStream.MarkersHelper
    method valid_signatures (line 103) | def valid_signatures() do

FILE: lib/bolt_sips/internals/pack_stream/message.ex
  class Bolt.Sips.Internals.PackStream.Message (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message
    method encode (line 34) | def encode(message, bolt_version) do
    method decode (line 43) | def decode(message, bolt_version) do

FILE: lib/bolt_sips/internals/pack_stream/message/decoder.ex
  class Bolt.Sips.Internals.PackStream.Message.Decoder (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.Decoder
    method decode (line 14) | def decode(
    method decode (line 22) | def decode(
    method decode (line 30) | def decode(
    method decode (line 38) | def decode(
    method build_response (line 52) | defp build_response(message_type, data, nb_entries, bolt_version) do

FILE: lib/bolt_sips/internals/pack_stream/message/encoder.ex
  class Bolt.Sips.Internals.PackStream.Message.Encoder (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.Encoder
    method signature (line 98) | defp signature(:ack_failure), do: @ack_failure_signature
    method signature (line 99) | defp signature(:discard_all), do: @discard_all_signature
    method signature (line 100) | defp signature(:pull_all), do: @pull_all_signature
    method signature (line 101) | defp signature(:reset), do: @reset_signature
    method signature (line 102) | defp signature(:begin), do: @begin_signature
    method signature (line 103) | defp signature(:commit), do: @commit_signature
    method signature (line 104) | defp signature(:goodbye), do: @goodbye_signature
    method signature (line 105) | defp signature(:hello), do: @hello_signature
    method signature (line 106) | defp signature(:rollback), do: @rollback_signature
    method signature (line 107) | defp signature(:run), do: @run_signature
    method signature (line 108) | defp signature(:init), do: @init_signature
    method client_name (line 113) | def client_name() do
    method valid_signatures (line 125) | def valid_signatures(3) do
    method encode (line 136) | def encode({:hello, []}, 3) do
    method encode (line 142) | def encode({:hello, [auth]}, 3) do
    method encode (line 149) | def encode({:begin, []}, 3) do
    method encode (line 154) | def encode({:begin, [%Metadata{} = metadata]}, 3) do
    method encode (line 163) | def encode({:begin, _}, _) do
    method encode (line 168) | def encode({:run, [statement]}, 3) do
    method encode (line 178) | def encode({:run, [statement, params]}, 3) do
    method encode (line 183) | def encode({:run, [statement, params, %Metadata{} = metadata]}, 3) do
    method encode (line 188) | def encode({:init, _}, 3) do
    method encode (line 512) | def encode(_data, _bolt_version) do
    method do_encode (line 516) | defp do_encode(message_type, data, bolt_version) do
    method auth_params_v1 (line 523) | defp auth_params_v1({}), do: %{}
    method auth_params_v1 (line 525) | defp auth_params_v1({username, password}) do
    method auth_params (line 535) | defp auth_params({}), do: user_agent()
    method auth_params (line 537) | defp auth_params({username, password}) do
    method user_agent (line 546) | defp user_agent() do
    method encode_message (line 564) | def encode_message(message_type, signature, data, bolt_version) do
    method generate_chunks (line 578) | defp generate_chunks(<<>>, chunks) do
    method generate_chunks (line 582) | defp generate_chunks(data, chunks) do
    method format_chunk (line 602) | defp format_chunk(chunk) do

FILE: lib/bolt_sips/internals/pack_stream/message/encoder_v1.ex
  class Bolt.Sips.Internals.PackStream.Message.EncoderV1 (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV1
    method encode (line 12) | def encode(data, bolt_version) do

FILE: lib/bolt_sips/internals/pack_stream/message/encoder_v2.ex
  class Bolt.Sips.Internals.PackStream.Message.EncoderV2 (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV2
    method encode (line 2) | def encode(_, _) do

FILE: lib/bolt_sips/internals/pack_stream/message/encoder_v3.ex
  class Bolt.Sips.Internals.PackStream.Message.EncoderV3 (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV3
    method valid_signatures (line 23) | def valid_signatures() do
    method encode (line 35) | def encode(data, bolt_version) do

FILE: lib/bolt_sips/internals/pack_stream/message/signatures.ex
  class Bolt.Sips.Internals.PackStream.Message.Signatures (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.Signatures

FILE: lib/bolt_sips/internals/pack_stream/utils.ex
  class Bolt.Sips.Internals.PackStream.Utils (line 1) | defmodule Bolt.Sips.Internals.PackStream.Utils

FILE: lib/bolt_sips/internals/pack_stream/v1.ex
  class Bolt.Sips.Internals.PackStream.V1 (line 1) | defmodule Bolt.Sips.Internals.PackStream.V1

FILE: lib/bolt_sips/internals/pack_stream/v2.ex
  class Bolt.Sips.Internals.PackStream.V2 (line 1) | defmodule Bolt.Sips.Internals.PackStream.V2

FILE: lib/bolt_sips/metadata.ex
  class Bolt.Sips.Metadata (line 1) | defmodule Bolt.Sips.Metadata
    method new (line 18) | def new(data) do
    method to_map (line 39) | def to_map(metadata) do
    method check_keys (line 50) | defp check_keys(data) do
    method validate_bookmarks (line 64) | defp validate_bookmarks([]) do
    method validate_bookmarks (line 68) | defp validate_bookmarks(_) do
    method validate_timeout (line 77) | defp validate_timeout(nil) do
    method validate_timeout (line 81) | defp validate_timeout(_) do
    method validate_metadata (line 91) | defp validate_metadata(%{}) do
    method validate_metadata (line 95) | defp validate_metadata(_) do

FILE: lib/bolt_sips/protocol.ex
  class Bolt.Sips.Protocol (line 1) | defmodule Bolt.Sips.Protocol
    method connect (line 28) | def connect(opts \\ [])
    method connect (line 29) | def connect([]), do: connect(Bolt.Sips.Utils.default_config())
    method connect (line 31) | def connect(opts) do
    method do_init (line 65) | defp do_init(transport, port, 3, auth) do
    method do_init (line 69) | defp do_init(transport, port, bolt_version, auth) do
    method checkout (line 74) | def checkout(%ConnData{sock: sock, configuration: conf} = conn_data) do
    method checkin (line 82) | def checkin(%ConnData{sock: sock, configuration: conf} = conn_data) do
    method disconnect (line 89) | def disconnect(_err, %ConnData{sock: sock, bolt_version: 3, configurat...
    method disconnect (line 98) | def disconnect(_err, %ConnData{sock: sock, configuration: conf}) do
    method handle_begin (line 105) | def handle_begin(_, %ConnData{sock: sock, bolt_version: 3, configurati...
    method handle_begin (line 110) | def handle_begin(_opts, conn_data) do
    method handle_rollback (line 118) | def handle_rollback(_, %ConnData{sock: sock, bolt_version: 3, configur...
    method handle_rollback (line 123) | def handle_rollback(_opts, conn_data) do
    method handle_commit (line 131) | def handle_commit(_, %ConnData{sock: sock, bolt_version: 3, configurat...
    method handle_commit (line 136) | def handle_commit(_opts, conn_data) do
    method handle_execute (line 144) | def handle_execute(query, params, opts, conn_data) do
    method handle_info (line 148) | def handle_info(msg, state) do
    method ping (line 158) | def ping(state), do: {:ok, state}
    method handle_prepare (line 159) | def handle_prepare(query, _opts, state), do: {:ok, query, state}
    method handle_close (line 160) | def handle_close(query, _opts, state), do: {:ok, query, state}
    method handle_deallocate (line 161) | def handle_deallocate(query, _cursor, _opts, state), do: {:ok, query, ...
    method handle_declare (line 162) | def handle_declare(query, _params, _opts, state), do: {:ok, query, sta...
    method handle_fetch (line 163) | def handle_fetch(query, _cursor, _opts, state), do: {:cont, query, state}
    method handle_status (line 164) | def handle_status(_opts, state), do: {:idle, state}
    method extract_auth (line 166) | defp extract_auth(nil), do: {}
    method extract_auth (line 168) | defp extract_auth(basic_auth), do: {basic_auth[:username], basic_auth[...
    method execute (line 170) | defp execute(q, params, _, conn_data) do
    method _to_hostname (line 208) | defp _to_hostname(hostname), do: hostname
  class ConnData (line 6) | defmodule ConnData

FILE: lib/bolt_sips/query.ex
  class Bolt.Sips.Query (line 1) | defmodule Bolt.Sips.Query
    method query! (line 54) | def query!(conn, statement), do: query!(conn, statement, %{})
    method query (line 70) | def query(conn, statement), do: query(conn, statement, %{})
    method query_commit (line 89) | defp query_commit(conn, statement, params, opts) do
    method commit! (line 130) | defp commit!([], conn, statements, formatted_params, opts),
    method commit! (line 133) | defp commit!(errors, _conn, _statements, _formatted_params, _opts),
    method tx! (line 136) | defp tx!(conn, [statement], params, opts), do: hd(send!(conn, statemen...
    method send! (line 142) | defp send!(conn, statement, params, opts, acc \\ [])
    method send! (line 144) | defp send!(conn, statement, params, opts, acc) do
    method format_param (line 175) | defp format_param({name, %Types.Duration{} = duration}),
    method format_param (line 178) | defp format_param({name, %Types.Point{} = point}), do: {name, Types.Po...
    method format_param (line 180) | defp format_param({name, value}), do: {name, {:ok, value}}

FILE: lib/bolt_sips/query_statement.ex
  class Bolt.Sips.QueryStatement (line 1) | defmodule Bolt.Sips.QueryStatement

FILE: lib/bolt_sips/response.ex
  class Bolt.Sips.Response (line 1) | defmodule Bolt.Sips.Response
    method first (line 92) | def first(%__MODULE__{results: []}), do: nil
    method first (line 93) | def first(%__MODULE__{results: [head | _tail]}), do: head
    method transform! (line 97) | def transform!(records, stats \\ :no) do
    method transform (line 105) | def transform(records, _stats \\ :no) do
    method fetch (line 118) | def fetch(%Bolt.Sips.Response{fields: fields, results: results}, key) do
    method fetch! (line 132) | def fetch!(%Bolt.Sips.Response{} = r, key) do
    method parse (line 138) | defp parse(records) do
    method parse_record (line 158) | defp parse_record(:success, %{"fields" => fields}, response) do
    method parse_record (line 162) | defp parse_record(:success, %{"profile" => profile, "type" => type} = ...
    method parse_record (line 166) | defp parse_record(:success, %{"notifications" => n, "plan" => plan, "t...
    method parse_record (line 170) | defp parse_record(:success, %{"plan" => plan, "type" => type}, respons...
    method parse_record (line 174) | defp parse_record(:success, %{"stats" => stats, "type" => type}, respo...
    method parse_record (line 178) | defp parse_record(:success, %{"bookmark" => bookmark, "type" => type},...
    method parse_record (line 182) | defp parse_record(:success, %{"type" => type}, response) do
    method parse_record (line 186) | defp parse_record(:success, %{}, response) do
    method parse_record (line 190) | defp parse_record(:success, record, _response) do
    method parse_record (line 201) | defp parse_record(:record, record, response) do
    method parse_record (line 205) | defp parse_record(_type, record, _response) do
    method create_results (line 214) | defp create_results(fields, records) do

FILE: lib/bolt_sips/response_encoder.ex
  class Bolt.Sips.ResponseEncoder (line 1) | defmodule Bolt.Sips.ResponseEncoder
    method encode (line 48) | def encode(response, :json) do
    method encode! (line 80) | def encode!(response, :json) do
    method jsonable_response (line 86) | defp jsonable_response(response) do

FILE: lib/bolt_sips/router.ex
  class Bolt.Sips.Router (line 1) | defmodule Bolt.Sips.Router
    method configure (line 33) | def configure(opts), do: GenServer.call(__MODULE__, {:configure, opts})
    method get_connection (line 35) | def get_connection(role, prefix \\ :direct)
    method get_connection (line 37) | def get_connection(role, prefix),
    method terminate_connections (line 40) | def terminate_connections(role, prefix \\ :default)
    method terminate_connections (line 42) | def terminate_connections(role, prefix),
    method info (line 45) | def info(), do: GenServer.call(__MODULE__, :info)
    method routing_table (line 46) | def routing_table(prefix), do: GenServer.call(__MODULE__, {:routing_ta...
    method start_link (line 49) | def start_link(init_args) do
    method init (line 55) | def init(options) do
    method handle_call (line 60) | def handle_call({:configure, opts}, _from, state) do
    method handle_call (line 98) | def handle_call({:get_connection, role, prefix}, _from, state) do
    method handle_call (line 112) | def handle_call({:terminate_connections, role, prefix}, _from, state) do
    method handle_call (line 131) | def handle_call(:info, _from, state), do: {:reply, state, state}
    method handle_call (line 133) | def handle_call({:routing_table_info, prefix}, _from, state) do
    method handle_continue (line 144) | def handle_continue(:post_init, opts), do: {:noreply, _configure(opts)}
    method _configure (line 146) | defp _configure(opts) do
    method get_routing_table (line 168) | defp get_routing_table(
    method get_routing_table (line 192) | defp get_routing_table(_opts, false), do: {:ok, @no_routing}
    method get_routing_table (line 194) | defp get_routing_table(opts, _) do
    method start_connections (line 244) | def start_connections(opts, routing_table)
    method start_connections (line 255) | def start_connections(opts, routing_table) do
    method handle_info (line 295) | def handle_info({:refresh, prefix}, state) do
    method handle_info (line 326) | def handle_info(req, state) do
    method parse_server_version (line 352) | def parse_server_version(%{"server" => server_version_string}) do
    method parse_server_version (line 360) | def parse_server_version(some_version),
    method error_no_connection_available_for_role (line 363) | defp error_no_connection_available_for_role(role, _e, prefix \\ :default)
    method error_no_connection_available_for_role (line 365) | defp error_no_connection_available_for_role(role, _e, prefix) do
    method merge_connections_maps (line 371) | def merge_connections_maps(current_connections, new_connections, prefi...
    method merge_connections_maps (line 373) | def merge_connections_maps(current_connections, new_connections, prefi...
    method remove_old_urls (line 390) | defp remove_old_urls(role, url, urls), do: if(url in urls, do: [], els...
    method close_connections (line 399) | defp close_connections(connections, prefix) do
    method _get_connection (line 413) | defp _get_connection(role, connections, prefix) do
  class State (line 11) | defmodule State

FILE: lib/bolt_sips/routing/connection_supervisor.ex
  class Bolt.Sips.ConnectionSupervisor (line 1) | defmodule Bolt.Sips.ConnectionSupervisor
    method start_link (line 12) | def start_link(init_args) do
    method init (line 17) | def init(_args) do
    method start_child (line 25) | def start_child(role, url, config) do
    method find_connection (line 57) | def find_connection(role, url, prefix), do: find_connection("#{prefix}...
    method find_connection (line 60) | def find_connection(name) do
    method terminate_connection (line 68) | def terminate_connection(role, url, prefix \\ :default) do
    method connections (line 76) | def connections() do
    method _connections (line 85) | defp _connections() do
    method via_tuple (line 99) | def via_tuple(name) do

FILE: lib/bolt_sips/routing/load_balancer.ex
  class Bolt.Sips.LoadBalancer (line 1) | defmodule Bolt.Sips.LoadBalancer
    method least_reused_url (line 19) | def least_reused_url(urls) do

FILE: lib/bolt_sips/routing/routing_table.ex
  class Bolt.Sips.Routing.RoutingTable (line 1) | defmodule Bolt.Sips.Routing.RoutingTable
    method parse (line 40) | def parse(%{"servers" => servers, "ttl" => ttl}) do
    method parse (line 47) | def parse(map),
    method parse_servers (line 51) | defp parse_servers(servers) do
    method to_atomic_role (line 83) | defp to_atomic_role(_), do: {:error, :alien_role}
    method parse_ttl (line 85) | def parse_ttl(ttl), do: {:ok, ensure_integer(ttl)}
    method ttl_expired? (line 88) | def ttl_expired?(updated_at, ttl) do
    method ensure_integer (line 95) | defp ensure_integer(ttl), do: raise(ArgumentError, "invalid ttl: " <> ...

FILE: lib/bolt_sips/socket.ex
  class Bolt.Sips.Socket (line 1) | defmodule Bolt.Sips.Socket

FILE: lib/bolt_sips/types.ex
  class Bolt.Sips.Types (line 1) | defmodule Bolt.Sips.Types
  class Entity (line 34) | defmodule Entity
  class Node (line 48) | defmodule Node
  class Relationship (line 68) | defmodule Relationship
  class UnboundRelationship (line 87) | defmodule UnboundRelationship
  class Path (line 104) | defmodule Path
    method graph (line 128) | def graph(path) do
    method draw_path (line 146) | defp draw_path(_n, _r, _s, _i, [], acc, _ln, _nn), do: acc
    method draw_path (line 148) | defp draw_path(n, r, s, i, [h | t] = _rel_index, acc, ln, _nn) do
  class TimeWithTZOffset (line 180) | defmodule TimeWithTZOffset
    method format_param (line 214) | def format_param(param) do
  class DateTimeWithTZOffset (line 219) | defmodule DateTimeWithTZOffset
    method format_param (line 253) | def format_param(param) do
  class Duration (line 258) | defmodule Duration
    method manage_nanoseconds (line 350) | defp manage_nanoseconds(seconds, nanoseconds) do
    method format_param (line 384) | def format_param(param) do
    method format_date (line 389) | defp format_date(%Duration{years: years, months: months, weeks: weeks,...
    method format_time (line 413) | defp format_time(_) do
    method format_duration_part (line 423) | defp format_duration_part(_, _) do
    method stringify_number (line 432) | defp stringify_number(number) do
  class Point (line 437) | defmodule Point
    method create (line 508) | def create(:cartesian, x, y) do
    method create (line 512) | def create(:wgs_84, longitude, latitude) do
    method create (line 568) | def create(:cartesian, x, y, z) do
    method create (line 572) | def create(:wgs_84, longitude, latitude, height) do
    method crs (line 601) | defp crs(@srid_cartesian), do: "cartesian"
    method crs (line 602) | defp crs(@srid_cartesian_3d), do: "cartesian-3d"
    method crs (line 603) | defp crs(@srid_wgs_84), do: "wgs-84"
    method crs (line 604) | defp crs(@srid_wgs_84_3d), do: "wgs-84-3d"
    method format_coord (line 607) | defp format_coord(coord), do: coord
    method format_param (line 632) | def format_param(param) do

FILE: lib/bolt_sips/types_helper.ex
  class Bolt.Sips.TypesHelper (line 1) | defmodule Bolt.Sips.TypesHelper
    method decompose_in_hms (line 6) | def decompose_in_hms(seconds) do
    method datetime_with_micro (line 23) | def datetime_with_micro(%NaiveDateTime{} = naive_dt, timezone) do
    method formated_time_offset (line 31) | def formated_time_offset(offset_seconds) do
    method get_sign_string (line 40) | defp get_sign_string(_) do
    method format_time_part (line 48) | defp format_time_part(time_part) do

FILE: lib/bolt_sips/utils.ex
  class Bolt.Sips.Utils (line 1) | defmodule Bolt.Sips.Utils
    method random_id (line 24) | def random_id, do: :rand.uniform() |> Float.to_string() |> String.slic...
    method default_config (line 30) | def default_config(), do: Application.get_env(:bolt_sips, Bolt, []) |>...
    method default_config (line 32) | def default_config(opts) do
    method or_use_url_if_present (line 102) | defp or_use_url_if_present(config) do
    method username_and_password (line 145) | defp username_and_password(config, _), do: config
    method port_from_url (line 155) | defp port_from_url(_port), do: @default_bolt_port
    method now (line 164) | def now(unit \\ :seconds)
    method now (line 167) | def now(unit),
    method routing_context (line 174) | defp routing_context(nil), do: decode("")
    method routing_context (line 175) | defp routing_context(query), do: decode(query)
    method decode (line 177) | def decode(query) do
    method do_decode (line 181) | defp do_decode([], acc), do: acc
    method do_decode (line 183) | defp do_decode([h | t], acc) do
    method decode_kv (line 191) | defp decode_kv(""), do: false
    method decode_kv (line 192) | defp decode_kv(<<?$, _::binary>>), do: false
    method decode_kv (line 194) | defp decode_kv(kv), do: decode_key(kv, "")
    method decode_key (line 196) | defp decode_key("", _key), do: false
    method decode_key (line 197) | defp decode_key(<<?=, _::binary>>, ""), do: false
    method decode_key (line 198) | defp decode_key(<<?=, t::binary>>, key), do: decode_value(t, "", key, "")
    method decode_key (line 200) | defp decode_key(<<h, t::binary>>, key), do: decode_key(t, <<key::binar...
    method decode_value (line 202) | defp decode_value("", _spaces, key, value), do: {key, value}
    method decode_value (line 204) | defp decode_value(<<?\s, t::binary>>, spaces, key, value),
    method decode_value (line 210) | defp decode_value(<<h, t::binary>>, spaces, key, value),

FILE: lib/mix/tasks/cypher.ex
  class Mix.Tasks.Bolt.Cypher (line 1) | defmodule Mix.Tasks.Bolt.Cypher
    method run (line 33) | def run(args) do
    method run_options (line 61) | defp run_options(_, nil) do
    method run_options (line 67) | defp run_options(args, config) do
    method log_cypher (line 71) | defp log_cypher(msg), do: Mix.shell().info([:green, "#{inspect(msg)}"])
    method log_response (line 72) | defp log_response(msg), do: Mix.shell().info([:yellow, "#{inspect(msg)...
    method log_error (line 73) | defp log_error(msg), do: Mix.shell().info([:white, "#{msg}"])

FILE: mix.exs
  class BoltSips.Mixfile (line 1) | defmodule BoltSips.Mixfile
    method project (line 8) | def project do
    method application (line 37) | def application do
    method aliases (line 43) | defp aliases do
    method elixirc_paths (line 51) | defp elixirc_paths(:test), do: ["lib", "test/support"]
    method elixirc_paths (line 52) | defp elixirc_paths(_), do: ["lib"]
    method package (line 54) | defp package do
    method docs (line 75) | defp docs do
    method deps (line 101) | defp deps do

FILE: test/bolt_sips/internals/bolt_protocol_all_bolt_version_test.exs
  class Bolt.Sips.Internals.BoltProtocolAllBoltVersionTest (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolAllBoltVersionTest

FILE: test/bolt_sips/internals/bolt_protocol_bolt_v1_test.exs
  class Bolt.Sips.Internals.BoltProtocolV1Test (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolV1Test

FILE: test/bolt_sips/internals/bolt_protocol_bolt_v2_test.exs
  class Bolt.Sips.Internals.BoltProtoolBoltV2Test (line 1) | defmodule Bolt.Sips.Internals.BoltProtoolBoltV2Test

FILE: test/bolt_sips/internals/bolt_protocol_bolt_v3_test.exs
  class Bolt.Sips.Internals.BoltProtocolBoltV3Test (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolBoltV3Test

FILE: test/bolt_sips/internals/bolt_protocol_v1_test.exs
  class BoltProtocolV1.Sips.Internals.BoltProtocolV1Test (line 1) | defmodule BoltProtocolV1.Sips.Internals.BoltProtocolV1Test

FILE: test/bolt_sips/internals/bolt_protocol_v3_test.exs
  class Bolt.Sips.Internals.BoltProtocolV3Test (line 1) | defmodule Bolt.Sips.Internals.BoltProtocolV3Test

FILE: test/bolt_sips/internals/bolt_version_helper_test.exs
  class Bolt.Sips.Internals.BoltVersionHelperTest (line 1) | defmodule Bolt.Sips.Internals.BoltVersionHelperTest

FILE: test/bolt_sips/internals/logger_test.exs
  class Bolt.Sips.Internals.LoggerTest (line 1) | defmodule Bolt.Sips.Internals.LoggerTest

FILE: test/bolt_sips/internals/pack_stream/decoder_test.exs
  class Bolt.Sips.Internals.PackStream.DecoderTest (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderTest

FILE: test/bolt_sips/internals/pack_stream/decoder_v1_test.exs
  class Bolt.Sips.Internals.PackStream.DecoderV1Test (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderV1Test

FILE: test/bolt_sips/internals/pack_stream/decoder_v2_test.exs
  class Bolt.Sips.Internals.PackStream.DecoderV2Test (line 1) | defmodule Bolt.Sips.Internals.PackStream.DecoderV2Test

FILE: test/bolt_sips/internals/pack_stream/encoder_helper_test.exs
  class Bolt.Sips.Internals.PackStream.EncoderHelperTest (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderHelperTest

FILE: test/bolt_sips/internals/pack_stream/encoder_test.exs
  class Bolt.Sips.Internals.PackStream.EncoderTest (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderTest
  class TestStruct (line 9) | defmodule TestStruct

FILE: test/bolt_sips/internals/pack_stream/encoder_v1_test.exs
  class Bolt.Sips.Internals.PackStream.EncoderV1Test (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderV1Test
  class TestStruct (line 6) | defmodule TestStruct

FILE: test/bolt_sips/internals/pack_stream/encoder_v2_test.exs
  class Bolt.Sips.Internals.PackStream.EncoderV2Test (line 1) | defmodule Bolt.Sips.Internals.PackStream.EncoderV2Test

FILE: test/bolt_sips/internals/pack_stream/message/decoder_test.exs
  class Bolt.Sips.Internals.PackStream.Message.DecoderTest (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.DecoderTest

FILE: test/bolt_sips/internals/pack_stream/message/encoder_test.exs
  class Bolt.Sips.Internals.PackStream.Message.EncoderTest (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.EncoderTest
  class TestUser (line 10) | defmodule TestUser

FILE: test/bolt_sips/internals/pack_stream/message/encoder_v1_test.exs
  class Bolt.Sips.Internals.PackStream.Message.EncoderV1Test (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV1Test

FILE: test/bolt_sips/internals/pack_stream/message/encoder_v3_test.exs
  class Bolt.Sips.Internals.PackStream.Message.EncoderV3Test (line 1) | defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV3Test

FILE: test/bolt_sips/internals/pack_stream/message_test.exs
  class Bolt.Sips.Internals.PackStream.MessageTest (line 1) | defmodule Bolt.Sips.Internals.PackStream.MessageTest

FILE: test/bolt_sips/metadata_test.exs
  class Bolt.Sips.MetadataTest (line 1) | defmodule Bolt.Sips.MetadataTest

FILE: test/bolt_sips/performance_test.exs
  class Bolt.Sips.PerformanceTest (line 1) | defmodule Bolt.Sips.PerformanceTest

FILE: test/bolt_sips/protocol_test.exs
  class Bolt.Sips.ProtocolTest (line 1) | defmodule Bolt.Sips.ProtocolTest

FILE: test/bolt_sips/response_encoder/json_implementations_test.exs
  class Bolt.Sips.JsonImplementationsTest (line 1) | defmodule Bolt.Sips.JsonImplementationsTest
    method fixture (line 27) | defp fixture() do
    method result (line 83) | defp result(:jason) do
    method result (line 146) | defp result(:poison) do
  class TestStruct (line 15) | defmodule TestStruct

FILE: test/bolt_sips/response_encoder/json_test.exs
  class Bolt.Sips.ResponseEncode.JsonTest (line 1) | defmodule Bolt.Sips.ResponseEncode.JsonTest
  class TestStruct (line 17) | defmodule TestStruct

FILE: test/bolt_sips/response_encoder_test.exs
  class Bolt.Sips.ResponseEncoderTest (line 1) | defmodule Bolt.Sips.ResponseEncoderTest

FILE: test/bolt_sips/types_helpers_test.exs
  class Bolt.Sips.TypesHelperTest (line 1) | defmodule Bolt.Sips.TypesHelperTest

FILE: test/bolt_sips/types_test.exs
  class Bolt.Sips.TypesTest (line 1) | defmodule Bolt.Sips.TypesTest

FILE: test/boltkit_test.exs
  class Bolt.Sips.BoltStubTest (line 1) | defmodule Bolt.Sips.BoltStubTest

FILE: test/config_test.exs
  class Config.Test (line 1) | defmodule Config.Test

FILE: test/errors_test.exs
  class ErrorsTest (line 1) | defmodule ErrorsTest

FILE: test/invalid_param_type_test.exs
  class Bolt.Sips.InvalidParamType.Test (line 1) | defmodule Bolt.Sips.InvalidParamType.Test

FILE: test/one_test.exs
  class One.Test (line 1) | defmodule One.Test

FILE: test/query_bolt_v2_test.exs
  class Bolt.Sips.QueryBoltV2Test (line 1) | defmodule Bolt.Sips.QueryBoltV2Test

FILE: test/query_test.exs
  class Query.Test (line 1) | defmodule Query.Test
    method rebuild_fixtures (line 12) | defp rebuild_fixtures(conn) do
  class TestUser (line 8) | defmodule TestUser

FILE: test/response_test.exs
  class ResponseTest (line 1) | defmodule ResponseTest

FILE: test/router_test.exs
  class Bolt.Sips.Routing.RouterTest (line 1) | defmodule Bolt.Sips.Routing.RouterTest

FILE: test/routing/connections_test.exs
  class Bolt.Sips.Routing.ConnectionsTest (line 1) | defmodule Bolt.Sips.Routing.ConnectionsTest

FILE: test/routing/crud_test.exs
  class Bolt.Sips.Routing.CrudTest (line 1) | defmodule Bolt.Sips.Routing.CrudTest

FILE: test/routing/routing_table_parser_test.exs
  class Routing.Routing.TableParserTest (line 1) | defmodule Routing.Routing.TableParserTest

FILE: test/routing/routing_test.exs
  class Bolt.Sips.RoutingTest (line 1) | defmodule Bolt.Sips.RoutingTest

FILE: test/routing/transaction_test.exs
  class Bolt.Sips.Routing.TransactionTest (line 1) | defmodule Bolt.Sips.Routing.TransactionTest

FILE: test/support/boltkit_case.ex
  class Bolt.Sips.BoltKitCase (line 1) | defmodule Bolt.Sips.BoltKitCase
    method stub_servers (line 73) | defp stub_servers(%{scripts: scripts} = args) do
    method wait_for_socket (line 95) | defp wait_for_socket(address, port) do
    method connect (line 102) | defp connect(url, prefix) do

FILE: test/support/conn_case.ex
  class Bolt.Sips.ConnCase (line 1) | defmodule Bolt.Sips.ConnCase

FILE: test/support/conn_routing_case.ex
  class Bolt.Sips.RoutingConnCase (line 1) | defmodule Bolt.Sips.RoutingConnCase

FILE: test/support/database.ex
  class Bolt.Sips.Test.Support.Database (line 1) | defmodule Bolt.Sips.Test.Support.Database
    method clear (line 2) | def clear(conn) do

FILE: test/support/fixture.ex
  class Bolt.Sips.Fixture (line 1) | defmodule Bolt.Sips.Fixture
    method create_graph (line 2) | def create_graph(conn, :movie) do
    method create_graph (line 6) | def create_graph(conn, :bolt_sips) do
    method bolt_sips_cypher (line 10) | def bolt_sips_cypher() do
    method movie_cypher (line 29) | def movie_cypher() do

FILE: test/support/internal_case.ex
  class Bolt.Sips.InternalCase (line 1) | defmodule Bolt.Sips.InternalCase
    method neo4j_uri (line 20) | defp neo4j_uri do
    method init (line 36) | defp init(transport, port, 3, auth) do
    method init (line 40) | defp init(transport, port, bolt_version, auth) do

FILE: test/test_helper.exs
  class Bolt.Sips.TestHelper (line 7) | defmodule Bolt.Sips.TestHelper
    method read_whole_file (line 12) | def read_whole_file(path) do
    method stream_file_join (line 22) | def stream_file_join(filename) do
    method file_error_description (line 27) | defp file_error_description(:enoent), do: "because the file does not e...
    method file_error_description (line 28) | defp file_error_description(reason), do: "due to #{reason}."

FILE: test/test_large_param_set.exs
  class Large.Param.Set.Test (line 1) | defmodule Large.Param.Set.Test

FILE: test/test_support.exs
  class TestSupport (line 1) | defmodule TestSupport

FILE: test/transaction_test.exs
  class Transaction.Test (line 1) | defmodule Transaction.Test
Condensed preview — 153 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (552K chars).
[
  {
    "path": ".credo.exs",
    "chars": 4329,
    "preview": "# This file contains the configuration for Credo.\n#\n# If you find anything wrong or unclear in this file, please report "
  },
  {
    "path": ".dialyzer_ignore.exs",
    "chars": 39,
    "preview": "[\n  ~r/__impl__.*does\\ not\\ exist\\./\n]\n"
  },
  {
    "path": ".formatter.exs",
    "chars": 342,
    "preview": "# Used by \"mix format\" and to export configuration.\nexport_locals_without_parens = [\n  plug: 1,\n  plug: 2,\n  adapter: 1,"
  },
  {
    "path": ".gitattributes",
    "chars": 12,
    "preview": "* text=auto\n"
  },
  {
    "path": ".gitignore",
    "chars": 2063,
    "preview": "### Elixir template\n# The directory Mix will write compiled artifacts to.\n/_build\n\n# If you run \"mix test --cover\", cove"
  },
  {
    "path": ".iex.exs",
    "chars": 1219,
    "preview": "try do\n  Code.eval_file(\".iex.exs\", \"~\")\nrescue\n  Code.LoadError -> :rescued\nend\n\nalias Bolt.Sips.{Utils, Protocol, Rout"
  },
  {
    "path": ".markdownlint.json",
    "chars": 42,
    "preview": "{\n    \"MD013\": false,\n    \"MD030\": false\n}"
  },
  {
    "path": ".prettierrc.yaml",
    "chars": 123,
    "preview": "# .prettierrc or .prettierrc.yaml\ntrailingComma: \"es5\"\ntabWidth: 2\nsemi: false\nsingleQuote: true\nMD013: false\nMD030: fal"
  },
  {
    "path": ".tool-versions",
    "chars": 136,
    "preview": "erlang 24.1.7\nelixir 1.13.0-otp-24\nnodejs 12.6.0\n#python 3.7.3\npython 3.7.3 2.7.16\n\nruby 2.7.5\nlua 5.3.5\nterraform 0.15."
  },
  {
    "path": ".travis.yml",
    "chars": 2947,
    "preview": "sudo: required\nservices: docker\nlanguage: elixir\nmatrix:\n  include:\n    - elixir: 1.7.4\n      otp_release: 21.2\n      en"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 15584,
    "preview": "# Changelog\n\n## 2.1.0\n\nThank you https://github.com/zediogoviana, for the following improvements:\n\n- Add configurable SS"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "chars": 435,
    "preview": "# Precheck\n\n* For bugs, do a quick search and make sure the bug has not yet been reported.\n* Finally, be nice and have f"
  },
  {
    "path": "LICENSE",
    "chars": 10850,
    "preview": "                              Apache License\n                        Version 2.0, January 2004\n                     http"
  },
  {
    "path": "README.md",
    "chars": 5328,
    "preview": "<img src=\"assets/logo_transparent.png\" alt=\"logo\" width=\"240\"/>\n\n# Neo4j driver for Elixir.\n\n[![Build Status](https://tr"
  },
  {
    "path": "benchees/conn_to_local_bench.exs",
    "chars": 1632,
    "preview": "# benchmark the time it takes to open connections to a local neo4j server.\n# Misc...\n#\n# What I want:\n#\n# - I want to me"
  },
  {
    "path": "config/config.exs",
    "chars": 1116,
    "preview": "# This file is responsible for configuring your application\n# and its dependencies with the aid of the Mix.Config module"
  },
  {
    "path": "config/dev.exs",
    "chars": 571,
    "preview": "import Config\n\nconfig :mix_test_watch,\n  clear: true\n\nlevel =\n  if System.get_env(\"DEBUG\") do\n    :debug\n  else\n    :inf"
  },
  {
    "path": "config/test.exs",
    "chars": 672,
    "preview": "import Config\n\nconfig :bolt_sips, Bolt,\n  # default port considered to be: 7687\n  url: \"bolt://localhost\",\n  basic_auth:"
  },
  {
    "path": "docker-compose.yml",
    "chars": 2734,
    "preview": "version: \"3.0\"\n\nnetworks:\n  lan:\n\nservices:\n  core1:\n    container_name: core1\n    image: neo4j:3.5.3-enterprise\n    net"
  },
  {
    "path": "docs/examples/readme.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/features/about-encoding.md",
    "chars": 1936,
    "preview": "# About encoding\n\nBolt.Sips provides support for encoding your query result in different formats.\nFor now, only JSON is "
  },
  {
    "path": "docs/features/about-transactions.md",
    "chars": 846,
    "preview": "# About transactions\n\nTransaction management in Neo4j 3.5+ differs from what it was in prior versions.\nThe cypher keywor"
  },
  {
    "path": "docs/features/configuration.md",
    "chars": 6672,
    "preview": "# Configuration\n\nBolt.Sips can be configured using the well known Mix config files, or by using simple keyword lists.\n\nT"
  },
  {
    "path": "docs/features/multi-tenancy.md",
    "chars": 1449,
    "preview": "# Multi tenancy\n\nVery similar to the role-based connections, with multi-tenancy you will be able to connect to servers w"
  },
  {
    "path": "docs/features/role-based-connections.md",
    "chars": 1565,
    "preview": "# Role-based connections\n\nStarting with the 2.0 version, you can have distinct configurations that you can use in your a"
  },
  {
    "path": "docs/features/routing.md",
    "chars": 7538,
    "preview": "# Routing\n\nWhen connecting to a Neo4j cluster, `Bolt.Sips` will create 3 distinct connection pools, each of them dedicat"
  },
  {
    "path": "docs/features/using-cypher.md",
    "chars": 4643,
    "preview": "# Using Bolt.Sips to query the Neo4j server\n\nLet's talk about the basics of querying a Neo4j server, using `Bolt.Sips`, "
  },
  {
    "path": "docs/features/using-temporal-and-spatial-types.md",
    "chars": 4776,
    "preview": "# Using temporal and spatial types\n\nTemporal and spatial types are supported since Neo4J 3.4.\nYou can used the elixir st"
  },
  {
    "path": "docs/features/using-with-phoenix.md",
    "chars": 1536,
    "preview": "# Using Bolt.Sips with Phoenix, or similar\n\nDon't forget to start the `Bolt.Sips` driver in your supervision tree. Examp"
  },
  {
    "path": "docs/getting-started.md",
    "chars": 4297,
    "preview": "# Getting Started\n\nLet's start by creating a simple Elixir project, as a playground for our tests.\n\n```sh\nmix new neo4j_"
  },
  {
    "path": "lib/bolt_sips/application.ex",
    "chars": 202,
    "preview": "defmodule Bolt.Sips.Application do\n  @moduledoc false\n\n  use Application\n\n  alias Bolt.Sips\n\n  def start(_, start_args) "
  },
  {
    "path": "lib/bolt_sips/enumerable_response.ex",
    "chars": 1179,
    "preview": "defimpl Enumerable, for: Bolt.Sips.Response do\n  alias Bolt.Sips.Response\n\n  def count(%Response{results: nil}), do: {:o"
  },
  {
    "path": "lib/bolt_sips/error.ex",
    "chars": 734,
    "preview": "defmodule Bolt.Sips.Error do\n  @moduledoc \"\"\"\n  represents an error message\n  \"\"\"\n  alias __MODULE__\n  @type t :: %__MOD"
  },
  {
    "path": "lib/bolt_sips/exception.ex",
    "chars": 272,
    "preview": "defmodule Bolt.Sips.Exception do\n  @moduledoc \"\"\"\n  This module defines a `Bolt.Sips.Exception` structure containing two"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol.ex",
    "chars": 8715,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocol do\n  @moduledoc false\n  # A library that handles Bolt Protocol (v1 and v2).\n "
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_helper.ex",
    "chars": 4580,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolHelper do\n  @moduledoc false\n\n  alias Bolt.Sips.Internals.PackStream.Message\n "
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_v1.ex",
    "chars": 8894,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolV1 do\n  @moduledoc false\n  alias Bolt.Sips.Internals.BoltProtocolHelper\n  alia"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_v2.ex",
    "chars": 195,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolV2 do\n  @moduledoc false\n  # There's no specific messagee for Bolt V2\n  # This"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_v3.ex",
    "chars": 6899,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolV3 do\n  alias Bolt.Sips.Internals.BoltProtocol\n  alias Bolt.Sips.Internals.Bol"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_version_helper.ex",
    "chars": 1017,
    "preview": "defmodule Bolt.Sips.Internals.BoltVersionHelper do\n  @moduledoc false\n  @available_bolt_versions [1, 2, 3]\n\n  @doc \"\"\"\n "
  },
  {
    "path": "lib/bolt_sips/internals/error.ex",
    "chars": 2948,
    "preview": "defmodule Bolt.Sips.Internals.Error do\n  @moduledoc false\n  defexception [:message, :code, :connection_id, :function, :t"
  },
  {
    "path": "lib/bolt_sips/internals/logger.ex",
    "chars": 1454,
    "preview": "defmodule Bolt.Sips.Internals.Logger do\n  @moduledoc false\n  # Designed to log Bolt protocol message between Client and "
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder.ex",
    "chars": 672,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Decoder do\n  @moduledoc false\n  _moduledoc = \"\"\"\n  This module is responsible f"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_impl_v1.ex",
    "chars": 7656,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderImplV1 do\n  alias Bolt.Sips.Types\n\n  defmacro __using__(_options) do\n   "
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_impl_v2.ex",
    "chars": 6077,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderImplV2 do\n  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffse"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_utils.ex",
    "chars": 1851,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderUtils do\n  alias Bolt.Sips.Internals.PackStreamError\n\n  defmacro __using"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_v1.ex",
    "chars": 628,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderV1 do\n  @moduledoc false\n  _moduledoc = \"\"\"\n  Bolt V1 can decode:\n  - Nu"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_v2.ex",
    "chars": 925,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderV2 do\n  @moduledoc false\n  _module_doc = \"\"\"\n  Bolt V2 has specification"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_v3.ex",
    "chars": 117,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderV3 do\n  def decode(_, _) do\n    {:error, :not_implemented}\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder.ex",
    "chars": 3782,
    "preview": "alias Bolt.Sips.Internals.PackStream\nalias Bolt.Sips.Internals.PackStream.EncoderHelper\n\ndefprotocol Bolt.Sips.Internals"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_helper.ex",
    "chars": 1371,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderHelper do\n  @moduledoc false\n  alias Bolt.Sips.Internals.BoltVersionHelp"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_v1.ex",
    "chars": 5764,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderV1 do\n  @moduledoc false\n  alias Bolt.Sips.Internals.PackStream.EncoderH"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_v2.ex",
    "chars": 8866,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderV2 do\n  @moduledoc false\n  use Bolt.Sips.Internals.PackStream.Markers\n  "
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_v3.ex",
    "chars": 58,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderV3 do\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/error.ex",
    "chars": 924,
    "preview": "defmodule Bolt.Sips.Internals.PackStreamError do\n  @moduledoc false\n\n  # Represents an error when encoding data for the "
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/markers.ex",
    "chars": 2468,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Markers do\n  @moduledoc false\n  defmacro __using__(_opts) do\n    quote do\n     "
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/decoder.ex",
    "chars": 1946,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.Decoder do\n  @moduledoc false\n\n  @tiny_struct_marker 0xB\n\n  @success_si"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder.ex",
    "chars": 18418,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.Encoder do\n  @moduledoc false\n  _module_doc = \"\"\"\n  Manages the message"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder_v1.ex",
    "chars": 520,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV1 do\n  @moduledoc false\n  use Bolt.Sips.Internals.PackStream.Me"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder_v2.ex",
    "chars": 125,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV2 do\n  def encode(_, _) do\n    {:error, :not_implemented}\n  end"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder_v3.ex",
    "chars": 967,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV3 do\n  @moduledoc false\n  use Bolt.Sips.Internals.PackStream.Me"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/signatures.ex",
    "chars": 629,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.Signatures do\n  @moduledoc false\n  defmacro __using__(_opts) do\n    quo"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message.ex",
    "chars": 1401,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message do\n  @moduledoc false\n\n  # Manage the message encoding and decoding.\n  "
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/utils.ex",
    "chars": 2169,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Utils do\n  alias Bolt.Sips.Internals.PackStream.Encoder\n  alias Bolt.Sips.Types"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/v1.ex",
    "chars": 6685,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.V1 do\n  defmacro __using__(_options) do\n    quote do\n      import unquote(__MOD"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/v2.ex",
    "chars": 3461,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.V2 do\n  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream.ex",
    "chars": 1274,
    "preview": "defmodule Bolt.Sips.Internals.PackStream do\n  @moduledoc false\n\n  # The PackStream implementation for Bolt.\n  #\n  # This"
  },
  {
    "path": "lib/bolt_sips/metadata.ex",
    "chars": 2605,
    "preview": "defmodule Bolt.Sips.Metadata do\n  @moduledoc false\n  defstruct [:bookmarks, :tx_timeout, :metadata]\n\n  @type t :: %__MOD"
  },
  {
    "path": "lib/bolt_sips/protocol.ex",
    "chars": 6478,
    "preview": "defmodule Bolt.Sips.Protocol do\n  @moduledoc false\n  # Implements callbacks required by DBConnection.\n  # Each callback "
  },
  {
    "path": "lib/bolt_sips/query.ex",
    "chars": 6676,
    "preview": "defmodule Bolt.Sips.Query do\n  @moduledoc \"\"\"\n  Provides a simple Query DSL.\n\n  You can run simple Cypher queries with o"
  },
  {
    "path": "lib/bolt_sips/query_statement.ex",
    "chars": 304,
    "preview": "defmodule Bolt.Sips.QueryStatement do\n  @moduledoc false\n  defstruct statement: \"\"\nend\n\ndefimpl DBConnection.Query, for:"
  },
  {
    "path": "lib/bolt_sips/response.ex",
    "chars": 6651,
    "preview": "defmodule Bolt.Sips.Response do\n  @moduledoc \"\"\"\n  Support for transforming a Bolt response to a list of Bolt.Sips.Types"
  },
  {
    "path": "lib/bolt_sips/response_encoder/json/jason.ex",
    "chars": 1500,
    "preview": "if Code.ensure_loaded?(Jason) do\n  defmodule Bolt.Sips.ResponseEncoder.Json.Jason do\n    @moduledoc \"\"\"\n    A default im"
  },
  {
    "path": "lib/bolt_sips/response_encoder/json/poison.ex",
    "chars": 1537,
    "preview": "if Code.ensure_loaded?(Poison) do\n  defmodule Bolt.Sips.ResponseEncoder.Json.Poison do\n    @moduledoc \"\"\"\n    A default "
  },
  {
    "path": "lib/bolt_sips/response_encoder/json.ex",
    "chars": 3357,
    "preview": "defprotocol Bolt.Sips.ResponseEncoder.Json do\n  @moduledoc \"\"\"\n  Protocol controlling how a value is made jsonable.\n\n  I"
  },
  {
    "path": "lib/bolt_sips/response_encoder.ex",
    "chars": 2867,
    "preview": "defmodule Bolt.Sips.ResponseEncoder do\n  @moduledoc \"\"\"\n  This module provides functions to encode a query result or dat"
  },
  {
    "path": "lib/bolt_sips/router.ex",
    "chars": 15179,
    "preview": "defmodule Bolt.Sips.Router do\n  @moduledoc \"\"\"\n  This \"driver\" works in tandem with Neo4j's [Causal Clustering](https://"
  },
  {
    "path": "lib/bolt_sips/routing/connection_supervisor.ex",
    "chars": 2597,
    "preview": "defmodule Bolt.Sips.ConnectionSupervisor do\n  @moduledoc false\n\n  use DynamicSupervisor\n\n  alias Bolt.Sips.Protocol\n  al"
  },
  {
    "path": "lib/bolt_sips/routing/load_balancer.ex",
    "chars": 693,
    "preview": "defmodule Bolt.Sips.LoadBalancer do\n  @moduledoc \"\"\"\n  a simple load balancer used for selecting a server address from a"
  },
  {
    "path": "lib/bolt_sips/routing/routing_table.ex",
    "chars": 2754,
    "preview": "defmodule Bolt.Sips.Routing.RoutingTable do\n  @moduledoc ~S\"\"\"\n  representing the routing table elements\n\n  There are a "
  },
  {
    "path": "lib/bolt_sips/socket.ex",
    "chars": 1056,
    "preview": "defmodule Bolt.Sips.Socket do\n  @moduledoc \"\"\"\n  A default socket interface used to communicate to a Neo4j instance.\n\n  "
  },
  {
    "path": "lib/bolt_sips/types.ex",
    "chars": 19024,
    "preview": "defmodule Bolt.Sips.Types do\n  @moduledoc \"\"\"\n  Basic support for representing nodes, relationships and paths belonging "
  },
  {
    "path": "lib/bolt_sips/types_helper.ex",
    "chars": 1462,
    "preview": "defmodule Bolt.Sips.TypesHelper do\n  @doc \"\"\"\n  Decompose an amount seconds into the tuple {hours, minutes, seconds}\n  \""
  },
  {
    "path": "lib/bolt_sips/utils.ex",
    "chars": 6360,
    "preview": "defmodule Bolt.Sips.Utils do\n  @moduledoc false\n  # Common utilities\n\n  @default_hostname \"localhost\"\n  @default_bolt_po"
  },
  {
    "path": "lib/bolt_sips.ex",
    "chars": 9328,
    "preview": "defmodule Bolt.Sips do\n  @moduledoc \"\"\"\n  A Neo4j driver for Elixir providing many useful features:\n\n  - using the Bolt "
  },
  {
    "path": "lib/mix/tasks/cypher.ex",
    "chars": 2049,
    "preview": "defmodule Mix.Tasks.Bolt.Cypher do\n  use Mix.Task\n\n  @shortdoc \"Execute a Cypher command\"\n  @recursive true\n\n  @moduledo"
  },
  {
    "path": "mix.exs",
    "chars": 3471,
    "preview": "defmodule BoltSips.Mixfile do\n  use Mix.Project\n\n  @version \"2.1.0\"\n  @url_docs \"https://hexdocs.pm/bolt_sips\"\n  @url_gi"
  },
  {
    "path": "requirements.txt",
    "chars": 48,
    "preview": "boto==2.48.0\ncertifi\nclick<8,>=7\ndocker\nurllib3\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_all_bolt_version_test.exs",
    "chars": 3441,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolAllBoltVersionTest do\n  use Bolt.Sips.InternalCase\n  alias Bolt.Sips.Internals"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_bolt_v1_test.exs",
    "chars": 1751,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolV1Test do\n  use Bolt.Sips.InternalCase\n  @moduletag :bolt_v1\n  alias Bolt.Sips"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_bolt_v2_test.exs",
    "chars": 7233,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtoolBoltV2Test do\n  use Bolt.Sips.InternalCase\n  @moduletag :bolt_v2\n\n  alias Bolt."
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_bolt_v3_test.exs",
    "chars": 8889,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolBoltV3Test do\n  use ExUnit.Case, async: true\n  @moduletag :bolt_v3\n\n  alias Bo"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_v1_test.exs",
    "chars": 6548,
    "preview": "defmodule BoltProtocolV1.Sips.Internals.BoltProtocolV1Test do\n  use ExUnit.Case, async: true\n  @moduletag :bolt_v1\n\n  al"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_v3_test.exs",
    "chars": 7899,
    "preview": "defmodule Bolt.Sips.Internals.BoltProtocolV3Test do\n  use ExUnit.Case, async: true\n  @moduletag :bolt_v3\n\n  alias Bolt.S"
  },
  {
    "path": "test/bolt_sips/internals/bolt_version_helper_test.exs",
    "chars": 663,
    "preview": "defmodule Bolt.Sips.Internals.BoltVersionHelperTest do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.Internals.Bol"
  },
  {
    "path": "test/bolt_sips/internals/logger_test.exs",
    "chars": 760,
    "preview": "defmodule Bolt.Sips.Internals.LoggerTest do\n  use ExUnit.Case\n  import ExUnit.CaptureLog\n\n  alias Bolt.Sips.Internals.Lo"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/decoder_test.exs",
    "chars": 10381,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.Pack"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/decoder_v1_test.exs",
    "chars": 5898,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderV1Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.Pa"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/decoder_v2_test.exs",
    "chars": 5371,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.DecoderV2Test do\n  use ExUnit.Case, async: true\n  alias Bolt.Sips.Internals.Pac"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_helper_test.exs",
    "chars": 1043,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderHelperTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internal"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_test.exs",
    "chars": 6480,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderTest do\n  use ExUnit.Case, async: false\n\n  alias Bolt.Sips.Internals.Pac"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_v1_test.exs",
    "chars": 4765,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderV1Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.Pa"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_v2_test.exs",
    "chars": 4144,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.EncoderV2Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.Pa"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/decoder_test.exs",
    "chars": 2195,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.DecoderTest do\n  use ExUnit.Case, async: true\n  alias Bolt.Sips.Interna"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/encoder_test.exs",
    "chars": 9262,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderTest do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.Inte"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/encoder_v1_test.exs",
    "chars": 2152,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV1Test do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.In"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/encoder_v3_test.exs",
    "chars": 3911,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV3Test do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.In"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message_test.exs",
    "chars": 7094,
    "preview": "defmodule Bolt.Sips.Internals.PackStream.MessageTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.Pack"
  },
  {
    "path": "test/bolt_sips/metadata_test.exs",
    "chars": 2374,
    "preview": "defmodule Bolt.Sips.MetadataTest do\n  use ExUnit.Case, async: true\n  alias Bolt.Sips.Metadata\n\n  @valid_metadata %{\n    "
  },
  {
    "path": "test/bolt_sips/performance_test.exs",
    "chars": 1899,
    "preview": "defmodule Bolt.Sips.PerformanceTest do\n  use Bolt.Sips.ConnCase, async: false\n\n  setup(%{conn: conn} = context) do\n    B"
  },
  {
    "path": "test/bolt_sips/protocol_test.exs",
    "chars": 1118,
    "preview": "defmodule Bolt.Sips.ProtocolTest do\n  use ExUnit.Case, async: false\n\n  alias Bolt.Sips.Protocol\n\n  # Transactions are no"
  },
  {
    "path": "test/bolt_sips/response_encoder/json_implementations_test.exs",
    "chars": 4375,
    "preview": "defmodule Bolt.Sips.JsonImplementationsTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Types.{\n    DateTimeWit"
  },
  {
    "path": "test/bolt_sips/response_encoder/json_test.exs",
    "chars": 4527,
    "preview": "defmodule Bolt.Sips.ResponseEncode.JsonTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Types.{\n    DateTimeWit"
  },
  {
    "path": "test/bolt_sips/response_encoder_test.exs",
    "chars": 115,
    "preview": "defmodule Bolt.Sips.ResponseEncoderTest do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.ResponseEncoder\nend\n"
  },
  {
    "path": "test/bolt_sips/types_helpers_test.exs",
    "chars": 1622,
    "preview": "defmodule Bolt.Sips.TypesHelperTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.TypesHelper\n\n  describe \"decomp"
  },
  {
    "path": "test/bolt_sips/types_test.exs",
    "chars": 5173,
    "preview": "defmodule Bolt.Sips.TypesTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Types.{DateTimeWithTZOffset, Duration"
  },
  {
    "path": "test/boltkit_test.exs",
    "chars": 1369,
    "preview": "defmodule Bolt.Sips.BoltStubTest do\n  @moduledoc \"\"\"\n  !!Remember!!\n    you cannot reuse the boltstub across the tests, "
  },
  {
    "path": "test/config_test.exs",
    "chars": 3079,
    "preview": "defmodule Config.Test do\n  use ExUnit.Case\n  alias Bolt.Sips.Utils\n\n  doctest Bolt.Sips\n\n  @graphenedb_like_url \"bolt://"
  },
  {
    "path": "test/errors_test.exs",
    "chars": 1281,
    "preview": "defmodule ErrorsTest do\n  @moduledoc \"\"\"\n  every new error, and related tests\n  \"\"\"\n  use ExUnit.Case, async: true\n\n  @s"
  },
  {
    "path": "test/invalid_param_type_test.exs",
    "chars": 596,
    "preview": "defmodule Bolt.Sips.InvalidParamType.Test do\n  use ExUnit.Case\n\n  setup_all do\n    Bolt.Sips.ConnectionSupervisor.connec"
  },
  {
    "path": "test/one_test.exs",
    "chars": 1418,
    "preview": "defmodule One.Test do\n  # use Bolt.Sips.RoutingConnCase\n  # @moduletag :routing\n\n  # # alias Bolt.Sips.{Success, Error, "
  },
  {
    "path": "test/query_bolt_v2_test.exs",
    "chars": 4205,
    "preview": "defmodule Bolt.Sips.QueryBoltV2Test do\n  use Bolt.Sips.ConnCase, async: true\n  @moduletag :bolt_v2\n\n  alias Bolt.Sips.Ty"
  },
  {
    "path": "test/query_test.exs",
    "chars": 13440,
    "preview": "defmodule Query.Test do\n  use Bolt.Sips.ConnCase, async: true\n\n  alias Query.Test\n  alias Bolt.Sips.Test.Support.Databas"
  },
  {
    "path": "test/response_test.exs",
    "chars": 13918,
    "preview": "defmodule ResponseTest do\n  use ExUnit.Case\n\n  alias Bolt.Sips.Response\n  # import ExUnit.CaptureLog\n\n  @explain [\n    s"
  },
  {
    "path": "test/router_test.exs",
    "chars": 3057,
    "preview": "defmodule Bolt.Sips.Routing.RouterTest do\n  use ExUnit.Case\n  doctest Bolt.Sips.Router\n\n  alias Bolt.Sips.Response\n\n  # "
  },
  {
    "path": "test/routing/connections_test.exs",
    "chars": 1223,
    "preview": "defmodule Bolt.Sips.Routing.ConnectionsTest do\n  use ExUnit.Case, async: true\n  @moduletag :routing\n\n  alias Bolt.Sips.R"
  },
  {
    "path": "test/routing/crud_test.exs",
    "chars": 2035,
    "preview": "defmodule Bolt.Sips.Routing.CrudTest do\n  use Bolt.Sips.RoutingConnCase\n  @moduletag :routing\n\n  alias Bolt.Sips\n\n  desc"
  },
  {
    "path": "test/routing/routing_table_parser_test.exs",
    "chars": 2282,
    "preview": "defmodule Routing.Routing.TableParserTest do\n  use ExUnit.Case, async: true\n  @moduletag :routing\n\n  alias Bolt.Sips.Rou"
  },
  {
    "path": "test/routing/routing_test.exs",
    "chars": 4413,
    "preview": "defmodule Bolt.Sips.RoutingTest do\n  @moduledoc \"\"\"\n\n  \"\"\"\n  use Bolt.Sips.BoltKitCase, async: false\n\n  alias Bolt.Sips."
  },
  {
    "path": "test/routing/transaction_test.exs",
    "chars": 3376,
    "preview": "defmodule Bolt.Sips.Routing.TransactionTest do\n  use Bolt.Sips.RoutingConnCase\n  @moduletag :routing\n\n  setup do\n    {:o"
  },
  {
    "path": "test/scripts/count.bolt",
    "chars": 297,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nC: RUN \"UNWIND range(1, 10) AS n RETURN n\" {}\nS: SUCCESS {\"fields\": [\"n\"]}\n"
  },
  {
    "path": "test/scripts/create_a.script",
    "chars": 116,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CREATE (a $x)\" {\"x\": {\"name\": \"Alice\"}}\nS: SUCCESS {\"fields\": []}\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/forbidden_on_read_only_database.script",
    "chars": 273,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO DISCARD_ALL\n!: AUTO RUN \"ROLLBACK\" {}\n!: AUTO RUN \"BEGIN\" {}\n!: AUTO RUN \"COMMIT\" {}\n"
  },
  {
    "path": "test/scripts/get_routing_table.script",
    "chars": 598,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nS: SUCCESS {\"server\": \"Neo4j/3.2.3\"}\nC: RUN \"CALL dbms.cluster.routing.getR"
  },
  {
    "path": "test/scripts/get_routing_table_with_context.script",
    "chars": 446,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nS: SUCCESS {\"server\": \"Neo4j/3.2.3\"}\nC: RUN \"CALL dbms.cluster.routing.getR"
  },
  {
    "path": "test/scripts/non_router.script",
    "chars": 233,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\nS: FAILURE {\"co"
  },
  {
    "path": "test/scripts/return_1.script",
    "chars": 113,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"RETURN $x\" {\"x\": 1}\nS: SUCCESS {\"fields\": [\"x\"]}\n   RECORD [1]\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/return_1_in_tx_twice.script",
    "chars": 595,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"BEGIN\" {}\nS: SUCCESS {\"fields\": []}\n   SUCCESS {}\n\nC: RUN \"RETURN 1\" {}\nS: SUCCESS {"
  },
  {
    "path": "test/scripts/return_1_twice.script",
    "chars": 199,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"RETURN $x\" {\"x\": 1}\nS: SUCCESS {\"fields\": [\"x\"]}\n   RECORD [1]\n   SUCCESS {}\n\nC: RUN"
  },
  {
    "path": "test/scripts/return_x.bolt",
    "chars": 129,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nC: RUN \"RETURN $x\" {\"x\": 1}\nS: SUCCESS {\"fields\": [\"x\"]}\n   RECORD [1]\n   S"
  },
  {
    "path": "test/scripts/router.script",
    "chars": 374,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\nS: SUCCESS {\"fi"
  },
  {
    "path": "test/scripts/router_no_readers.script",
    "chars": 353,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\n   PULL_ALL\nS: "
  },
  {
    "path": "test/scripts/router_no_writers.script",
    "chars": 370,
    "preview": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\n   PULL_ALL\nS: "
  },
  {
    "path": "test/support/boltkit_case.ex",
    "chars": 2946,
    "preview": "defmodule Bolt.Sips.BoltKitCase do\n  _doc = \"\"\"\n  tag your tests with `boltkit`, like this:\n\n      @tag boltkit: %{\n    "
  },
  {
    "path": "test/support/conn_case.ex",
    "chars": 277,
    "preview": "defmodule Bolt.Sips.ConnCase do\n  use ExUnit.CaseTemplate\n\n  setup_all do\n    Bolt.Sips.start_link(Application.get_env(:"
  },
  {
    "path": "test/support/conn_routing_case.ex",
    "chars": 678,
    "preview": "defmodule Bolt.Sips.RoutingConnCase do\n  @moduletag :routing\n\n  use ExUnit.CaseTemplate\n\n  alias Bolt.Sips\n\n  @routing_c"
  },
  {
    "path": "test/support/database.ex",
    "chars": 132,
    "preview": "defmodule Bolt.Sips.Test.Support.Database do\n  def clear(conn) do\n    Bolt.Sips.query!(conn, \"MATCH (n) DETACH DELETE n\""
  },
  {
    "path": "test/support/fixture.ex",
    "chars": 30500,
    "preview": "defmodule Bolt.Sips.Fixture do\n  def create_graph(conn, :movie) do\n    Bolt.Sips.query!(conn, movie_cypher())\n  end\n\n  d"
  },
  {
    "path": "test/support/internal_case.ex",
    "chars": 1105,
    "preview": "defmodule Bolt.Sips.InternalCase do\n  use ExUnit.CaseTemplate\n\n  alias Bolt.Sips.Internals.BoltProtocol\n\n  setup do\n    "
  },
  {
    "path": "test/test_helper.exs",
    "chars": 1176,
    "preview": "Logger.configure(level: :debug)\nExUnit.start(capture_log: true, assert_receive_timeout: 500, exclude: [:skip, :bench, :a"
  },
  {
    "path": "test/test_large_param_set.exs",
    "chars": 741,
    "preview": "defmodule Large.Param.Set.Test do\n  use ExUnit.Case\n  doctest Bolt.Sips\n\n  setup_all do\n    {:ok, [conn: Bolt.Sips.conn("
  },
  {
    "path": "test/test_support.exs",
    "chars": 48,
    "preview": "defmodule TestSupport do\n  @moduledoc false\nend\n"
  },
  {
    "path": "test/transaction_test.exs",
    "chars": 3831,
    "preview": "defmodule Transaction.Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Response\n\n  setup do\n    {:ok, [main_con"
  }
]

About this extraction

This page contains the full source code of the florinpatrascu/bolt_sips GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 153 files (507.4 KB), approximately 159.5k tokens, and a symbol index with 452 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!