Commit Graph

148 Commits

Author SHA1 Message Date
Jon Parise
815029d1ca
Move Thrift.Parser.Models to Thrift.AST (#346)
This moves the nested Models into a more distinct top-level namespace.
There aren't any functional changes here, but it prefaces future work to
better define the parsed (AST) representation of a Thrift file.
2018-06-13 12:11:11 -07:00
Michael Oliver
45ce5a6e60
Handle special floats like NaN, inf, -inf (#329)
* Handle special floats like NaN, inf, -inf

* Add NaN struct

* Write large number with underscores

* Move Thrift.NaN and remove unnecessary function clause

* Follow consistent code style
2018-05-16 01:38:44 -07:00
Michael Oliver
f26f5e78b0
Add option for allowing both SSL and plain connections on same port (#341)
* Add option for allowing both SSL and plain connections on same port

* PR Feedback
2018-05-11 12:20:25 -07:00
Jon Parise
a30f6434cb
Support comment-based namespace declarations (#340)
Thrift uses the `namespace` keyword to define a language's namespace.

    namespace elixir Thrift.Namespace

Unfortunately, the Apache Thrift compiler will produce a warning on this
line because it doesn't recognize `elixir` as a supported language.
While that warning is benign, it can be annoying. For that reason, you
can also specify your Elixir namespace as a "magic" namespace comment:

    #@namespace elixir Thrift.Namespace

This alternate syntax is borrowed from Scrooge, which uses the same
trick for defining `scala` namespaces.
2018-05-10 10:33:09 -07:00
Jon Parise
91651ff4e8
Test the SSL.configuration/1 error path (#339) 2018-05-05 19:15:42 -07:00
James Fish
1ed4b894a5
Support SSL/TLS (#336) 2018-05-02 15:47:49 -07:00
Jon Parise
ce8a0e6daa
Raise ConnectionError for bang function errors (#320)
The previous code attempted to raise an error atom as an exception.
Instead, wrap that error reason in a new ConnectionError exception.
2018-05-02 09:40:48 -07:00
Li Xiaobin
129b9f3a69 fix test failed on windows (#330) 2018-04-02 07:53:47 -07:00
Michael Oliver
cc37cb4dd5
Fix bug with enum numbering (#328)
* Fix bug with enum numbering

* Be quiet ebert

* Start enum numbering from 0
2018-03-04 19:00:35 -08:00
James Fish
0a298c9b9c
Avoid redefining generated in memory only modules (#326) 2018-02-17 17:04:50 -08:00
Michael Oliver
7033f56acf
Fix gen_server_opts not being passed to GenServer (#324)
* Fix gen_server_opts not being passed to GenServer

* Make gen_server_opts top-level options
2018-02-06 17:38:20 -08:00
James Fish
56e4f9b14e
Improve log handling in tests and binary server (#321)
Previously all backends were removed which would hide all logs. This
is quite inconvenient if a test fails. Capture log will only show
output for failed tests (grouped and inline with each test failure).

Do not log expected errors (tcp socket will always close eventually).
2018-01-30 10:48:47 -08:00
Jon Parise
3057d5e1af
Support Elixir 1.6 setup_all context change (#318)
Elixir 1.6's ExUnit implementation changed such that setup_all's
`context` now contains a `:module` key instead of a `:case` key.
Backwards compatibility was intended, but it looks like the ExUnit
runner now only passes `:module`.
2018-01-29 12:17:17 -08:00
Jon Parise
50c575831e
Switch to using "Error" as our exception suffix (#314)
This is more consistent with Elixir's own naming conventions. It also
removes a small amount of ambiguity with code that refers to Thrift's
exceptions; that is, things named "exceptions" in this library are
nearly all Thrift exceptions.
2018-01-10 13:31:49 -08:00
Jon Parise
82396f6da5
Improve the oneway function client-server test (#310)
This test has been flaky because it relies on race conditions.

Force synchronization using :sys.get_state/1 and remove
assertions that can't be guaranteed.
2018-01-08 10:45:48 -08:00
Jon Parise
116fe03eef
Remove with _with_options function variants (#308)
These were introduced to avoid potential conflicts between our options
parameter (named `opts`) and an `opts` function paramter named by the
Thrift IDL.

This resulted in doubling the number of generated methods (as well as
another level of function call indirection), in addition to making the
resulting user-callable API a little bit more complex.

Instead, go back to using just two generated functions (the "regular"
call and the "bang!" exception-raising variant) and rename our options
parameter to `rpc_opts`, which is very unlikely to conflict in practice.
2018-01-02 12:42:03 -08:00
Jon Parise
31d73b6d0f
Timeouts should also disconnect to close socket (#306) 2017-12-31 15:11:02 -08:00
Jon Parise
f2c14c09a7
Use any free port for our test server process (#307)
Passing 0 as the port tells the TCP subsystem to select any free port,
and we can querying the server's listening port via a {:get, :port}
call.

This lets us drop the random_port() helper function and makes things
simpler overall.
2017-12-31 13:49:03 -08:00
Jon Parise
1a1344aef2
Stop special-casing the {:error, :closed} case (#304)
We can instead reply immediately (with the existing :disconnect case)
and let the client send the :EXIT.
2017-12-31 12:21:26 -08:00
Jon Parise
c8f12b1047
Remove the client-side retry logic (#303)
In addition to the moderate state-management complexity introduced by
this feature, we've also found that implementing message-level retry
logic directly within a Thrift client to not be ideal. Instead, retries
are better implemented by either application logic (which has complete
understanding of the message payloads, idempotence, etc.) or closer to
the wire level, which can make stronger guarantees about whether the
original message was or was not delivered.

We also no longer attempt to reconnect from the disconnect/2 path.
Instead, we :stop with a clear error reason.
2017-12-31 10:07:56 -08:00
Jon Parise
ca68f8ea11
Support multiple destination module name casings (#299)
Previously, we expected the "struct name" portion of the input name to
be a suitable module name string (for Module.concat/1). Lowercased names
failed this assumption because they're not valid aliases.

This change reworks the destination module name processing to ensure
that the "struct name" string starts with an initial capitalize letter.

Fixes #297
2017-11-27 14:19:53 -08:00
Michael Oliver
89130e838a Handle reserved keywords (#295)
* Handle reserved keywords

[Similarly to apache/thrift](19baeefd8c/compiler/cpp/src/thrift/thriftl.ll (L273)), we disallow keywords being used in the Thrift IDL
However the keywords restriction only applies to exact-casing, so for example we must support "AND" as a valid enum. When down-casing this value, it conflicts with the Elixir keyword "and", and so using some macro magic we can get around this limitation.
Additionally, since we down-case method names too, similar logic applies when generating a client. The server implementation did not require any changes.

Fixes: #294

* Remove stray IO.inspect

* Add an 'error parsing' test case and fix typo
2017-10-17 11:45:11 -07:00
Jon Parise
2a4dd5a2df Enable credo for the test/ directory (#284)
This addresses nearly all of the warnings in the test files. It also
tweaks the LargeNumbers rule to only apply to numbers over 99999, which
helps avoid a lot of additional transformations.
2017-09-23 12:00:03 -04:00
Jon Parise
8b7091a500 Improve unpack_response/1 test coverage (#283)
This also includes additional commentary on the implementation as well
as a small optimization for the case where a void function doesn't throw
any exceptions.
2017-09-22 14:14:36 -07:00
Steve Cohen
3b605d1873 Typedefs in other modules weren't working properly (#275)
* Typedefs in other modules weren't working properly

Due to the way typedefs work (they're different than everything else),
resolution wasn't working properly on them, this commit adds proper
resolution so you can define them in remote modules.

Fixes #268
2017-09-20 08:20:32 -07:00
James Fish
1434ab9adb Check erlang thrift files generated (#278) 2017-09-12 13:03:39 -07:00
James Fish
c2e4fef7f3 Fix annotation test module name (#277) 2017-09-12 09:39:46 -07:00
Jon Parise
5519008f00 Add support for Thrift annotations (#276)
Annotations appear within parentheses as part of various Thrift IDL
types such as fields and structures. They can be used to hint the
compiler or inform runtime behavior; the specification is pretty
open-ended.

This change adds support for parsing annotations (whose presence
previously resulted in parse errors) and storing them on our parser
models.

We currently support annotations on:

 - Enums
 - Fields
 - Functions
 - Services
 - Structures, Unions, and Exceptions

We can parse annotations on typedefs but don't currently store them
anywhere due to the way we internally represent a schema's typedefs as a
simple map of strings to types. We can revisit that later, perhaps as
part of a broader typedef refactoring.
2017-09-12 07:20:35 -07:00
Jon Parise
711e5e5a94 Address Elixir 1.5 compilation warnings (#271)
- to_char_list/1 -> to_charlist/1
 - use for comprehensions in place of Enum.filter_map/3
2017-07-30 17:20:00 -07:00
Jon Parise
0f09360090 Clean up service test shutdown (#263)
Also, remove some unused variables.
2017-04-25 15:25:14 -07:00
Jon Parise
8fc39a292d Prefer try do .. after .. end for cleanup (#265)
This syntax is shorter, gurantees cleanup, and preserves stacktraces
(unlike re-raising).
2017-04-08 08:26:19 -07:00
James Fish
0ebb91e0aa Fix line numbers for thrift tests (#262)
Create/register tests inline in test module so that test line numbers
can be used with `mix test`.
2017-04-06 19:17:25 -07:00
Jon Parise
1a9293bfeb Revise TApplicationException (#259)
This brings TApplicationException more in line with Elixir's conventions
for creating exceptions. `exception/1` now expects a keyword list with a
:type and an optional :message value. :type can either be an integer or
one of our internal atoms (e.g. :invalid_protocol).

Only the atom form is stored internally. It can be lazily converted back
to an integer at serialization time using `type_id/1`. We don't store
the integer form up-front because it's only used in "exceptional" (ha!)
cases in the server code path.

The list of well-known exception types has been expanded based on the
current Apache Thrift 0.10.0 release (C++ implementation).

Lastly, TApplicationException now has dedicated unit test coverage.
2017-04-06 07:51:32 -07:00
Jon Parise
4954dee60c Constant modules now reuse suitable existing names (#257)
We were previously writing constants to modules named after their
originating files. This approach was unfortunately too naive because the
resulting module names could conflict with other generated modules that
are spelled the same but different only in letter case.

For example, for a file named `myservice.thrift`:

    const float PI = 3.14
    service MyService {}

We would previously write the constants to `myservice.Myservice`
(`myservice.ex`) and the service definition to `myservice.MyService`
(`my_service.ex`). We now write both to `myservice.MyService` (thanks to
the generator's constants-merging logic) and no longer run into module
name conflicts.
2017-03-28 13:03:14 -07:00
Jon Parise
70d6e66db7 Use "Thrift.Generated" as the default namespace (#255)
Previously, we defaulted to generating our output files in the root, but
that behavior is potentially dangerous because it could class with other
top-level module names.

It's still possible to write files to the root by specifying `""` as the
namespace.
2017-03-16 09:06:44 -07:00
Jon Parise
fe1ed8611c Remove the implicit field identifier warning (#252)
This is useful information, but we don't currently have a good way to
capture it for later display. As is, printing out these warnings as part
of the parsing path can product a bit of spurious output as part of
normal operations because our mix compile task always parses .thrift
files to determine their build targets.
2017-03-14 19:58:39 -07:00
Jon Parise
6872e42fec Allow the default namespace to be nil (#251)
Change #228 allowed the default namespace to be either an atom or a
string, but we still want to special-case the `nil` atom to mean "no
default namespace". Otherwise, we get a namespace named "Nil".
2017-03-14 10:26:52 -07:00
Steve Cohen
ab561e2558 Fixed Global resolution (#253)
We had the concept of global resolution, that is, we would place the
current module's definitions in a global scope. This would have worked,
but it wasn't deterministic.

Now, we tell the file group which module is local, and have it update
its resolutions with only the local definitions when we build their
data. This limits the scope of global variables.
2017-03-13 09:52:58 -07:00
Jon Parise
11810e5999 Deterministically merge FileGroup resolutions (#250)
This is an incomplete solution to the fact that our existing resolution
merge was non-deterministic with regard to non-qualified names. We now
always take the newer value for a conflicting key, which gets us closer
to the expected behavior.

We unfortunately still aren't implementing the correct scoping rules.
For example, this still allows a non-qualified name to "leak" from one
included file into another. This should be revisited and addressed with
a more complete solution.
2017-03-07 09:01:35 -08:00
Jon Parise
a73154157b Use negative values for implicit field indices (#249)
The Apache Thrift compiler auto-assigns negative values (starting at -1)
to fields with implicit indices. This changes our code to do the same.
Because field identifiers are serialized on the wire, it's important for
our implementations to match in this regard.

This also moves our implicit identifier warning directly into the
parser. This was the only warning being generated from the higher level
Thrift.Parser code; all other error conditions raise exceptions. This
also lets us remove the now-unused Thrift.Parser.Shell module.

The only downside to moving the warning into the parser is that we lose
a little context (the name of the parent struct), but because we now
include a line number and this is such a rare case, this feels like a
reasonable trade-off.
2017-03-03 09:38:11 -08:00
Jon Parise
6fc7e069ac Parsed models now have line numbers (#248)
This change extracts line numbers from the lexer's tokens and assigns
them to our model structures for later use in error messages or for
debugging.

This change also generalizes model construction by introducing
build_model/2 and build_model/3.
2017-03-01 13:49:21 -08:00
Jon Parise
48e87e627a Improve the grammar's Thrift IDL compliance (#245)
This is a near complete rewrite of our yecc grammar. It includes the
following changes:

1. Our symbols now follow Erlang's TitleCase naming convention. This
   makes them easier to differentiate from literals and tokens.
2. We no longer support set constants because they aren't supported by
   the Apache Thrift compiler.
3. We no longer support parens around a service definition's "extends"
   clause. That was introduced by mistake and isn't part of the official
   Thrift IDL specification.
4. We now support both `,` and `;` as list separators. We previously
   only supported ',' while ';' was discarded by the tokenizer.
2017-02-28 14:03:01 -08:00
Steve Cohen
1168b29d40 Connections couldn't be closed (#244)
* Connections couldn't be closed

Previously, connections would reconnect if they were closed by calling
`Client.close`. This is bad behavior, and could easily lead to running
out of sockets. `Client.close` now closes the TCP connection and stops
the process.
2017-02-27 13:19:55 -08:00
Jon Parise
7fd72cdfb4 Simplify the lexer's rules (#243)
There are multiple of small changes here that result in a large
simplification of the lexer's rules:

1. Many rules that were mapping literals to atoms have been collapsed in
   favor of generic rules that call list_to_atom/1 on TokenChars.
2. Quoted string processing now uses a simpler regular expression paired
   with a processing function, giving us support for character escape
   sequences.
3. The st_ident token type has been removed because Smalltalk-specific
   IDL support was deprecated a long time ago by Apache Thrift and we
   don't generate Smalltalk namespaces in our library.

Also, the lexer unit test now verifies that we can tokenize a complete
document.
2017-02-27 11:41:07 -08:00
Jon Parise
bd4f524bd6 Implement Exception.message/1 for exceptions (#242)
We generate Elixir exception structs via defexception for Thrift
exception types. That macro always provides an exception/1
implementation, but message/1 is only generated if the exception is
defined with a :message field. When it doesn't, the generated code
produces a warning because the full Exception behaviour hasn't been
implemented.

This change generates a message/1 implementation for all of our Thrift
exception structs which don't have a natural :message field. It returns
the Kernel.inspect/2 string representation of the exception, which is
both simple and unambiguous.
2017-02-16 13:58:30 -08:00
Jon Parise
359d70a1cb Maintain a manifest of generated files (#234)
This is signifiant rewrite of the compiler task to support manifests,
making us a better mix citizen and adding support for `mix clean`-based
file cleanup.

Our manifest is written as a binary-encoded Erlang term to give us the
most forward-compatible file format. At the moment, we only store a
manifest version number and the list of generated files, but we may
expand this in the future to include additional dependency information
such as the "version" of the code that was used to generate these files.

See #138 for a more detailed discussion.
2017-02-11 18:32:30 -08:00
Jon Parise
2b4479a79b Update remaining integers to use signed values (#237)
This continues the work started in #233 to address #206.
2017-02-11 18:31:57 -08:00
Steve Cohen
af41860c95 Serializer / Deserializer do not respect signedness (#233)
We were using size(x) rather than x-signed; apparently, size(x) is for
unsigned numbers.

Fixed, and added unit tests for boundary conditions. Fixes #206
2017-02-11 13:55:14 -07:00
Dan Swain
282374481d Do not generate a module with no code 2017-02-10 17:06:02 -05:00
Dan Swain
9bb49b3a2d Merge branch 'thrift_tng' into incursion 2017-02-10 16:51:40 -05:00