├── .gitignore ├── ChangeLog.md ├── LICENSE ├── README.md ├── Setup.hs ├── app └── Main.hs ├── example ├── README.md ├── frontend │ ├── .gitignore │ ├── README.md │ ├── __tests__ │ │ └── Exported │ │ │ └── SharedTypes_spec.ml │ ├── bsconfig.json │ ├── build │ │ ├── Index.js │ │ └── index.html │ ├── package.json │ ├── src │ │ ├── Exported │ │ │ ├── SharedTypes.bs.js │ │ │ ├── SharedTypes.ml │ │ │ └── SharedTypes.mli │ │ ├── Index.bs.js │ │ ├── Index.re │ │ ├── ServerFetch.bs.js │ │ ├── ServerFetch.re │ │ ├── ServerFetchConfig.bs.js │ │ ├── ServerFetchConfig.re │ │ ├── UserSelector.bs.js │ │ ├── UserSelector.re │ │ └── index.html │ ├── webpack.config.js │ └── yarn.lock ├── server │ ├── .gitignore │ ├── ChangeLog.md │ ├── LICENSE │ ├── README.md │ ├── Setup.hs │ ├── app │ │ └── Main.hs │ ├── package.yaml │ ├── src │ │ └── Server.hs │ ├── stack.yaml │ ├── static │ │ ├── Index.js │ │ └── index.html │ └── test │ │ └── Spec.hs ├── shared-types-re │ ├── .gitignore │ ├── ChangeLog.md │ ├── LICENSE │ ├── README.md │ ├── Setup.hs │ ├── app │ │ └── Main.hs │ ├── handwritten │ │ ├── Entity.ml │ │ └── Entity.mli │ ├── package.yaml │ ├── src │ │ └── Shared │ │ │ └── Types │ │ │ └── Reason │ │ │ ├── Package.hs │ │ │ └── Types.hs │ ├── stack.yaml │ └── test │ │ ├── Spec.hs │ │ └── golden │ │ ├── Entity │ │ └── Entity.json │ │ ├── Key │ │ └── Key.json │ │ ├── Todo │ │ └── Todo.json │ │ ├── TodoId │ │ └── TodoId.json │ │ ├── User │ │ └── User.json │ │ ├── UserId │ │ └── UserId.json │ │ └── Username │ │ └── Username.json └── shared-types │ ├── .gitignore │ ├── ChangeLog.md │ ├── LICENSE │ ├── README.md │ ├── Setup.hs │ ├── app │ └── Main.hs │ ├── package.yaml │ ├── src │ └── Shared │ │ └── Types.hs │ ├── stack.yaml │ └── test │ └── Spec.hs ├── jenkins.sh ├── package.yaml ├── scratch.txt ├── src └── OCaml │ ├── BuckleScript │ ├── Decode.hs │ ├── Encode.hs │ ├── Internal │ │ ├── Module.hs │ │ ├── Package.hs │ │ └── Spec.hs │ ├── Record.hs │ ├── Spec.hs │ └── Types.hs │ ├── Export.hs │ └── Internal │ └── Common.hs ├── stack.yaml ├── test.sh └── test ├── Dependency.hs ├── File.hs ├── FileApp.hs ├── Options.hs ├── Product.hs ├── ProductApp.hs ├── Spec.hs ├── Sum.hs ├── SumApp.hs ├── Util.hs ├── interface └── golden │ ├── __tests__ │ ├── file-servant │ │ └── File_spec.ml │ ├── file │ │ └── File_spec.ml │ ├── product-servant │ │ ├── Card_spec.ml │ │ ├── Company_spec.ml │ │ ├── ComplexProduct_spec.ml │ │ ├── CustomOption_spec.ml │ │ ├── OneTypeParameter_spec.ml │ │ ├── Person_spec.ml │ │ ├── SimpleChoice_spec.ml │ │ ├── SubTypeParameter_spec.ml │ │ ├── ThreeTypeParameters_spec.ml │ │ ├── TwoTypeParameters_spec.ml │ │ ├── UnnamedProduct_spec.ml │ │ └── Wrapper_spec.ml │ ├── product │ │ ├── Box_spec.ml │ │ ├── Card_spec.ml │ │ ├── Company_spec.ml │ │ ├── ComplexProduct_spec.ml │ │ ├── CustomOption_spec.ml │ │ ├── Key_spec.ml │ │ ├── OneTypeParameter_spec.ml │ │ ├── Person_spec.ml │ │ ├── SimpleChoice_spec.ml │ │ ├── SubTypeParameter_spec.ml │ │ ├── ThreeTypeParameters_spec.ml │ │ ├── TwoTypeParameters_spec.ml │ │ ├── UnnamedProduct_spec.ml │ │ └── Wrapper_spec.ml │ ├── sum-servant │ │ ├── NameOrIdNumber_spec.ml │ │ ├── NewType_spec.ml │ │ ├── OnOrOff_spec.ml │ │ ├── Result_spec.ml │ │ ├── SingleSum_spec.ml │ │ ├── SumVariant_spec.ml │ │ ├── SumWithRecord_spec.ml │ │ └── WithTuple_spec.ml │ └── sum │ │ ├── NameOrIdNumber_spec.ml │ │ ├── NewType_spec.ml │ │ ├── OnOrOff_spec.ml │ │ ├── Result_spec.ml │ │ ├── SingleSum_spec.ml │ │ ├── SumVariant_spec.ml │ │ ├── SumWithRecord_spec.ml │ │ └── WithTuple_spec.ml │ ├── bsconfig.json │ ├── file │ ├── File.ml │ └── File.mli │ ├── golden │ ├── dependency │ │ └── Class │ │ │ └── Class.json │ ├── file │ │ ├── AutoDependingOnManual │ │ │ └── AutoDependingOnManual.json │ │ ├── Automobile │ │ │ └── Automobile.json │ │ ├── Business │ │ │ └── Business.json │ │ ├── NonGenericType │ │ │ └── NonGenericType.json │ │ ├── Person │ │ │ └── Person.json │ │ └── Wrapper │ │ │ └── Wrapper.json │ ├── product │ │ ├── AuditAction │ │ │ ├── Create.json │ │ │ ├── Delete.json │ │ │ └── Update.json │ │ ├── AuditModel │ │ │ └── AuditModel.json │ │ ├── Card │ │ │ └── Card.json │ │ ├── Company │ │ │ └── Company.json │ │ ├── Company2 │ │ │ └── Company2.json │ │ ├── ComplexProduct │ │ │ └── ComplexProduct.json │ │ ├── ComplexWrapped │ │ │ └── ComplexWrapped.json │ │ ├── EitherWrapped │ │ │ └── EitherWrapped.json │ │ ├── HalfWrapped │ │ │ └── HalfWrapped.json │ │ ├── InnerBox │ │ │ └── InnerBox.json │ │ ├── IntWrapped │ │ │ └── IntWrapped.json │ │ ├── MaybeWrapped │ │ │ └── MaybeWrapped.json │ │ ├── OneTypeParameter │ │ │ └── OneTypeParameter.json │ │ ├── OuterBox │ │ │ └── OuterBox.json │ │ ├── PartiallyWrapped │ │ │ └── PartiallyWrapped.json │ │ ├── Person │ │ │ └── Person.json │ │ ├── ScrambledTypeParameterRefs │ │ │ └── ScrambledTypeParameterRefs.json │ │ ├── SecondLayer │ │ │ └── SecondLayer.json │ │ ├── SecondLayerFilled │ │ │ └── SecondLayerFilled.json │ │ ├── Simple │ │ │ └── Simple.json │ │ ├── SimpleChoice │ │ │ └── SimpleChoice.json │ │ ├── SqlKey │ │ │ └── SqlKey.json │ │ ├── SubTypeParameter │ │ │ └── SubTypeParameter.json │ │ ├── Suit │ │ │ ├── Clubs.json │ │ │ ├── Diamonds.json │ │ │ ├── Hearts.json │ │ │ └── Spades.json │ │ ├── SumWrapped │ │ │ ├── SW1.json │ │ │ ├── SW2.json │ │ │ ├── SW3.json │ │ │ └── SW4.json │ │ ├── Three │ │ │ └── Three.json │ │ ├── TupleWrapped │ │ │ └── TupleWrapped.json │ │ ├── TwoTypeParameters │ │ │ └── TwoTypeParameters.json │ │ ├── UnnamedProduct │ │ │ └── UnnamedProduct.json │ │ ├── User │ │ │ └── User.json │ │ ├── UserAudit │ │ │ └── UserAudit.json │ │ ├── UserId │ │ │ └── UserId.json │ │ ├── WrapThree │ │ │ └── WrapThree.json │ │ ├── WrapThreeFilled │ │ │ └── WrapThreeFilled.json │ │ ├── WrapThreePartiallyFilled │ │ │ └── WrapThreePartiallyFilled.json │ │ ├── WrapThreeUnfilled │ │ │ └── WrapThreeUnfilled.json │ │ ├── WrappedWrapper │ │ │ └── WrappedWrapper.json │ │ ├── Wrapper │ │ │ └── Wrapper.json │ │ └── Wrapper2 │ │ │ └── Wrapper2.json │ ├── school │ │ └── School │ │ │ └── School.json │ ├── subs │ │ ├── A │ │ │ └── A.json │ │ ├── B │ │ │ └── B.json │ │ ├── C │ │ │ └── C.json │ │ ├── D │ │ │ └── D.json │ │ └── E │ │ │ └── E.json │ └── sum │ │ ├── NameOrIdNumber │ │ ├── IdNumber.json │ │ └── Name.json │ │ ├── NewType │ │ └── NewType.json │ │ ├── OnOrOff │ │ ├── Off.json │ │ └── On.json │ │ ├── Result │ │ ├── Error.json │ │ └── Success.json │ │ ├── SingleSum │ │ └── SingleSum.json │ │ ├── SumVariant │ │ ├── HasMixed.json │ │ ├── HasMultipleInts.json │ │ ├── HasMultipleTuples.json │ │ ├── HasNameOrIdNumber.json │ │ ├── HasNothing.json │ │ ├── HasSingleInt.json │ │ └── HasSingleTuple.json │ │ ├── SumWithRecord │ │ ├── A1.json │ │ └── B2.json │ │ └── WithTuple │ │ └── WithTuple.json │ ├── options │ ├── NameOrIdNumber.ml │ ├── NameOrIdNumber.mli │ ├── Person.ml │ └── Person.mli │ ├── package.json │ ├── person.json │ ├── product │ ├── Box.ml │ ├── Box.mli │ ├── Card.ml │ ├── Card.mli │ ├── Company.ml │ ├── Company.mli │ ├── ComplexProduct.ml │ ├── ComplexProduct.mli │ ├── CustomOption.ml │ ├── CustomOption.mli │ ├── Key.ml │ ├── Key.mli │ ├── OneTypeParameter.ml │ ├── OneTypeParameter.mli │ ├── Person.ml │ ├── Person.mli │ ├── SimpleChoice.ml │ ├── SimpleChoice.mli │ ├── SubTypeParameter.ml │ ├── SubTypeParameter.mli │ ├── ThreeTypeParameters.ml │ ├── ThreeTypeParameters.mli │ ├── TwoTypeParameters.ml │ ├── TwoTypeParameters.mli │ ├── UnnamedProduct.ml │ ├── UnnamedProduct.mli │ ├── Wrapper.ml │ └── Wrapper.mli │ ├── sum │ ├── NameOrIdNumber.ml │ ├── NameOrIdNumber.mli │ ├── NewType.ml │ ├── NewType.mli │ ├── OnOrOff.ml │ ├── OnOrOff.mli │ ├── Result.ml │ ├── Result.mli │ ├── SingleSum.ml │ ├── SingleSum.mli │ ├── SumVariant.ml │ ├── SumVariant.mli │ ├── SumWithRecord.ml │ ├── SumWithRecord.mli │ ├── WithTuple.ml │ └── WithTuple.mli │ ├── webpack.config.js │ └── yarn.lock └── ocaml ├── Business.ml ├── Business.mli ├── NonGenericType.ml ├── NonGenericType.mli ├── Person.ml ├── Person.mli ├── Simple.ml ├── Simple.mli ├── Wrapper.ml └── Wrapper.mli /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work 2 | node_modules 3 | .merlin 4 | lib 5 | \#*# 6 | .#* 7 | *.log 8 | test/nointerface/temp 9 | test/interface/temp 10 | *.cabal 11 | *.faulty.json 12 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Revision history for ocaml-export 2 | 3 | ## 0.14.0.0 -- 2019-04-22 4 | * Fix single enumerator encoding. Now matches aeson. 5 | 6 | ## 0.13.0.0 -- 2019-03-17 7 | * Expose TypeParameterRef constructors. They are useful in some cases. 8 | 9 | ## 0.12.0.0 -- 2018-12-28 10 | * Fix Decode record case when NamedConstructor is an OCamlRef, not a primitive. 11 | 12 | ## 0.11.0.0 -- 2018-12-22 13 | * Support record, encode, decode for type that has a type parameter filled with a non-primitive type (eg. newtype Z = Z (Maybe X)) 14 | 15 | ## 0.10.0.0 -- 2018-12-07 16 | 17 | * Support sum-type constructors with non-primitive types 18 | * Remove dodgy-imports warnings with latests wl.pprint text 19 | * addDependentFile for embedded files 20 | 21 | ## 0.9.0.0 -- 2018-06-03 22 | 23 | * Output BuckleScript code requires at least bs-platform 3.1.0. 24 | * Change output BuckleScript code and test code to use `Belt.Result.t` instead of `Js.Result.t`. `Js.Result.t` has been deprecated since bs-platform 3.1.0. 25 | * Add `OInt32` to `OCaml.BuckleScript.Types.OCamlPrimitive` to convert Haskell `Int32` to BuckleScript `int32`. 26 | 27 | ## 0.8.0.0 -- 2018-05-03 28 | 29 | * Support GHC 8.2.2 and drop support for previous GHC versions. 'base >= 4.10' is a requirement. 30 | * Replace 'typelits-witnesses' dependency with 'singletons'. 31 | * Remove use of Overlappable from 'HasEmbeddedFile'. Use 'Nat' flags instead. 32 | 33 | ## 0.7.0.0 -- 2018-02-14 34 | 35 | * Add 'runGoldenSpec' to automate Haskell side file checks. 36 | * Alter 'SpecOptions servantURL' to 'Maybe String'. If it is nothing then produce specs that use files, if Just then spec that uses servant and files. 37 | * Fix encoding for sum type with enumerator. 38 | 39 | ## 0.6.0.0 -- 2018-01-31 40 | 41 | * Properly convert Haskell types to OCaml types that have type parameters. 42 | 43 | * Fix cases when the order of type parameter declarations differs from the order they appear in right hand side of the type declaration. 44 | 45 | * Add 'template-haskell < 2.12.0.0' restriction. OCamlTypeInFile seems to break at runtime with version 2.12.0.0. 46 | 47 | ## 0.5.0.0 -- 2018-01-16 48 | 49 | * Fix 'OCaml.BuckleScript.Encode.renderRef' for OBool. 50 | 51 | ## 0.4.0.0 -- 2018-01-10 52 | 53 | * Add 'OCamlType' instance for ByteString. 54 | 55 | * Add 'HaskellTypeName' to support servant test server for types without Generic. You must manually provide the type's name as a symbol. 56 | 57 | ## 0.3.0.0 -- 2018-01-04 58 | 59 | * 'OCaml.BuckleScript.Encode.renderRef' now add parentheses when rendering: list, optional, either, pair, tuple3, tuple4, tuple5, tuple6. 60 | * Support 'HasEmbeddedFile' for '(:<|>)'. 61 | * 'HasDecoder' now unwraps all OCamlDatatype. 62 | 63 | ## 0.2.0.0 -- 2017-12-29 64 | 65 | * `HasDecoder` properly supports Haskell types like `data X = X String Int`, product type with multiple unnamed records. 66 | 67 | * Support `genericToOCamlDatatype` when one of the type's dependencies implements instance OCamlType without `genericToOCamlDatatype`. 68 | 69 | ## 0.1.1.0 -- 2017-12-19 70 | 71 | * Remove `OCaml.File`. It was unexported, but was causing import errors because it depended on unincluded data files. 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright James M.C. Haver II (c) 2017 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of James M.C. Haver II nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ocaml-export 4 | 5 | - Export Haskell types to OCaml BuckleScript. 6 | - Export Haskell aeson serializations to OCaml BuckleScript using bs-aeson. 7 | - Automate integration tests between OCaml BuckleScript and Haskell using bs-aeson-spec, quickcheck-arbitrary-adt, hspec-golden-aeson and servant. 8 | 9 | ## Test 10 | 11 | ``` 12 | bash test.sh 13 | ``` 14 | 15 | ## Todo 16 | 17 | Complete before stablizing and pushing to version 1.0.0.0. 18 | 19 | - Documentation 20 | - Examples 21 | - readthedocs 22 | - More tests. 23 | - Haskell list as OCaml array. 24 | - Support (Map String ..) (Map Text ..) (Map Int ..), must provide one decoder. 25 | - Support (Map x ..) but two encoders/decoders must be provided. 26 | - Resolve dependency order for output code so you are not required to declare 'OCamlPackage' types in the correct order. 27 | - Suppport list of list, option of option, etc. 28 | - Use GHC.TypeList (TypeError). 29 | - Correct Haskell Int (64-bits) to OCaml int, int32, int64 types. 30 | - Support recursive and mutually interdependent types. 31 | 32 | ## Wishlist 33 | 34 | Nice to have but low priority. 35 | 36 | - OCaml source code parser and AST. Would make it a lot easier to handle manually written types, encoders, decoders. 37 | - Output to js_of_ocaml and regular OCaml. 38 | 39 | ## ocaml-export Internal Tests 40 | 41 | If you want to run it against servant: 42 | 43 | ```bash 44 | stack test --flag ocaml-export:servant-spec 45 | cd test/interface/golden 46 | npm install 47 | npm run test 48 | ``` 49 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # ocaml-export servant server and reason-react frontend example 2 | 3 | ## Structure 4 | 5 | - shared-types: data types and servant api 6 | - shared-types-re: an executable that generates the Reason types 7 | from shared-types using ocaml-export 8 | - frontend: a simple Reason React app that uses the automatically 9 | generated types to query the server 10 | - server: a servant server with a mock database, uses shared-types 11 | and serves the fronted 12 | 13 | ## run 14 | 15 | The compile process is not automated yet. You have to do it manually for the time being. 16 | Any time you change the types you need update fronted and the server. Anytime you change 17 | the frontend you need to compile it and re-copy the output to the server's static directory. 18 | 19 | ``` 20 | cd shared-types-re 21 | stack build 22 | stack exec generate-reason 23 | cd ../frontend 24 | yarn 25 | yarn build 26 | cd ../server 27 | rm -rf static 28 | cp -rf ../frontend/build static 29 | stack build 30 | stack exec server 31 | ``` 32 | 33 | Now you can view the app in your browser `localhost:8001/index.html` 34 | 35 | ## Todo 36 | 37 | - automate the build process 38 | - add authentication 39 | - add admin interface 40 | - unhardcode a few things 41 | - add comments to the code 42 | -------------------------------------------------------------------------------- /example/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .merlin 3 | .bsb.lock 4 | npm-debug.log 5 | /lib/bs/ 6 | /node_modules/ -------------------------------------------------------------------------------- /example/frontend/README.md: -------------------------------------------------------------------------------- 1 | # frontend 2 | 3 | ## Run Project 4 | 5 | ```sh 6 | npm install 7 | npm start 8 | # in another tab 9 | npm run webpack 10 | ``` 11 | 12 | After you see the webpack compilation succeed (the `npm run webpack` step), open up `build/index.html` (**no server needed!**). Then modify whichever `.re` file in `src` and refresh the page to see the changes. 13 | 14 | **For more elaborate ReasonReact examples**, please see https://github.com/reasonml-community/reason-react-example 15 | 16 | ## Run Project with Server 17 | 18 | To run with the webpack development server run `npm run server` and view in the browser at http://localhost:8000. Running in this environment provides hot reloading and support for routing; just edit and save the file and the browser will automatically refresh. 19 | 20 | Note that any hot reload on a route will fall back to the root (`/`), so `ReasonReact.Router.dangerouslyGetInitialUrl` will likely be needed alongside the `ReasonReact.Router.watchUrl` logic to handle routing correctly on hot reload refreshes or simply opening the app at a URL that is not the root. 21 | 22 | To use a port other than 8000 set the `PORT` environment variable (`PORT=8080 npm run server`). 23 | 24 | ## Build for Production 25 | 26 | ```sh 27 | npm run build 28 | npm run webpack:production 29 | ``` 30 | 31 | This will replace the development artifact `build/Index.js` for an optimized version as well as copy `src/index.html` into `build/`. You can then deploy the contents of the `build` directory (`index.html` and `Index.js`). 32 | 33 | If you make use of routing (via `ReasonReact.Router` or similar logic) ensure that server-side routing handles your routes or that 404's are directed back to `index.html` (which is how the dev server is set up). 34 | 35 | **To enable dead code elimination**, change `bsconfig.json`'s `package-specs` `module` from `"commonjs"` to `"es6"`. Then re-run the above 2 commands. This will allow Webpack to remove unused code. 36 | -------------------------------------------------------------------------------- /example/frontend/__tests__/Exported/SharedTypes_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | (SharedTypes.decodeEntity AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (SharedTypes.encodeEntity Aeson.Encode.int Aeson.Encode.int) 5 | "entity" 6 | "test/golden/Entity"; 7 | 8 | AesonSpec.goldenDirSpec 9 | SharedTypes.decodeKey 10 | SharedTypes.encodeKey 11 | "key" 12 | "test/golden/Key"; 13 | 14 | AesonSpec.goldenDirSpec 15 | SharedTypes.decodeTodoId 16 | SharedTypes.encodeTodoId 17 | "todoId" 18 | "test/golden/TodoId"; 19 | 20 | AesonSpec.goldenDirSpec 21 | SharedTypes.decodeUserId 22 | SharedTypes.encodeUserId 23 | "userId" 24 | "test/golden/UserId"; 25 | 26 | AesonSpec.goldenDirSpec 27 | SharedTypes.decodeUsername 28 | SharedTypes.encodeUsername 29 | "username" 30 | "test/golden/Username"; 31 | 32 | AesonSpec.goldenDirSpec 33 | SharedTypes.decodeTodo 34 | SharedTypes.encodeTodo 35 | "todo" 36 | "test/golden/Todo"; 37 | 38 | AesonSpec.goldenDirSpec 39 | SharedTypes.decodeUser 40 | SharedTypes.encodeUser 41 | "user" 42 | "test/golden/User"; 43 | -------------------------------------------------------------------------------- /example/frontend/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "reason": { 4 | "react-jsx": 2 5 | }, 6 | "sources": { 7 | "dir": "src", 8 | "subdirs": true 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "commonjs", 13 | "in-source": true 14 | } 15 | ], 16 | "namespace": true, 17 | "bs-dependencies": ["bs-aeson", "bs-fetch", "reason-react"], 18 | "suffix": ".bs.js", 19 | "bs-dev-dependencies": ["bs-aeson-spec"], 20 | "refmt": 3 21 | } 22 | -------------------------------------------------------------------------------- /example/frontend/build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OCaml Export - Todo Example 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /example/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "build": "yarn clean && yarn bsb -make-world && webpack", 6 | "clean": "yarn bsb -clean-world", 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "BuckleScript" 11 | ], 12 | "author": "", 13 | "license": "MIT", 14 | "dependencies": { 15 | "bs-aeson": "^3.1.0", 16 | "bs-fetch": "^0.3.1", 17 | "react": "^16.2.0", 18 | "react-dom": "^16.2.0", 19 | "reason-react": ">=0.4.0" 20 | }, 21 | "devDependencies": { 22 | "bs-aeson-spec": "^2.2.0", 23 | "bs-platform": "^4.0.18", 24 | "html-webpack-plugin": "^3.2.0", 25 | "webpack": "^4.0.1", 26 | "webpack-cli": "^3.1.1", 27 | "webpack-dev-server": "^3.1.8" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/frontend/src/Exported/SharedTypes.ml: -------------------------------------------------------------------------------- 1 | type ('key, 'value) entity = 2 | { entityKey : 'key 3 | ; entityValue : 'value 4 | } 5 | 6 | let encodeEntity encodeKey encodeValue entity = 7 | Aeson.Encode.object_ 8 | [ ( "key", encodeKey entity.entityKey ) 9 | ; ( "value", encodeValue entity.entityValue ) 10 | ] 11 | 12 | let decodeEntity decodeKey decodeValue json = 13 | match Aeson.Decode. 14 | { entityKey = field "key" (fun a -> unwrapResult (decodeKey a)) json 15 | ; entityValue = field "value" (fun a -> unwrapResult (decodeValue a)) json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeEntity: " ^ message) 20 | 21 | 22 | type key = 23 | | Key of int 24 | 25 | let encodeKey x = 26 | match x with 27 | | Key y0 -> 28 | Aeson.Encode.int y0 29 | 30 | let decodeKey json = 31 | match Aeson.Decode.int json with 32 | | v -> Belt.Result.Ok (Key v) 33 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeKey: " ^ msg) 34 | 35 | type todoId = 36 | | TodoId of key 37 | 38 | let encodeTodoId x = 39 | match x with 40 | | TodoId y0 -> 41 | encodeKey y0 42 | 43 | let decodeTodoId json = 44 | match (Aeson.Decode.unwrapResult (decodeKey json)) with 45 | | v -> Belt.Result.Ok (TodoId v) 46 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeTodoId: " ^ msg) 47 | 48 | type userId = 49 | | UserId of key 50 | 51 | let encodeUserId x = 52 | match x with 53 | | UserId y0 -> 54 | encodeKey y0 55 | 56 | let decodeUserId json = 57 | match (Aeson.Decode.unwrapResult (decodeKey json)) with 58 | | v -> Belt.Result.Ok (UserId v) 59 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeUserId: " ^ msg) 60 | 61 | type username = 62 | | Username of string 63 | 64 | let encodeUsername x = 65 | match x with 66 | | Username y0 -> 67 | Aeson.Encode.string y0 68 | 69 | let decodeUsername json = 70 | match Aeson.Decode.string json with 71 | | v -> Belt.Result.Ok (Username v) 72 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeUsername: " ^ msg) 73 | 74 | type todo = 75 | { description : string 76 | ; completed : bool 77 | } 78 | 79 | let encodeTodo x = 80 | Aeson.Encode.object_ 81 | [ ( "description", Aeson.Encode.string x.description ) 82 | ; ( "completed", Aeson.Encode.bool x.completed ) 83 | ] 84 | 85 | let decodeTodo json = 86 | match Aeson.Decode. 87 | { description = field "description" string json 88 | ; completed = field "completed" bool json 89 | } 90 | with 91 | | v -> Belt.Result.Ok v 92 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeTodo: " ^ message) 93 | 94 | type user = 95 | { username : username 96 | ; password : string 97 | } 98 | 99 | let encodeUser x = 100 | Aeson.Encode.object_ 101 | [ ( "username", encodeUsername x.username ) 102 | ; ( "password", Aeson.Encode.string x.password ) 103 | ] 104 | 105 | let decodeUser json = 106 | match Aeson.Decode. 107 | { username = field "username" (fun a -> unwrapResult (decodeUsername a)) json 108 | ; password = field "password" string json 109 | } 110 | with 111 | | v -> Belt.Result.Ok v 112 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeUser: " ^ message) 113 | -------------------------------------------------------------------------------- /example/frontend/src/Exported/SharedTypes.mli: -------------------------------------------------------------------------------- 1 | type ('key, 'value) entity = 2 | { entityKey : 'key 3 | ; entityValue : 'value 4 | } 5 | 6 | val encodeEntity : ('key -> Js_json.t) -> ('value -> Js_json.t) -> ('key, 'value) entity -> Js_json.t 7 | 8 | val decodeEntity : (Js_json.t -> ('key, string) Belt.Result.t) -> (Js_json.t -> ('value, string) Belt.Result.t) -> Js_json.t -> (('key, 'value) entity, string) Belt.Result.t 9 | 10 | 11 | type key = 12 | | Key of int 13 | 14 | val encodeKey : key -> Js_json.t 15 | 16 | val decodeKey : Js_json.t -> (key, string) Belt.Result.t 17 | 18 | type todoId = 19 | | TodoId of key 20 | 21 | val encodeTodoId : todoId -> Js_json.t 22 | 23 | val decodeTodoId : Js_json.t -> (todoId, string) Belt.Result.t 24 | 25 | type userId = 26 | | UserId of key 27 | 28 | val encodeUserId : userId -> Js_json.t 29 | 30 | val decodeUserId : Js_json.t -> (userId, string) Belt.Result.t 31 | 32 | type username = 33 | | Username of string 34 | 35 | val encodeUsername : username -> Js_json.t 36 | 37 | val decodeUsername : Js_json.t -> (username, string) Belt.Result.t 38 | 39 | type todo = 40 | { description : string 41 | ; completed : bool 42 | } 43 | 44 | val encodeTodo : todo -> Js_json.t 45 | 46 | val decodeTodo : Js_json.t -> (todo, string) Belt.Result.t 47 | 48 | type user = 49 | { username : username 50 | ; password : string 51 | } 52 | 53 | val encodeUser : user -> Js_json.t 54 | 55 | val decodeUser : Js_json.t -> (user, string) Belt.Result.t 56 | -------------------------------------------------------------------------------- /example/frontend/src/Index.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by BUCKLESCRIPT VERSION 4.0.18, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var ReactDOMRe = require("reason-react/src/ReactDOMRe.js"); 5 | var ReasonReact = require("reason-react/src/ReasonReact.js"); 6 | var UserSelector$Frontend = require("./UserSelector.bs.js"); 7 | 8 | ReactDOMRe.renderToElementWithId(ReasonReact.element(undefined, undefined, UserSelector$Frontend.make(/* array */[])), "root"); 9 | 10 | /* Not a pure module */ 11 | -------------------------------------------------------------------------------- /example/frontend/src/Index.re: -------------------------------------------------------------------------------- 1 | ReactDOMRe.renderToElementWithId(, "root"); -------------------------------------------------------------------------------- /example/frontend/src/ServerFetchConfig.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by BUCKLESCRIPT VERSION 4.0.18, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | var getBaseUrl = ( 6 | function () { 7 | var cfg = window.__serverFetchConfig; 8 | if (typeof(cfg) !== "undefined") { 9 | return cfg.scheme + "://" + cfg.host + ":" + cfg.port; 10 | } else { 11 | return ""; 12 | } 13 | } 14 | ); 15 | 16 | var config = /* record */[/* baseUrl */"http://localhost:8001"]; 17 | 18 | exports.getBaseUrl = getBaseUrl; 19 | exports.config = config; 20 | /* getBaseUrl Not a pure module */ 21 | -------------------------------------------------------------------------------- /example/frontend/src/ServerFetchConfig.re: -------------------------------------------------------------------------------- 1 | module type Config = {let baseUrl: string;}; 2 | 3 | type projectConfig = {baseUrl: string}; 4 | 5 | let getBaseUrl: unit => string = [%bs.raw 6 | {| 7 | function () { 8 | var cfg = window.__serverFetchConfig; 9 | if (typeof(cfg) !== "undefined") { 10 | return cfg.scheme + "://" + cfg.host + ":" + cfg.port; 11 | } else { 12 | return ""; 13 | } 14 | } 15 | |} 16 | ]; 17 | 18 | let config = {baseUrl: "http://localhost:8001"}; -------------------------------------------------------------------------------- /example/frontend/src/UserSelector.re: -------------------------------------------------------------------------------- 1 | open SharedTypes; 2 | 3 | let usernameToString = (username: username) => 4 | switch (username) { 5 | | Username(s) => s 6 | }; 7 | 8 | module Fetch = 9 | ServerFetch.MakeServerFetch({ 10 | let baseUrl = ServerFetchConfig.config.baseUrl; 11 | }); 12 | 13 | type page = 14 | | UsersPage 15 | | TodosPage; 16 | 17 | type state = { 18 | users: array(entity(userId, user)), 19 | page, 20 | todos: array(entity(todoId, todo)), 21 | }; 22 | 23 | let initialState = () => {users: [||], page: UsersPage, todos: [||]}; 24 | 25 | /* Action declaration */ 26 | type action = 27 | | UpdateUsers(array(entity(userId, user))) 28 | | UpdatePage(page) 29 | | FetchTodos(userId) 30 | | GotTodos(array(entity(todoId, todo))); 31 | 32 | let reducer = (action, state) => 33 | switch (action) { 34 | | UpdateUsers(users) => ReasonReact.Update({...state, users}) 35 | | UpdatePage(page) => ReasonReact.Update({...state, page}) 36 | | FetchTodos(userId) => 37 | ReasonReact.UpdateWithSideEffects( 38 | {...state, page: TodosPage}, 39 | ( 40 | ({send}) => 41 | Fetch.getUserTodos(userId) 42 | |> Js.Promise.then_(todos => { 43 | send(GotTodos(todos)); 44 | Js.Promise.resolve(); 45 | }) 46 | |> ignore 47 | ), 48 | ) 49 | | GotTodos(todos) => ReasonReact.Update({...state, todos}) 50 | }; 51 | 52 | let component = ReasonReact.reducerComponent("UserSelector"); 53 | 54 | let make = _children => { 55 | ...component, 56 | initialState, 57 | didMount: self => 58 | Fetch.getUsers() 59 | |> Js.Promise.then_(rUsers => { 60 | self.send(UpdateUsers(rUsers)); 61 | Js.Promise.resolve(); 62 | }) 63 | |> ignore, 64 | reducer, 65 | render: ({send, state}) => 66 |
67 | ( 68 | if (state.page == UsersPage) { 69 | Array.map( 70 | (user: entity(userId, user)) => 71 |
send(FetchTodos(user.entityKey)))> 72 | ( 73 | ReasonReact.string( 74 | usernameToString(user.entityValue.username), 75 | ) 76 | ) 77 |
, 78 | state.users, 79 | ) 80 | |> ReasonReact.array; 81 | } else if (state.page == TodosPage) { 82 |
83 |
84 | ( 85 | if (Array.length(state.todos) == 0) { 86 | ReasonReact.string("No todos"); 87 | } else { 88 | Array.map( 89 | (todo: entity(todoId, todo)) => 90 |
91 | (ReasonReact.string(todo.entityValue.description)) 92 |
, 93 | state.todos, 94 | ) 95 | |> ReasonReact.array; 96 | } 97 | ) 98 |
99 |
100 | 103 |
104 |
; 105 | } else { 106 | ReasonReact.null; 107 | } 108 | ) 109 |
, 110 | }; -------------------------------------------------------------------------------- /example/frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OCaml Export - Todo Example 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /example/frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const outputDir = path.join(__dirname, 'build/'); 4 | 5 | const isProd = process.env.NODE_ENV === 'production'; 6 | 7 | module.exports = { 8 | entry: './src/Index.bs.js', 9 | mode: isProd ? 'production' : 'development', 10 | output: { 11 | path: outputDir, 12 | filename: 'Index.js' 13 | }, 14 | plugins: [ 15 | new HtmlWebpackPlugin({ 16 | template: 'src/index.html', 17 | inject: false 18 | }) 19 | ], 20 | devServer: { 21 | compress: true, 22 | contentBase: outputDir, 23 | port: process.env.PORT || 8000, 24 | historyApiFallback: true 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /example/server/.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | server.cabal 3 | *~ -------------------------------------------------------------------------------- /example/server/ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for server 2 | 3 | ## Unreleased changes 4 | -------------------------------------------------------------------------------- /example/server/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2019 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /example/server/README.md: -------------------------------------------------------------------------------- 1 | # server 2 | -------------------------------------------------------------------------------- /example/server/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /example/server/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Server 4 | 5 | main :: IO () 6 | main = runServer 7 | -------------------------------------------------------------------------------- /example/server/package.yaml: -------------------------------------------------------------------------------- 1 | name: server 2 | version: 0.1.0.0 3 | github: "plow-technologies/ocaml-export" 4 | license: BSD3 5 | author: "James M.C. Haver II" 6 | maintainer: "james.haver@plowtech.net" 7 | copyright: "2019 Plow Technologies" 8 | 9 | extra-source-files: 10 | - README.md 11 | - ChangeLog.md 12 | 13 | description: Please see the README on GitHub at 14 | 15 | dependencies: 16 | - base >= 4.7 && < 5 17 | - aeson 18 | - servant 19 | - servant-server 20 | - shared-types 21 | - stm 22 | - wai 23 | - warp 24 | 25 | library: 26 | source-dirs: src 27 | 28 | executables: 29 | server: 30 | main: Main.hs 31 | source-dirs: app 32 | ghc-options: 33 | - -threaded 34 | - -rtsopts 35 | - -with-rtsopts=-N 36 | dependencies: 37 | - server 38 | -------------------------------------------------------------------------------- /example/server/stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.13 2 | 3 | packages: 4 | - . 5 | - "../shared-types" 6 | -------------------------------------------------------------------------------- /example/server/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OCaml Export - Todo Example 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /example/server/test/Spec.hs: -------------------------------------------------------------------------------- 1 | main :: IO () 2 | main = putStrLn "Test suite not yet implemented" 3 | -------------------------------------------------------------------------------- /example/shared-types-re/.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | shared-types-re.cabal 3 | *~ -------------------------------------------------------------------------------- /example/shared-types-re/ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for shared-types-re 2 | 3 | ## Unreleased changes 4 | -------------------------------------------------------------------------------- /example/shared-types-re/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2019 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /example/shared-types-re/README.md: -------------------------------------------------------------------------------- 1 | # shared-types-re 2 | -------------------------------------------------------------------------------- /example/shared-types-re/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /example/shared-types-re/app/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | module Main where 3 | 4 | import System.Environment (getArgs) 5 | import System.Exit (exitFailure) 6 | 7 | import OCaml.Export 8 | import qualified Shared.Types.Reason.Package as SharedTypes 9 | 10 | main :: IO () 11 | main = do 12 | mkPackageWithGolden 13 | (Proxy :: Proxy SharedTypes.SharedTypesPackage) 14 | "test/golden" 15 | SharedTypes.fileMap 16 | 17 | where 18 | mkPackageWithGolden proxy dir fileMap = do 19 | mkGoldenFiles proxy 5 dir 20 | mkPackage proxy (PackageOptions "." "../frontend/src/Exported" fileMap True $ Just $ SpecOptions "../frontend/__tests__/Exported" dir Nothing) 21 | -------------------------------------------------------------------------------- /example/shared-types-re/handwritten/Entity.ml: -------------------------------------------------------------------------------- 1 | type ('key, 'value) entity = 2 | { entityKey : 'key 3 | ; entityValue : 'value 4 | } 5 | 6 | let encodeEntity encodeKey encodeValue entity = 7 | Aeson.Encode.object_ 8 | [ ( "key", encodeKey entity.entityKey ) 9 | ; ( "value", encodeValue entity.entityValue ) 10 | ] 11 | 12 | let decodeEntity decodeKey decodeValue json = 13 | match Aeson.Decode. 14 | { entityKey = field "key" (fun a -> unwrapResult (decodeKey a)) json 15 | ; entityValue = field "value" (fun a -> unwrapResult (decodeValue a)) json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeEntity: " ^ message) 20 | -------------------------------------------------------------------------------- /example/shared-types-re/handwritten/Entity.mli: -------------------------------------------------------------------------------- 1 | type ('key, 'value) entity = 2 | { entityKey : 'key 3 | ; entityValue : 'value 4 | } 5 | 6 | val encodeEntity : ('key -> Js_json.t) -> ('value -> Js_json.t) -> ('key, 'value) entity -> Js_json.t 7 | 8 | val decodeEntity : (Js_json.t -> ('key, string) Belt.Result.t) -> (Js_json.t -> ('value, string) Belt.Result.t) -> Js_json.t -> (('key, 'value) entity, string) Belt.Result.t 9 | -------------------------------------------------------------------------------- /example/shared-types-re/package.yaml: -------------------------------------------------------------------------------- 1 | name: shared-types-re 2 | version: 0.1.0.0 3 | github: "plow-technologies/ocaml-export" 4 | license: BSD3 5 | author: "James M.C. Haver II" 6 | maintainer: "james.haver@plowtech.net" 7 | copyright: "2019 Plow Technologies" 8 | 9 | extra-source-files: 10 | - README.md 11 | - ChangeLog.md 12 | 13 | description: Please see the README on GitHub at 14 | 15 | dependencies: 16 | - base >= 4.7 && < 5 17 | - containers 18 | - ocaml-export 19 | - QuickCheck 20 | - quickcheck-arbitrary-adt 21 | - shared-types 22 | - text 23 | - time 24 | 25 | library: 26 | source-dirs: src 27 | 28 | executables: 29 | generate-reason: 30 | main: Main.hs 31 | source-dirs: app 32 | ghc-options: 33 | - -threaded 34 | - -rtsopts 35 | - -with-rtsopts=-N 36 | dependencies: 37 | - shared-types-re 38 | 39 | tests: 40 | shared-types-re-test: 41 | main: Spec.hs 42 | source-dirs: test 43 | ghc-options: 44 | - -threaded 45 | - -rtsopts 46 | - -with-rtsopts=-N 47 | dependencies: 48 | - hspec 49 | - shared-types-re 50 | -------------------------------------------------------------------------------- /example/shared-types-re/src/Shared/Types/Reason/Package.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | module Shared.Types.Reason.Package 4 | ( fileMap 5 | , SharedTypesPackage 6 | ) where 7 | 8 | import qualified Data.Map as Map 9 | import Data.Proxy 10 | import OCaml.Export 11 | import Shared.Types.Reason.Types 12 | 13 | fileMap :: Map.Map String EmbeddedOCamlFiles 14 | fileMap = Map.fromList $(mkFiles True False (Proxy :: Proxy SharedTypesPackage)) 15 | -------------------------------------------------------------------------------- /example/shared-types-re/src/Shared/Types/Reason/Types.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -fno-warn-orphans #-} 2 | 3 | {-# LANGUAGE DataKinds #-} 4 | {-# LANGUAGE DeriveGeneric #-} 5 | {-# LANGUAGE FlexibleInstances #-} 6 | {-# LANGUAGE OverloadedStrings #-} 7 | {-# LANGUAGE RecordWildCards #-} 8 | {-# LANGUAGE ScopedTypeVariables #-} 9 | {-# LANGUAGE StandaloneDeriving #-} 10 | {-# LANGUAGE TypeOperators #-} 11 | 12 | module Shared.Types.Reason.Types where 13 | 14 | import Data.Time.Clock.POSIX 15 | import Data.Time 16 | import qualified Data.Text as T 17 | import OCaml.Export 18 | import Test.QuickCheck 19 | import Test.QuickCheck.Arbitrary.ADT 20 | 21 | import Shared.Types 22 | (Entity(..), IsKey(..), Key(..), Todo(..), TodoId(..), User(..), UserId(..), Username(..)) 23 | 24 | -- because of the restriction we put on Entity 25 | -- we have to make this dummy instance 26 | instance IsKey TypeParameterRef0 where 27 | fromKey (Key k) = TypeParameterRef0 (fromIntegral k) 28 | toKey (TypeParameterRef0 k) = Key (fromIntegral k) 29 | 30 | type SharedTypesPackage = 31 | OCamlPackage "shared-types" '[] :> 32 | (OCamlModule '["SharedTypes"] 33 | :> OCamlTypeInFile (Entity TypeParameterRef0 TypeParameterRef1) "handwritten/Entity" 34 | :> Key 35 | :> TodoId 36 | :> UserId 37 | :> Username 38 | :> Todo 39 | :> User 40 | ) 41 | 42 | instance (IsKey a, Arbitrary a, Arbitrary b) => Arbitrary (Entity a b) where 43 | arbitrary = Entity <$> arbitrary <*> arbitrary 44 | 45 | instance (IsKey a, Arbitrary a, ToADTArbitrary a, Arbitrary b, ToADTArbitrary b) => ToADTArbitrary (Entity a b) where 46 | toADTArbitrarySingleton Proxy = 47 | ADTArbitrarySingleton "Shared.Types" "Entity" 48 | <$> oneof 49 | [ ConstructorArbitraryPair "Entity" <$> (Entity <$> arbitrary <*> arbitrary) 50 | ] 51 | 52 | toADTArbitrary Proxy = 53 | ADTArbitrary "Shared.Types" "Entity" 54 | <$> sequence 55 | [ ConstructorArbitraryPair "Entity" <$> (Entity <$> arbitrary <*> arbitrary) ] 56 | 57 | instance OCamlType (Entity TypeParameterRef0 TypeParameterRef1) where 58 | toOCamlType _ = typeableToOCamlType (Proxy :: Proxy (Entity TypeParameterRef0 TypeParameterRef1)) 59 | 60 | instance Arbitrary Key where 61 | arbitrary = Key <$> arbitrary 62 | instance ToADTArbitrary Key 63 | instance OCamlType Key 64 | 65 | instance Arbitrary TodoId where 66 | arbitrary = TodoId <$> arbitrary 67 | instance ToADTArbitrary TodoId 68 | instance OCamlType TodoId 69 | 70 | instance Arbitrary Todo where 71 | arbitrary = Todo <$> arbitrary <*> arbitrary -- <*> arbitrary <*> arbitrary 72 | instance ToADTArbitrary Todo 73 | instance OCamlType Todo 74 | 75 | instance Arbitrary UserId where 76 | arbitrary = UserId <$> arbitrary 77 | instance ToADTArbitrary UserId 78 | instance OCamlType UserId 79 | 80 | instance Arbitrary User where 81 | arbitrary = User <$> arbitrary <*> arbitrary 82 | instance ToADTArbitrary User 83 | instance OCamlType User 84 | 85 | instance Arbitrary Username where 86 | arbitrary = Username <$> arbitrary 87 | instance ToADTArbitrary Username 88 | instance OCamlType Username 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | instance Arbitrary T.Text where 98 | arbitrary = T.pack <$> arbitrary 99 | 100 | instance Arbitrary UTCTime where 101 | arbitrary = posixSecondsToUTCTime . fromIntegral <$> (arbitrary :: Gen Integer) 102 | -------------------------------------------------------------------------------- /example/shared-types-re/stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.13 2 | 3 | packages: 4 | - . 5 | - "../shared-types" 6 | - "../.." 7 | -------------------------------------------------------------------------------- /example/shared-types-re/test/Spec.hs: -------------------------------------------------------------------------------- 1 | import OCaml.Export 2 | import Shared.Types.Reason.Types 3 | import Test.Hspec 4 | 5 | main :: IO () 6 | main = do 7 | mkGoldenFiles (Proxy :: Proxy SharedTypesPackage) 100 "test/golden" 8 | 9 | hspec $ runGoldenSpec (Proxy :: Proxy SharedTypesPackage) 100 "test/golden" 10 | -------------------------------------------------------------------------------- /example/shared-types-re/test/golden/Key/Key.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -9016493426044224665, 3 | "samples": [ 4 | 14712555, 5 | 7437287, 6 | 14792562, 7 | 1480808, 8 | 4190332, 9 | 13155843, 10 | 11439407, 11 | 1775515, 12 | 7532512, 13 | 4661264, 14 | 7024275, 15 | 6668764, 16 | 7985246, 17 | 13438602, 18 | 12485020, 19 | 4081590, 20 | 5961914, 21 | 15251722, 22 | 6795836, 23 | 13222274, 24 | 1604816, 25 | 6338079, 26 | 274059, 27 | 28615, 28 | 4034922, 29 | 4777494, 30 | 12594374, 31 | 4974051, 32 | 11277270, 33 | 2261227, 34 | 9305863, 35 | 11828696, 36 | 11907381, 37 | 14136564, 38 | 14820928, 39 | 4382107, 40 | 2445010, 41 | 15375736, 42 | 16715365, 43 | 11113750, 44 | 7078096, 45 | 13092708, 46 | 3668060, 47 | 12258593, 48 | 10753589, 49 | 10467312, 50 | 3331722, 51 | 16658658, 52 | 9953561, 53 | 532131, 54 | 3123232, 55 | 10916078, 56 | 12981692, 57 | 5916980, 58 | 11030128, 59 | 7675371, 60 | 9797685, 61 | 9493925, 62 | 3443565, 63 | 5107255, 64 | 5941828, 65 | 11209041, 66 | 11960801, 67 | 14212318, 68 | 10422842, 69 | 10273439, 70 | 1964415, 71 | 781712, 72 | 6323020, 73 | 12073073, 74 | 9750335, 75 | 7870988, 76 | 13512407, 77 | 4524596, 78 | 4416529, 79 | 14022590, 80 | 16490956, 81 | 574126, 82 | 13123093, 83 | 15785711, 84 | 16199621, 85 | 866033, 86 | 4772027, 87 | 6862673, 88 | 13671382, 89 | 3457404, 90 | 4787414, 91 | 12328771, 92 | 9548103, 93 | 5053808, 94 | 11707982, 95 | 1703195, 96 | 2671780, 97 | 4675495, 98 | 14811032, 99 | 3403113, 100 | 3622502, 101 | 2364409, 102 | 15671864, 103 | 2360132 104 | ] 105 | } -------------------------------------------------------------------------------- /example/shared-types-re/test/golden/TodoId/TodoId.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -5370017512571779706, 3 | "samples": [ 4 | 12714750, 5 | 48180, 6 | 15216059, 7 | 13032757, 8 | 4502333, 9 | 14824121, 10 | 12775197, 11 | 10972032, 12 | 15028092, 13 | 15015365, 14 | 3559901, 15 | 615304, 16 | 3513444, 17 | 8975503, 18 | 13614715, 19 | 3377585, 20 | 8636632, 21 | 460582, 22 | 12273922, 23 | 6474639, 24 | 5792685, 25 | 2999549, 26 | 5523174, 27 | 14619030, 28 | 892818, 29 | 7436672, 30 | 6130050, 31 | 16246841, 32 | 14883374, 33 | 9503905, 34 | 14508021, 35 | 12603684, 36 | 1322568, 37 | 3342408, 38 | 12576237, 39 | 16043243, 40 | 13066615, 41 | 14210384, 42 | 5871673, 43 | 5867524, 44 | 8968753, 45 | 4671933, 46 | 3228015, 47 | 4805271, 48 | 12085015, 49 | 13750083, 50 | 13753302, 51 | 11725104, 52 | 16030444, 53 | 1511088, 54 | 16284032, 55 | 4080328, 56 | 16525945, 57 | 5502429, 58 | 10439009, 59 | 7034842, 60 | 14461692, 61 | 11047743, 62 | 522861, 63 | 11379924, 64 | 11781851, 65 | 9389506, 66 | 4553996, 67 | 11533374, 68 | 766157, 69 | 12381207, 70 | 8338576, 71 | 13337164, 72 | 4057039, 73 | 3111867, 74 | 12991803, 75 | 15190716, 76 | 7845301, 77 | 10668892, 78 | 10594743, 79 | 6189965, 80 | 3289733, 81 | 16601887, 82 | 4844259, 83 | 2525729, 84 | 6307988, 85 | 7894810, 86 | 9614952, 87 | 6258536, 88 | 4840102, 89 | 15368432, 90 | 6905675, 91 | 10706986, 92 | 6125804, 93 | 3042528, 94 | 1242615, 95 | 5733302, 96 | 554569, 97 | 13892110, 98 | 16367184, 99 | 9329612, 100 | 7461491, 101 | 959756, 102 | 13445600, 103 | 7427605 104 | ] 105 | } -------------------------------------------------------------------------------- /example/shared-types-re/test/golden/UserId/UserId.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -5093999010263708143, 3 | "samples": [ 4 | 3028242, 5 | 8999914, 6 | 15168533, 7 | 127708, 8 | 4381148, 9 | 3992934, 10 | 3258504, 11 | 6916557, 12 | 8800571, 13 | 8897664, 14 | 10375706, 15 | 13248134, 16 | 7455144, 17 | 3093037, 18 | 8326406, 19 | 14919782, 20 | 10732389, 21 | 7435949, 22 | 6844595, 23 | 10279866, 24 | 3457624, 25 | 15933670, 26 | 824013, 27 | 4642393, 28 | 2512434, 29 | 284855, 30 | 1386282, 31 | 8632007, 32 | 10133744, 33 | 4180439, 34 | 6365843, 35 | 7910376, 36 | 11388970, 37 | 8406803, 38 | 12343093, 39 | 11847212, 40 | 15321735, 41 | 4747706, 42 | 5889729, 43 | 6331352, 44 | 15663849, 45 | 13772356, 46 | 16172884, 47 | 12459110, 48 | 7143349, 49 | 9570796, 50 | 16775226, 51 | 6428390, 52 | 422648, 53 | 2168914, 54 | 14352328, 55 | 7134985, 56 | 14099254, 57 | 7333550, 58 | 8081575, 59 | 746151, 60 | 8360810, 61 | 6930455, 62 | 10665611, 63 | 7264848, 64 | 4935709, 65 | 8494254, 66 | 6022911, 67 | 15927909, 68 | 645697, 69 | 104158, 70 | 14313882, 71 | 11137850, 72 | 4272423, 73 | 16712363, 74 | 11333087, 75 | 2876488, 76 | 16099456, 77 | 11346059, 78 | 3014458, 79 | 131611, 80 | 566311, 81 | 12582047, 82 | 7979208, 83 | 8324229, 84 | 40792, 85 | 10484055, 86 | 8645376, 87 | 9236610, 88 | 11890050, 89 | 9961247, 90 | 578042, 91 | 2867722, 92 | 13554669, 93 | 6211901, 94 | 16519162, 95 | 16761875, 96 | 805322, 97 | 15875302, 98 | 563868, 99 | 3207819, 100 | 11756087, 101 | 6766121, 102 | 8462496, 103 | 13583838 104 | ] 105 | } -------------------------------------------------------------------------------- /example/shared-types/.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | shared-types.cabal 3 | *~ -------------------------------------------------------------------------------- /example/shared-types/ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for shared-types 2 | 3 | ## Unreleased changes 4 | -------------------------------------------------------------------------------- /example/shared-types/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2019 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /example/shared-types/README.md: -------------------------------------------------------------------------------- 1 | # shared-types 2 | -------------------------------------------------------------------------------- /example/shared-types/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /example/shared-types/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Lib 4 | 5 | main :: IO () 6 | main = someFunc 7 | -------------------------------------------------------------------------------- /example/shared-types/package.yaml: -------------------------------------------------------------------------------- 1 | name: shared-types 2 | version: 0.1.0.0 3 | github: "plow-technologies/ocaml-export" 4 | license: BSD3 5 | author: "James M.C. Haver II" 6 | maintainer: "james.haver@plowtech.net" 7 | copyright: "2019 Plow Technologies" 8 | 9 | extra-source-files: 10 | - README.md 11 | - ChangeLog.md 12 | 13 | description: Please see the README on GitHub at 14 | 15 | dependencies: 16 | - base >= 4.7 && < 5 17 | - aeson 18 | - http-api-data 19 | - servant 20 | - text 21 | - time 22 | 23 | library: 24 | source-dirs: src 25 | 26 | tests: 27 | shared-types-test: 28 | main: Spec.hs 29 | source-dirs: test 30 | ghc-options: 31 | - -threaded 32 | - -rtsopts 33 | - -with-rtsopts=-N 34 | dependencies: 35 | - shared-types 36 | -------------------------------------------------------------------------------- /example/shared-types/stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-13.13 2 | 3 | packages: 4 | - . 5 | -------------------------------------------------------------------------------- /example/shared-types/test/Spec.hs: -------------------------------------------------------------------------------- 1 | main :: IO () 2 | main = putStrLn "Test suite not yet implemented" 3 | -------------------------------------------------------------------------------- /package.yaml: -------------------------------------------------------------------------------- 1 | name: ocaml-export 2 | version: 0.14.0 3 | synopsis: Convert Haskell types in OCaml types 4 | description: Use GHC.Generics and Typeable to convert Haskell types to OCaml types. Convert aeson serialization to ocaml. 5 | category: Web 6 | author: James M.C. Haver II 7 | maintainer: james.haver@plowtech.net 8 | copyright: 2017 Plow Technologies 9 | license: BSD3 10 | github: plow-technologies/ocaml-export 11 | 12 | dependencies: 13 | - base >= 4.10 && < 5 14 | - aeson 15 | - bytestring 16 | - containers 17 | - directory 18 | - filepath 19 | - hspec 20 | - hspec-golden-aeson >= 0.4.0.0 21 | - QuickCheck 22 | - quickcheck-arbitrary-adt 23 | - servant 24 | - servant-server 25 | - singletons 26 | - text 27 | - template-haskell 28 | - time 29 | ghc-options: 30 | - -Wall 31 | - -Werror 32 | - -Wcompat 33 | - -Wincomplete-record-updates 34 | - -Wincomplete-uni-patterns 35 | - -Wredundant-constraints 36 | 37 | library: 38 | source-dirs: src 39 | exposed-modules: 40 | - OCaml.Export 41 | - OCaml.BuckleScript.Decode 42 | - OCaml.BuckleScript.Encode 43 | - OCaml.BuckleScript.Record 44 | - OCaml.BuckleScript.Spec 45 | - OCaml.BuckleScript.Types 46 | # Internal packages 47 | - OCaml.Internal.Common 48 | - OCaml.BuckleScript.Internal.Spec 49 | - OCaml.BuckleScript.Internal.Module 50 | - OCaml.BuckleScript.Internal.Package 51 | dependencies: 52 | - file-embed 53 | - formatting 54 | - mtl 55 | - split 56 | - wl-pprint-text 57 | ghc-options: 58 | - -Wredundant-constraints 59 | - -fprint-potential-instances 60 | 61 | tests: 62 | spec: 63 | main: Spec.hs 64 | source-dirs: test 65 | dependencies: 66 | - ocaml-export 67 | - process 68 | - wai 69 | - wai-extra 70 | - warp 71 | when: 72 | - condition: flag(servant-spec) 73 | cpp-options: -DSERVANT_SPEC 74 | 75 | flags: 76 | servant-spec: 77 | description: Test ocaml-export against a servant server. Internal test use only. 78 | manual: True 79 | default: False 80 | -------------------------------------------------------------------------------- /scratch.txt: -------------------------------------------------------------------------------- 1 | https://hackage.haskell.org/package/base-4.10.1.0/docs/Type-Reflection.html 2 | 3 | proxyToTypeRep :: forall a. (Typeable a) => Proxy a -> TypeRep a 4 | proxyToTypeRep Proxy = typeRep @a 5 | 6 | typeRepKind $ proxyToTypeRep (Proxy :: Proxy Maybe) 7 | -------------------------------------------------------------------------------- /src/OCaml/Export.hs: -------------------------------------------------------------------------------- 1 | {-| 2 | Module : OCaml.Export 3 | Description : Export everything from one module 4 | Copyright : Plow Technologies, 2017 5 | License : BSD3 6 | Maintainer : mchaver@gmail.com 7 | Stability : experimental 8 | 9 | -} 10 | 11 | module OCaml.Export 12 | ( OCamlPackage 13 | , NoDependency 14 | , OCamlModule 15 | , OCamlSubModule 16 | , OCamlTypeInFile 17 | , HaskellTypeName 18 | 19 | , PackageOptions (..) 20 | , defaultPackageOptions 21 | , SpecOptions (..) 22 | , defaultSpecOptions 23 | 24 | , EmbeddedOCamlFiles (..) 25 | 26 | , mkPackage 27 | , mkFiles 28 | , mkOCamlTypeMetaData 29 | 30 | -- OCaml.BuckleScript.Types 31 | , OCamlType (..) 32 | , typeableToOCamlType 33 | , TypeParameterRef0(..) 34 | , TypeParameterRef1(..) 35 | , TypeParameterRef2(..) 36 | , TypeParameterRef3(..) 37 | , TypeParameterRef4(..) 38 | , TypeParameterRef5(..) 39 | 40 | -- servant spec 41 | , mkOCamlSpecServer 42 | , MkOCamlSpecAPI 43 | , mkGoldenFiles 44 | , runGoldenSpec 45 | 46 | -- re-export 47 | , Proxy (..) 48 | , (:>) 49 | , (:<|>) (..) 50 | , Application 51 | , Server 52 | , serve 53 | 54 | ) where 55 | 56 | import Data.Proxy (Proxy (..)) 57 | 58 | import OCaml.BuckleScript.Types 59 | 60 | import OCaml.BuckleScript.Internal.Module 61 | import OCaml.BuckleScript.Internal.Package 62 | import OCaml.BuckleScript.Internal.Spec 63 | 64 | import Servant (Application, Server, serve) 65 | import Servant.API ((:>), (:<|>) (..)) 66 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-11.20 2 | 3 | packages: 4 | - . 5 | 6 | extra-deps: 7 | - hspec-golden-aeson-0.6.0.0 8 | - quickcheck-arbitrary-adt-0.3.0.0 9 | 10 | flags: {} 11 | 12 | extra-package-dbs: [] 13 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # file: test.sh 3 | 4 | stack build 5 | stack test --no-run-tests 6 | stack test & 7 | servant_pid=$! 8 | sleep 5 9 | 10 | # we store the lib so no need to build 11 | npm install --prefix test/interface/golden 12 | npm run build --prefix test/interface/golden 13 | npm run test --prefix test/interface/golden 14 | 15 | kill -9 $servant_pid 16 | -------------------------------------------------------------------------------- /test/FileApp.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TemplateHaskell #-} 3 | 4 | module FileApp where 5 | -- containers 6 | import qualified Data.Map as Map 7 | -- hspec 8 | import Test.Hspec 9 | -- servant-server 10 | import Servant 11 | -- ocaml-export 12 | import OCaml.Export 13 | import File 14 | import Util 15 | 16 | $(mkOCamlSpecServer "FilePackage" (Proxy :: Proxy FilePackage)) 17 | 18 | fileMap :: Map.Map String EmbeddedOCamlFiles 19 | fileMap = Map.fromList $(mkFiles True False (Proxy :: Proxy FilePackage)) 20 | 21 | compareInterfaceFiles :: FilePath -> SpecWith () 22 | compareInterfaceFiles = compareFiles "test/interface" "file" True 23 | 24 | spec :: Spec 25 | spec = do 26 | runIO $ mkGoldenFiles (Proxy :: Proxy FilePackage) 10 "test/interface/golden/golden/file" 27 | -- runGoldenSpec (Proxy :: Proxy FilePackage) 10 "test/interface/golden/golden/file" 28 | let dir = "test/interface/temp" 29 | 30 | -- create spec to be tested against servant 31 | runIO $ 32 | mkPackage 33 | (Proxy :: Proxy FilePackage) 34 | (PackageOptions dir "file" fileMap True $ 35 | Just $ SpecOptions "__tests__/file-servant" "golden/file" (Just "http://localhost:8083")) 36 | 37 | -- create spec to be tested against files only 38 | runIO $ 39 | mkPackage 40 | (Proxy :: Proxy FilePackage) 41 | (PackageOptions dir "file" fileMap True $ 42 | Just $ SpecOptions "__tests__/file" "golden/file" Nothing) 43 | 44 | describe "OCaml Declaration with Interface: Product Types" $ 45 | compareInterfaceFiles "File" 46 | -------------------------------------------------------------------------------- /test/Options.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveAnyClass #-} 2 | {-# LANGUAGE DeriveGeneric #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | 5 | module Options where 6 | 7 | {- 8 | import Data.Aeson (FromJSON, ToJSON) 9 | import Data.Aeson.Types hiding (Options) 10 | import Data.Char (toUpper,toLower) 11 | import Data.Monoid ((<>)) 12 | import Data.Proxy 13 | import Data.Text (Text) 14 | import Data.Time 15 | import Data.Time.Clock.POSIX 16 | import GHC.Generics 17 | import OCaml.Export hiding (Options,defaultOptions) 18 | import qualified OCaml.Export as OCaml (Options,defaultOptions) 19 | import Test.Hspec 20 | import Test.QuickCheck 21 | import Test.QuickCheck.Arbitrary.ADT 22 | import Test.Aeson.Internal.ADT.GoldenSpecs 23 | import Util 24 | 25 | 26 | testOptionsInterface = testOCamlTypeWithInterface Options 27 | 28 | mkTestOCaml :: OCamlType a => Text -> a -> OCamlInterface 29 | mkTestOCaml modul = mkOCamlInterfaceWithSpec "http://localhost:8081" "__tests__/golden/" modul 30 | 31 | fieldUpperOptions = defaultOptions { fieldLabelModifier = map toUpper } 32 | 33 | constructorLowerOptions = defaultOptions { constructorTagModifier = map toLower } 34 | 35 | oo = OCaml.defaultOptions {aesonOptions = fieldUpperOptions } 36 | ii = OCaml.defaultOptions {aesonOptions = constructorLowerOptions } 37 | 38 | spec :: Spec 39 | spec = do 40 | describe "OCaml Declaration with Interface: Types with Aeson Options" $ do 41 | testOptionsInterface "Person" (mkOCamlInterfaceWithOptions oo (Proxy :: Proxy Person)) 42 | testOptionsInterface "NameOrIdNumber" (mkOCamlInterfaceWithOptions ii (Proxy :: Proxy NameOrIdNumber)) 43 | 44 | data Person = Person 45 | { id :: Int 46 | , name :: Maybe String 47 | , created :: UTCTime 48 | } deriving (Show, Eq, Generic, OCamlType) 49 | 50 | instance Arbitrary Person where 51 | arbitrary = Person <$> arbitrary <*> arbitrary <*> arbitrary 52 | 53 | instance ToADTArbitrary Person 54 | 55 | instance ToJSON Person where 56 | toJSON = genericToJSON fieldUpperOptions 57 | 58 | instance FromJSON Person where 59 | parseJSON = genericParseJSON fieldUpperOptions 60 | 61 | data NameOrIdNumber = Name String | IdNumber Int 62 | deriving (Show, Eq, Generic, OCamlType) 63 | 64 | instance ToJSON NameOrIdNumber where 65 | toJSON = genericToJSON constructorLowerOptions 66 | 67 | instance FromJSON NameOrIdNumber where 68 | parseJSON = genericParseJSON constructorLowerOptions 69 | -} 70 | -------------------------------------------------------------------------------- /test/ProductApp.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | module ProductApp where 4 | 5 | import OCaml.Export 6 | import Product 7 | -- containers 8 | import qualified Data.Map as Map 9 | -- hspec 10 | import Test.Hspec 11 | 12 | fileMap :: Map.Map String EmbeddedOCamlFiles 13 | fileMap = Map.fromList $(mkFiles True False (Proxy :: Proxy ProductPackage)) 14 | 15 | $(mkOCamlSpecServer "ProductPackage" (Proxy :: Proxy ProductPackage)) 16 | 17 | spec :: Spec 18 | spec = do 19 | runIO $ mkGoldenFiles (Proxy :: Proxy ProductPackage) 10 "test/interface/golden/golden/product" 20 | runGoldenSpec (Proxy :: Proxy ProductPackage) 10 "test/interface/golden/golden/product" 21 | 22 | let dir = "test/interface/temp" 23 | 24 | -- create spec to be tested against servant 25 | runIO $ 26 | mkPackage 27 | (Proxy :: Proxy ProductPackage) 28 | (PackageOptions dir "product" fileMap True $ 29 | Just $ 30 | SpecOptions 31 | "__tests__/product-servant" 32 | "golden/product" 33 | (Just "http://localhost:8081")) 34 | 35 | -- create spec to be tested against files only 36 | runIO $ 37 | mkPackage 38 | (Proxy :: Proxy ProductPackage) 39 | (PackageOptions dir "product" fileMap True $ 40 | Just $ 41 | SpecOptions 42 | "__tests__/product" 43 | "golden/product" 44 | Nothing) 45 | 46 | describe "OCaml Declaration with Interface: Product Types" $ do 47 | compareInterfaceFiles "Person" 48 | compareInterfaceFiles "Company" 49 | compareInterfaceFiles "Card" 50 | compareInterfaceFiles "OneTypeParameter" 51 | compareInterfaceFiles "TwoTypeParameters" 52 | compareInterfaceFiles "ThreeTypeParameters" 53 | compareInterfaceFiles "SubTypeParameter" 54 | compareInterfaceFiles "UnnamedProduct" 55 | compareInterfaceFiles "ComplexProduct" 56 | compareInterfaceFiles "Wrapper" 57 | compareInterfaceFiles "Box" 58 | compareInterfaceFiles "Key" 59 | -------------------------------------------------------------------------------- /test/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE KindSignatures #-} 4 | {-# LANGUAGE OverloadedStrings #-} 5 | {-# LANGUAGE PolyKinds #-} 6 | {-# LANGUAGE RankNTypes #-} 7 | {-# LANGUAGE TemplateHaskell #-} 8 | {-# LANGUAGE TypeOperators #-} 9 | 10 | -- base 11 | import Data.Monoid ((<>)) 12 | -- hspec 13 | import Test.Hspec 14 | -- ocaml-export 15 | import qualified Dependency as D 16 | import qualified FileApp as File 17 | import qualified Product as Product 18 | import qualified ProductApp as Product 19 | import qualified Sum as Sum 20 | import OCaml.Export 21 | 22 | #ifdef SERVANT_SPEC 23 | import Control.Concurrent (forkIO) 24 | import SumApp 25 | -- warp 26 | import Network.Wai.Handler.Warp 27 | #endif 28 | 29 | main :: IO () 30 | main = do 31 | hspec Product.spec 32 | hspec Sum.spec 33 | hspec File.spec 34 | hspec D.spec 35 | 36 | hspec $ 37 | describe "mkOCamlTypeMetaData" $ 38 | it "mkOCamlTypeMetaData on package A and B should equal mkOCamlTypeMetaData on B which has A as a dependency" $ 39 | (mkOCamlTypeMetaData (Proxy :: Proxy Product.ProductPackage)) <> (mkOCamlTypeMetaData (Proxy :: Proxy D.DependencyPackageWithoutProduct)) 40 | `shouldBe` mkOCamlTypeMetaData (Proxy :: Proxy D.DependencyPackage) 41 | 42 | #ifdef SERVANT_SPEC 43 | _ <- forkIO $ run 8081 Product.productPackageApp 44 | _ <- forkIO $ run 8082 sumPackageApp 45 | run 8083 File.filePackageApp 46 | #endif 47 | -------------------------------------------------------------------------------- /test/SumApp.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | module SumApp where 4 | 5 | import OCaml.Export 6 | import Sum 7 | 8 | $(mkOCamlSpecServer "SumPackage" (Proxy :: Proxy SumPackage)) 9 | -------------------------------------------------------------------------------- /test/Util.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -fno-warn-orphans #-} 2 | 3 | module Util where 4 | -- base 5 | import Data.Monoid ((<>)) 6 | import Data.Time 7 | -- text 8 | import qualified Data.Text.IO as T 9 | -- filepath 10 | import System.FilePath.Posix (()) 11 | -- hspec 12 | import Test.Hspec 13 | -- QuickCheck 14 | import Test.QuickCheck 15 | 16 | instance Arbitrary UTCTime where 17 | arbitrary = 18 | UTCTime <$> (ModifiedJulianDay <$> (2000 +) <$> arbitrary) 19 | <*> pure 1.011 20 | -- <*> (fromRational . toRational . (\f -> fromInteger $ round $ f * (10^2) / (10.0^^2)) <$> choose (0:: Double, 86400)) 21 | 22 | data ADT 23 | = Options 24 | 25 | adtToPath :: ADT -> FilePath 26 | adtToPath Options = "options" 27 | 28 | compareFiles :: FilePath -> FilePath -> Bool -> FilePath -> SpecWith () 29 | compareFiles rootDir categoryDir compareInterfaceAndSpecFiles typeName = 30 | it typeName $ do 31 | automated <- T.readFile (testPath typeName <> ".ml") 32 | handWritten <- T.readFile (goldenPath typeName <> ".ml") 33 | automated `shouldBe` handWritten 34 | if compareInterfaceAndSpecFiles 35 | then do 36 | automatedI <- T.readFile (testPath typeName <> ".mli") 37 | handWrittenI <- T.readFile (goldenPath typeName <> ".mli") 38 | automatedI `shouldBe` handWrittenI 39 | 40 | automatedS <- T.readFile (testSpecPath typeName <> "_spec" <> ".ml") 41 | handWrittenS <- T.readFile (goldenSpecPath typeName <> "_spec" <> ".ml") 42 | automatedS `shouldBe` handWrittenS 43 | else pure () 44 | where 45 | testPath = rootDir "temp" categoryDir 46 | goldenPath = rootDir "golden" categoryDir 47 | testSpecPath = rootDir "temp" "__tests__" categoryDir 48 | goldenSpecPath = rootDir "golden" "__tests__" categoryDir 49 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/file-servant/File_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | File.decodePerson 4 | File.encodePerson 5 | "person" 6 | "http://localhost:8083/File/Person" 7 | "golden/file/Person"; 8 | 9 | AesonSpec.sampleGoldenAndServerSpec 10 | File.decodeAutomobile 11 | File.encodeAutomobile 12 | "automobile" 13 | "http://localhost:8083/File/Automobile" 14 | "golden/file/Automobile"; 15 | 16 | AesonSpec.sampleGoldenAndServerSpec 17 | File.decodeBusiness 18 | File.encodeBusiness 19 | "business" 20 | "http://localhost:8083/File/Business" 21 | "golden/file/Business"; 22 | 23 | AesonSpec.sampleGoldenAndServerSpec 24 | (File.decodeWrapper AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 25 | (File.encodeWrapper Aeson.Encode.int Aeson.Encode.int) 26 | "wrapper" 27 | "http://localhost:8083/File/Wrapper" 28 | "golden/file/Wrapper"; 29 | 30 | AesonSpec.sampleGoldenAndServerSpec 31 | File.decodeAutoDependingOnManual 32 | File.encodeAutoDependingOnManual 33 | "autoDependingOnManual" 34 | "http://localhost:8083/File/AutoDependingOnManual" 35 | "golden/file/AutoDependingOnManual"; 36 | 37 | AesonSpec.sampleGoldenAndServerSpec 38 | File.decodeNonGenericType 39 | File.encodeNonGenericType 40 | "nonGenericType" 41 | "http://localhost:8083/File/NonGenericType" 42 | "golden/file/NonGenericType"; 43 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/file/File_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | File.decodePerson 4 | File.encodePerson 5 | "person" 6 | "golden/file/Person"; 7 | 8 | AesonSpec.goldenDirSpec 9 | File.decodeAutomobile 10 | File.encodeAutomobile 11 | "automobile" 12 | "golden/file/Automobile"; 13 | 14 | AesonSpec.goldenDirSpec 15 | File.decodeBusiness 16 | File.encodeBusiness 17 | "business" 18 | "golden/file/Business"; 19 | 20 | AesonSpec.goldenDirSpec 21 | (File.decodeWrapper AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 22 | (File.encodeWrapper Aeson.Encode.int Aeson.Encode.int) 23 | "wrapper" 24 | "golden/file/Wrapper"; 25 | 26 | AesonSpec.goldenDirSpec 27 | File.decodeAutoDependingOnManual 28 | File.encodeAutoDependingOnManual 29 | "autoDependingOnManual" 30 | "golden/file/AutoDependingOnManual"; 31 | 32 | AesonSpec.goldenDirSpec 33 | File.decodeNonGenericType 34 | File.encodeNonGenericType 35 | "nonGenericType" 36 | "golden/file/NonGenericType"; 37 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/Card_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | Card.decodeSuit 4 | Card.encodeSuit 5 | "suit" 6 | "http://localhost:8081/Card/Suit" 7 | "golden/product/Suit"; 8 | 9 | AesonSpec.sampleGoldenAndServerSpec 10 | Card.decodeCard 11 | Card.encodeCard 12 | "card" 13 | "http://localhost:8081/Card/Card" 14 | "golden/product/Card"; 15 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/Company_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | Company.decodeCompany 4 | Company.encodeCompany 5 | "company" 6 | "http://localhost:8081/Company/Company" 7 | "golden/product/Company"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/ComplexProduct_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | ComplexProduct.decodeSimple 4 | ComplexProduct.encodeSimple 5 | "simple" 6 | "http://localhost:8081/ComplexProduct/Simple" 7 | "golden/product/Simple"; 8 | 9 | AesonSpec.sampleGoldenAndServerSpec 10 | ComplexProduct.decodeComplexProduct 11 | ComplexProduct.encodeComplexProduct 12 | "complexProduct" 13 | "http://localhost:8081/ComplexProduct/ComplexProduct" 14 | "golden/product/ComplexProduct"; 15 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/CustomOption_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec CustomOption.decodeCompany2 CustomOption.encodeCompany2 "company2" "http://localhost:8081/CustomOption/Company2" "golden/product/Company2"; 3 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/OneTypeParameter_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | (OneTypeParameter.decodeOneTypeParameter AesonSpec.decodeIntWithResult) 4 | (OneTypeParameter.encodeOneTypeParameter Aeson.Encode.int) 5 | "oneTypeParameter" 6 | "http://localhost:8081/OneTypeParameter/OneTypeParameter" 7 | "golden/product/OneTypeParameter"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/Person_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | Person.decodePerson 4 | Person.encodePerson 5 | "person" 6 | "http://localhost:8081/Person/Person" 7 | "golden/product/Person"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/SimpleChoice_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec SimpleChoice.decodeSimpleChoice SimpleChoice.encodeSimpleChoice "simpleChoice" "http://localhost:8081/SimpleChoice/SimpleChoice" "golden/product/SimpleChoice"; 3 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/SubTypeParameter_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | (SubTypeParameter.decodeSubTypeParameter AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (SubTypeParameter.encodeSubTypeParameter Aeson.Encode.int Aeson.Encode.int Aeson.Encode.int) 5 | "subTypeParameter" 6 | "http://localhost:8081/SubTypeParameter/SubTypeParameter" 7 | "golden/product/SubTypeParameter"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/ThreeTypeParameters_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | (ThreeTypeParameters.decodeThree AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (ThreeTypeParameters.encodeThree Aeson.Encode.int Aeson.Encode.int Aeson.Encode.int) 5 | "three" 6 | "http://localhost:8081/ThreeTypeParameters/Three" 7 | "golden/product/Three"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/TwoTypeParameters_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | (TwoTypeParameters.decodeTwoTypeParameters AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (TwoTypeParameters.encodeTwoTypeParameters Aeson.Encode.int Aeson.Encode.int) 5 | "twoTypeParameters" 6 | "http://localhost:8081/TwoTypeParameters/TwoTypeParameters" 7 | "golden/product/TwoTypeParameters"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product-servant/UnnamedProduct_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | UnnamedProduct.decodeUnnamedProduct 4 | UnnamedProduct.encodeUnnamedProduct 5 | "unnamedProduct" 6 | "http://localhost:8081/UnnamedProduct/UnnamedProduct" 7 | "golden/product/UnnamedProduct"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/Box_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | (Box.decodeInnerBox AesonSpec.decodeIntWithResult) 4 | (Box.encodeInnerBox Aeson.Encode.int) 5 | "innerBox" 6 | "golden/product/InnerBox"; 7 | 8 | AesonSpec.goldenDirSpec 9 | Box.decodeOuterBox 10 | Box.encodeOuterBox 11 | "outerBox" 12 | "golden/product/OuterBox"; 13 | 14 | AesonSpec.goldenDirSpec 15 | Box.decodeAuditAction 16 | Box.encodeAuditAction 17 | "auditAction" 18 | "golden/product/AuditAction"; 19 | 20 | AesonSpec.goldenDirSpec 21 | (Box.decodeAuditModel AesonSpec.decodeIntWithResult) 22 | (Box.encodeAuditModel Aeson.Encode.int) 23 | "auditModel" 24 | "golden/product/AuditModel"; 25 | 26 | AesonSpec.goldenDirSpec 27 | Box.decodeUser 28 | Box.encodeUser 29 | "user" 30 | "golden/product/User"; 31 | 32 | AesonSpec.goldenDirSpec 33 | Box.decodeUserAudit 34 | Box.encodeUserAudit 35 | "userAudit" 36 | "golden/product/UserAudit"; 37 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/Card_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | Card.decodeSuit 4 | Card.encodeSuit 5 | "suit" 6 | "golden/product/Suit"; 7 | 8 | AesonSpec.goldenDirSpec 9 | Card.decodeCard 10 | Card.encodeCard 11 | "card" 12 | "golden/product/Card"; 13 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/Company_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | Company.decodeCompany 4 | Company.encodeCompany 5 | "company" 6 | "golden/product/Company"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/ComplexProduct_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | ComplexProduct.decodeSimple 4 | ComplexProduct.encodeSimple 5 | "simple" 6 | "golden/product/Simple"; 7 | 8 | AesonSpec.goldenDirSpec 9 | ComplexProduct.decodeComplexProduct 10 | ComplexProduct.encodeComplexProduct 11 | "complexProduct" 12 | "golden/product/ComplexProduct"; 13 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/CustomOption_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | CustomOption.decodeCompany2 4 | CustomOption.encodeCompany2 5 | "company2" 6 | "golden/product/Company2"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/Key_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | Key.decodeSqlKey 4 | Key.encodeSqlKey 5 | "sqlKey" 6 | "golden/product/SqlKey"; 7 | 8 | AesonSpec.goldenDirSpec 9 | Key.decodeUserId 10 | Key.encodeUserId 11 | "userId" 12 | "golden/product/UserId"; 13 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/OneTypeParameter_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | (OneTypeParameter.decodeOneTypeParameter AesonSpec.decodeIntWithResult) 4 | (OneTypeParameter.encodeOneTypeParameter Aeson.Encode.int) 5 | "oneTypeParameter" 6 | "golden/product/OneTypeParameter"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/Person_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | Person.decodePerson 4 | Person.encodePerson 5 | "person" 6 | "golden/product/Person"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/SimpleChoice_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | SimpleChoice.decodeSimpleChoice 4 | SimpleChoice.encodeSimpleChoice 5 | "simpleChoice" 6 | "golden/product/SimpleChoice"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/SubTypeParameter_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | (SubTypeParameter.decodeSubTypeParameter AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (SubTypeParameter.encodeSubTypeParameter Aeson.Encode.int Aeson.Encode.int Aeson.Encode.int) 5 | "subTypeParameter" 6 | "golden/product/SubTypeParameter"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/ThreeTypeParameters_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | (ThreeTypeParameters.decodeThree AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (ThreeTypeParameters.encodeThree Aeson.Encode.int Aeson.Encode.int Aeson.Encode.int) 5 | "three" 6 | "golden/product/Three"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/TwoTypeParameters_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | (TwoTypeParameters.decodeTwoTypeParameters AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (TwoTypeParameters.encodeTwoTypeParameters Aeson.Encode.int Aeson.Encode.int) 5 | "twoTypeParameters" 6 | "golden/product/TwoTypeParameters"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/product/UnnamedProduct_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | UnnamedProduct.decodeUnnamedProduct 4 | UnnamedProduct.encodeUnnamedProduct 5 | "unnamedProduct" 6 | "golden/product/UnnamedProduct"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/NameOrIdNumber_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | NameOrIdNumber.decodeNameOrIdNumber 4 | NameOrIdNumber.encodeNameOrIdNumber 5 | "nameOrIdNumber" 6 | "http://localhost:8082/NameOrIdNumber/NameOrIdNumber" 7 | "golden/sum/NameOrIdNumber"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/NewType_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | NewType.decodeNewType 4 | NewType.encodeNewType 5 | "newType" 6 | "http://localhost:8082/NewType/NewType" 7 | "golden/sum/NewType"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/OnOrOff_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | OnOrOff.decodeOnOrOff 4 | OnOrOff.encodeOnOrOff 5 | "onOrOff" 6 | "http://localhost:8082/OnOrOff/OnOrOff" 7 | "golden/sum/OnOrOff"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/Result_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | (Result.decodeResult AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (Result.encodeResult Aeson.Encode.int Aeson.Encode.int) 5 | "result" 6 | "http://localhost:8082/Result/Result" 7 | "golden/sum/Result"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/SingleSum_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | SingleSum.decodeSingleSum 4 | SingleSum.encodeSingleSum 5 | "nameOrIdNumber" 6 | "http://localhost:8082/SingleSum/SingleSum" 7 | "golden/sum/SingleSum"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/SumVariant_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | SumVariant.decodeSumVariant 4 | SumVariant.encodeSumVariant 5 | "sumVariant" 6 | "http://localhost:8082/SumVariant/SumVariant" 7 | "golden/sum/SumVariant"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/SumWithRecord_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | SumWithRecord.decodeSumWithRecord 4 | SumWithRecord.encodeSumWithRecord 5 | "sumWithRecord" 6 | "http://localhost:8082/SumWithRecord/SumWithRecord" 7 | "golden/sum/SumWithRecord"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum-servant/WithTuple_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.sampleGoldenAndServerSpec 3 | WithTuple.decodeWithTuple 4 | WithTuple.encodeWithTuple 5 | "withTuple" 6 | "http://localhost:8082/WithTuple/WithTuple" 7 | "golden/sum/WithTuple"; 8 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/NameOrIdNumber_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | NameOrIdNumber.decodeNameOrIdNumber 4 | NameOrIdNumber.encodeNameOrIdNumber 5 | "nameOrIdNumber" 6 | "golden/sum/NameOrIdNumber"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/NewType_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | NewType.decodeNewType 4 | NewType.encodeNewType 5 | "newType" 6 | "golden/sum/NewType"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/OnOrOff_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | OnOrOff.decodeOnOrOff 4 | OnOrOff.encodeOnOrOff 5 | "onOrOff" 6 | "golden/sum/OnOrOff"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/Result_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | (Result.decodeResult AesonSpec.decodeIntWithResult AesonSpec.decodeIntWithResult) 4 | (Result.encodeResult Aeson.Encode.int Aeson.Encode.int) 5 | "result" 6 | "golden/sum/Result"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/SingleSum_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | SingleSum.decodeSingleSum 4 | SingleSum.encodeSingleSum 5 | "singleSum" 6 | "golden/sum/SingleSum"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/SumVariant_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | SumVariant.decodeSumVariant 4 | SumVariant.encodeSumVariant 5 | "sumVariant" 6 | "golden/sum/SumVariant"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/SumWithRecord_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | SumWithRecord.decodeSumWithRecord 4 | SumWithRecord.encodeSumWithRecord 5 | "sumWithRecord" 6 | "golden/sum/SumWithRecord"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/__tests__/sum/WithTuple_spec.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | AesonSpec.goldenDirSpec 3 | WithTuple.decodeWithTuple 4 | WithTuple.encodeWithTuple 5 | "withTuple" 6 | "golden/sum/WithTuple"; 7 | -------------------------------------------------------------------------------- /test/interface/golden/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ocaml-export", 3 | "bsc-flags": ["-bs-no-version-header", "-bs-super-errors"], 4 | "bs-dependencies": ["bs-aeson"], 5 | "bs-dev-dependencies": ["bs-aeson", "bs-aeson-spec", "bs-node-fetch", "@glennsl/bs-jest"], 6 | "sources": [ 7 | { 8 | "dir": "product", 9 | "subdirs": true 10 | }, 11 | { 12 | "dir": "sum", 13 | "subdirs": true 14 | }, 15 | { 16 | "dir": "file", 17 | "subdirs": true 18 | }, 19 | { 20 | "dir": "__tests__/product", 21 | "subdirs": true, 22 | "type": "dev" 23 | }, 24 | { 25 | "dir": "__tests__/sum", 26 | "subdirs": true, 27 | "type": "dev" 28 | }, 29 | { 30 | "dir": "__tests__/file", 31 | "subdirs": true, 32 | "type": "dev" 33 | } 34 | /* 35 | ,{ 36 | "dir": "__tests__/product-servant", 37 | "subdirs": true, 38 | "type": "dev" 39 | }, 40 | { 41 | "dir": "__tests__/sum-servant", 42 | "subdirs": true, 43 | "type": "dev" 44 | }, 45 | { 46 | "dir": "__tests__/file-servant", 47 | "subdirs": true, 48 | "type": "dev" 49 | } 50 | */ 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /test/interface/golden/file/File.mli: -------------------------------------------------------------------------------- 1 | type person = 2 | { id : int 3 | ; name : (string) option 4 | } 5 | 6 | val encodePerson : person -> Js_json.t 7 | 8 | val decodePerson : Js_json.t -> (person, string) Belt.Result.t 9 | 10 | 11 | type automobile = 12 | { make : string 13 | ; model : string 14 | ; year : int 15 | } 16 | 17 | val encodeAutomobile : automobile -> Js_json.t 18 | 19 | val decodeAutomobile : Js_json.t -> (automobile, string) Belt.Result.t 20 | 21 | type business = 22 | { taxId : string 23 | ; owner : person 24 | ; employees : (person) list 25 | ; companyVehicle : (automobile) option 26 | } 27 | 28 | val encodeBusiness : business -> Js_json.t 29 | 30 | val decodeBusiness : Js_json.t -> (business, string) Belt.Result.t 31 | 32 | 33 | type ('a, 'b) wrapper = 34 | { wrapperA : 'a 35 | ; wrapperB : 'b 36 | ; wrapperC : string 37 | } 38 | 39 | val encodeWrapper : ('a -> Js_json.t) -> ('b -> Js_json.t) -> ('a, 'b) wrapper -> Js_json.t 40 | 41 | val decodeWrapper : (Js_json.t -> ('a, string) Belt.Result.t) -> (Js_json.t -> ('b, string) Belt.Result.t) -> Js_json.t -> (('a, 'b) wrapper, string) Belt.Result.t 42 | 43 | 44 | type autoDependingOnManual = 45 | { abc : string 46 | ; bbBusiness : business 47 | } 48 | 49 | val encodeAutoDependingOnManual : autoDependingOnManual -> Js_json.t 50 | 51 | val decodeAutoDependingOnManual : Js_json.t -> (autoDependingOnManual, string) Belt.Result.t 52 | 53 | type nonGenericType = 54 | { ngA : string 55 | ; ngB : int 56 | } 57 | 58 | val encodeNonGenericType : nonGenericType -> Js_json.t 59 | 60 | val decodeNonGenericType : Js_json.t -> (nonGenericType, string) Belt.Result.t 61 | 62 | -------------------------------------------------------------------------------- /test/interface/golden/golden/file/Automobile/Automobile.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -252353446794561167, 3 | "samples": [ 4 | { 5 | "year": -5, 6 | "model": "ࡀ" 18 | }, 19 | { 20 | "year": 26, 21 | "model": "4l\u001f?C\u0007\u001exqNGU\u001b_b;_b71\nÿ", 22 | "make": "â²Mb\u0015\u001bÞ-Eo" 23 | }, 24 | { 25 | "year": 17, 26 | "model": "", 27 | "make": ".$\u001cfW¶\u0016\u0001‡WQu" 28 | }, 29 | { 30 | "year": 3, 31 | "model": "6\u0018B\u000f\u001a¸½`.V§", 32 | "make": ">PàBlº Ç" 33 | }, 34 | { 35 | "year": 23, 36 | "model": "<{֖«·\u0001¢‹$bPV", 37 | "make": "m?&X´ö»&27-\u000e?b,#\u000c=s\u000e€\u0016A\u0005‡z\u001a-c" 38 | }, 39 | { 40 | "year": -4, 41 | "model": "“\u0011\u001a\u000f#S{aY¶@:y\u0001 ò\u0002u'\u000fшE", 42 | "make": "ÊNÿ€*j×\u0004‘[Dl\u0005\u0019" 43 | }, 44 | { 45 | "year": -5, 46 | "model": "*\u0001DN\u000e[ûV‹0@MBå\u0003\u0000\u0004W\n…”\u0000\u001emh˜", 47 | "make": "ëJ*’eÓþCâ}H4X\u001a}hcú$'\u001b3Rrh\u001a" 48 | }, 49 | { 50 | "year": 1, 51 | "model": "H\u000b\u0016*ˆ\u0019\u00051G\u0003\u0006Qw3“¾\n8~s,\u000c", 52 | "make": "Ò\u001aWZ67D®½ \u0013_\u0008mjE’9" 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/file/NonGenericType/NonGenericType.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 4386779205279759687, 3 | "samples": [ 4 | { 5 | "ngA": "á\u0012Oe?", 6 | "ngB": 16 7 | }, 8 | { 9 | "ngA": "k·vZ{", 10 | "ngB": -16 11 | }, 12 | { 13 | "ngA": "d=\u0005UK\u001aeéØt\u0004\räíhT\u0013‚aw:ö\u0015Dt'", 14 | "ngB": -13 15 | }, 16 | { 17 | "ngA": "\u0001", 18 | "ngB": -2 19 | }, 20 | { 21 | "ngA": "P\u001b6•?n*or/!6Ó]Ré\u0008òK\u000c‹N˜A/ý", 22 | "ngB": -28 23 | }, 24 | { 25 | "ngA": "\u0019q\u0005", 26 | "ngB": 22 27 | }, 28 | { 29 | "ngA": "\u0014u\u0006o—L–{²ç\u001d@sœ\u0013\u0003\u0005\u0005t", 30 | "ngB": 13 31 | }, 32 | { 33 | "ngA": "\"I(>!Q\u001auh\u0014¥\u0016¯©`Á3-r\u0018dE\u0013L", 34 | "ngB": -1 35 | }, 36 | { 37 | "ngA": "\u0016i\"®p\nkH\"\u001aÎ>•bA‡hn\u000caÀn³", 38 | "ngB": -29 39 | }, 40 | { 41 | "ngA": "»D\u000bKï ë4\u0016V;\u0003#³“Õ\u001bf\u0019.", 42 | "ngB": -25 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/file/Person/Person.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 7903380226979960503, 3 | "samples": [ 4 | { 5 | "name": " …`\u000c*e}M†~[:o)\u001b6ŒH@“", 6 | "id": 6 7 | }, 8 | { 9 | "name": "z\u0014\u001c1ҋ+q\u0003èï\u0007\u0010'j|¦:\u0007Ž^DK\u001dsw\u001aY*", 10 | "id": 23 11 | }, 12 | { 13 | "name": "v3ÓÚNg\u001b‡äT\n\u000e\u001bÙÒQ{\u0015M\u0004@~Õ", 14 | "id": -5 15 | }, 16 | { 17 | "name": "99\u0013=L\u001aPF/p/(&‹ã\u0010(Íq,,", 18 | "id": 24 19 | }, 20 | { 21 | "name": "¼5\u0011c`EèÔ]\u0007Wn€S/Tb\u0004%}R’\u00139 ¸", 22 | "id": -15 23 | }, 24 | { 25 | "name": "\u0014Y¯!T2\u0008âû\u0015µ%µ~Mø{\u000b\u00126VlNQrDÔÚ`", 26 | "id": -16 27 | }, 28 | { 29 | "name": "", 30 | "id": -17 31 | }, 32 | { 33 | "name": null, 34 | "id": 30 35 | }, 36 | { 37 | "name": "M™\u000c\"[\u0014\u001dA<\u0005\u001c×\u001e-c¦\u0019", 38 | "id": -26 39 | }, 40 | { 41 | "name": null, 42 | "id": -7 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/file/Wrapper/Wrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -6868269631152827663, 3 | "samples": [ 4 | { 5 | "wrapperC": "3Hÿ¤-|\u000bŒZÊ&G2\r6F", 6 | "wrapperB": 9, 7 | "wrapperA": -18 8 | }, 9 | { 10 | "wrapperC": "ùÀF[\u0017\u0002", 11 | "wrapperB": 21, 12 | "wrapperA": -29 13 | }, 14 | { 15 | "wrapperC": "ì;â,.\u000fnD÷Tyh\u0016Vvµ2WÕ\u001d·\u001a*)\u000f", 16 | "wrapperB": -15, 17 | "wrapperA": -18 18 | }, 19 | { 20 | "wrapperC": "û6a\u001d\u00195;", 21 | "wrapperB": -9, 22 | "wrapperA": -5 23 | }, 24 | { 25 | "wrapperC": "t<-\tvU1\u0014\u001b-Û", 26 | "wrapperB": 28, 27 | "wrapperA": -19 28 | }, 29 | { 30 | "wrapperC": "¬\u0015?¯\u0017", 31 | "wrapperB": 10, 32 | "wrapperA": 23 33 | }, 34 | { 35 | "wrapperC": "JI\u0000V\u0017ï\u0006\u001eU#\u0000D\u0004Q", 36 | "wrapperB": 10, 37 | "wrapperA": -4 38 | }, 39 | { 40 | "wrapperC": "\")}>\t@8\u001bN\nÁ[åGÃ\u001b", 46 | "wrapperB": -1, 47 | "wrapperA": -14 48 | }, 49 | { 50 | "wrapperC": "p#8l", 51 | "wrapperB": -6, 52 | "wrapperA": 25 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/AuditAction/Create.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -5501544309030902648, 3 | "samples": [ 4 | "Create", 5 | "Create", 6 | "Create", 7 | "Create", 8 | "Create", 9 | "Create", 10 | "Create", 11 | "Create", 12 | "Create", 13 | "Create" 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/AuditAction/Delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 5301502580092110989, 3 | "samples": [ 4 | "Delete", 5 | "Delete", 6 | "Delete", 7 | "Delete", 8 | "Delete", 9 | "Delete", 10 | "Delete", 11 | "Delete", 12 | "Delete", 13 | "Delete" 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/AuditAction/Update.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 8067707645285678060, 3 | "samples": [ 4 | "Update", 5 | "Update", 6 | "Update", 7 | "Update", 8 | "Update", 9 | "Update", 10 | "Update", 11 | "Update", 12 | "Update", 13 | "Update" 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/AuditModel/AuditModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -4331257071399132001, 3 | "samples": [ 4 | { 5 | "editedOn": "14+Rh~󓞧M0>󇍜*\u0004񚸍􊧼7\u0003\u0010V", 6 | "originalId": ")󛓂%󢇾V_񅛠_v5Z𑶿-\u00167v", 7 | "auditModel": -24, 8 | "editedBy": "J!", 9 | "auditAction": "Update" 10 | }, 11 | { 12 | "editedOn": "懴\u0003cS+\u000eN񂓨󹫱\r𔧙", 13 | "originalId": "\u0001[\u0017񩸼{=񍛠uP򤆾򰕥!􋭲22z", 14 | "auditModel": -22, 15 | "editedBy": "󬼚3{\u0000!\u000c񀇕.1et\u0001kn\"\u000f\u0016@Y1%F𼴝}a\u001f", 16 | "auditAction": "Create" 17 | }, 18 | { 19 | "editedOn": "]\u0013j󅤄t񳓯u}6)񵧴^󄢚􁎛Q.", 20 | "originalId": "P\u0003򚻚8\"t4𧆛󎒁9󿞶򐇾C|iba'", 21 | "auditModel": -10, 22 | "editedBy": "jw𷨨󵧬𠶚", 23 | "auditAction": "Update" 24 | }, 25 | { 26 | "editedOn": "d𣤗jR", 27 | "originalId": "\u0013Z񆛴𩕐u4\u0016\u0000\u0018;𹟭󧻻8𦱅񢿯\"]Tp뀢", 28 | "auditModel": 11, 29 | "editedBy": "{-\u000f?型Bt󎆄􃚖_^𿝜񋔕)򚒨󰯊FU򭒞󹄍\\\u0000򩻒", 30 | "auditAction": "Delete" 31 | }, 32 | { 33 | "editedOn": "%\u001b.񼖒^ik\u0005wb~\u0007&O(\u0011򱥓gh󯎾%kN𡔔\u0015𲍲*򌵦 ", 34 | "originalId": "򂵽Y*ay;R\u000c𒴑񷛃򐷙𔖰򉫎l򒚏m", 35 | "auditModel": 7, 36 | "editedBy": "\u0010\u000ew򳗼^'%𑰾Oq(򿢮DI<\n", 37 | "auditAction": "Create" 38 | }, 39 | { 40 | "editedOn": "\u0008#򾄵z/ID񝨑x󊋖𯅲2𮂬򮘫Pw)\u0012b0~񲾵@ZX3", 41 | "originalId": "<򇖑\u0014>\u0004\u0019zglV-~7", 42 | "auditModel": -24, 43 | "editedBy": "`\u0001\u001e\u000eJ𸭮u*\u001aWP", 44 | "auditAction": "Update" 45 | }, 46 | { 47 | "editedOn": "b:WRH)򖔀򜴸c:󒍵\u000f\u0013", 48 | "originalId": "􅴁𺫌6B_\u001b&𪘛C&<\u0000񆩞", 49 | "auditModel": -15, 50 | "editedBy": "i-\u0008꒍2󀙵\\-\u0019 5#8񙲀󤣓M\u0008򽬠i\"R-$񓤺(𬗼򞸋NG", 51 | "auditAction": "Update" 52 | }, 53 | { 54 | "editedOn": "", 55 | "originalId": "8u󽇪󒤇si=op𙉅\u001bp񂫻\u001f@\u001dnc󝔴\u000fc\u0018\u0018\u0006\u0004Or3󿠤\u0016", 56 | "auditModel": -27, 57 | "editedBy": "\u0018S\u0006\u000c93{", 58 | "auditAction": "Delete" 59 | }, 60 | { 61 | "editedOn": "N4񲭊4{+񰛵󘽀9𡈼𪆙u\u00121b񙭫", 62 | "originalId": "Rh G\u0004^󾀓\u000eXYR", 63 | "auditModel": -19, 64 | "editedBy": "hw\u0017𫼆9񧨶/\u0003\u000e[8(\u001e򞭱j*򵏐$򿀤K\u0019N󀀔ynX󆖊Ky", 65 | "auditAction": "Create" 66 | }, 67 | { 68 | "editedOn": "^:𬉍<\u0019$}6\u0008", 29 | "id": 15 30 | } 31 | }, 32 | { 33 | "address2": "\u001dL3\u001c\t)\u0005𬮚E\u001fv򷧎]7", 34 | "boss": null 35 | }, 36 | { 37 | "address2": "a񢭛\u000e𗃐`󌖏H\u0016%㨠𴲬凷iX;\u001d|࿟􀼪", 38 | "boss": { 39 | "created": "1864-06-06T00:00:01.011Z", 40 | "name": "A\u000f>$󎐙", 41 | "id": 11 42 | } 43 | }, 44 | { 45 | "address2": "\u0002\u000bYs2\u0005\u0013y𓂥KjK\u000f\u0012=\u001d󪟪򣇫f򖢁F7{<", 46 | "boss": { 47 | "created": "1864-05-12T00:00:01.011Z", 48 | "name": ",f󪧞'=򫅃|]$\u000e\u001e򯻀󛄽􏁑\u0012#𰈙-y𓓶>\u0016", 49 | "id": -23 50 | } 51 | }, 52 | { 53 | "address2": "󗹧S\u0015`󙼔񽰔񲖃R􊏽\u0016W𺍰\u0016;G:򿗅G\u0006.񏑜w򡽱󝼞", 54 | "boss": { 55 | "created": "1864-06-03T00:00:01.011Z", 56 | "name": "K𓺂@INb$0\u0003h\u0013\u001e+", 57 | "id": 30 58 | } 59 | }, 60 | { 61 | "address2": "{8", 62 | "boss": null 63 | }, 64 | { 65 | "address2": "q󦎰󣐊*~5]$s󜦋񓧎*\u0008+\n򕢎󅜍\u0019󽪶2FBSj񼊜R\u00058]h", 66 | "boss": { 67 | "created": "1864-05-15T00:00:01.011Z", 68 | "name": "\u0006$\u0002N\"\u0000K\u0004iO\u0004`j]]6𧺘񊸞𙺿\u000cl86\u0016`Sr\u0013򰆻", 69 | "id": 30 70 | } 71 | } 72 | ] 73 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/ComplexWrapped/ComplexWrapped.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 8749844924866748307, 3 | "samples": [ 4 | { 5 | "cw": { 6 | "wpa": { 7 | "Right": -27.08048962139064 8 | } 9 | } 10 | }, 11 | { 12 | "cw": { 13 | "wpa": { 14 | "Left": null 15 | } 16 | } 17 | }, 18 | { 19 | "cw": { 20 | "wpa": { 21 | "Left": "\u0006" 22 | } 23 | } 24 | }, 25 | { 26 | "cw": { 27 | "wpa": { 28 | "Right": 26.11151959581339 29 | } 30 | } 31 | }, 32 | { 33 | "cw": { 34 | "wpa": { 35 | "Left": null 36 | } 37 | } 38 | }, 39 | { 40 | "cw": { 41 | "wpa": { 42 | "Left": "Z" 43 | } 44 | } 45 | }, 46 | { 47 | "cw": { 48 | "wpa": { 49 | "Right": -28.62596124078363 50 | } 51 | } 52 | }, 53 | { 54 | "cw": { 55 | "wpa": { 56 | "Right": 99.33759514062304 57 | } 58 | } 59 | }, 60 | { 61 | "cw": { 62 | "wpa": { 63 | "Left": null 64 | } 65 | } 66 | }, 67 | { 68 | "cw": { 69 | "wpa": { 70 | "Left": "q" 71 | } 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/EitherWrapped/EitherWrapped.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -3460743836191451419, 3 | "samples": [ 4 | { 5 | "ew": { 6 | "wpa": { 7 | "Right": -12.413585643688497 8 | } 9 | } 10 | }, 11 | { 12 | "ew": { 13 | "wpa": { 14 | "Right": -14.961549684281188 15 | } 16 | } 17 | }, 18 | { 19 | "ew": { 20 | "wpa": { 21 | "Left": 19 22 | } 23 | } 24 | }, 25 | { 26 | "ew": { 27 | "wpa": { 28 | "Right": -181.80462499555927 29 | } 30 | } 31 | }, 32 | { 33 | "ew": { 34 | "wpa": { 35 | "Right": 19.11136304874505 36 | } 37 | } 38 | }, 39 | { 40 | "ew": { 41 | "wpa": { 42 | "Left": 14 43 | } 44 | } 45 | }, 46 | { 47 | "ew": { 48 | "wpa": { 49 | "Left": 27 50 | } 51 | } 52 | }, 53 | { 54 | "ew": { 55 | "wpa": { 56 | "Right": 3.8877572537736302 57 | } 58 | } 59 | }, 60 | { 61 | "ew": { 62 | "wpa": { 63 | "Left": 0 64 | } 65 | } 66 | }, 67 | { 68 | "ew": { 69 | "wpa": { 70 | "Right": -11.018936377199028 71 | } 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/HalfWrapped/HalfWrapped.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 8960212111377330785, 3 | "samples": [ 4 | { 5 | "hw": { 6 | "wpa": { 7 | "Left": 3 8 | } 9 | } 10 | }, 11 | { 12 | "hw": { 13 | "wpa": { 14 | "Right": -30 15 | } 16 | } 17 | }, 18 | { 19 | "hw": { 20 | "wpa": { 21 | "Right": -16 22 | } 23 | } 24 | }, 25 | { 26 | "hw": { 27 | "wpa": { 28 | "Right": 27 29 | } 30 | } 31 | }, 32 | { 33 | "hw": { 34 | "wpa": { 35 | "Right": 28 36 | } 37 | } 38 | }, 39 | { 40 | "hw": { 41 | "wpa": { 42 | "Left": 5 43 | } 44 | } 45 | }, 46 | { 47 | "hw": { 48 | "wpa": { 49 | "Right": 29 50 | } 51 | } 52 | }, 53 | { 54 | "hw": { 55 | "wpa": { 56 | "Left": -15 57 | } 58 | } 59 | }, 60 | { 61 | "hw": { 62 | "wpa": { 63 | "Left": -22 64 | } 65 | } 66 | }, 67 | { 68 | "hw": { 69 | "wpa": { 70 | "Left": -15 71 | } 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/InnerBox/InnerBox.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -7851554943605849957, 3 | "samples": [ 4 | { 5 | "ibA": 2, 6 | "ibS": "񱅷?O$KjxT`#8\u0011\u001fxl?$UC2램;.b)4i\tG򒢠", 7 | "ibX": 7 8 | }, 9 | { 10 | "ibA": 15, 11 | "ibS": "Q\"#𓻧򙶓𠕐~, ftx", 12 | "ibX": -2 13 | }, 14 | { 15 | "ibA": 25, 16 | "ibS": "L򲈟", 17 | "ibX": -15 18 | }, 19 | { 20 | "ibA": 25, 21 | "ibS": "𴫱񰀶򂃝\t򁇥a𥲁t3q`{򰩀\u0001\u000c\u001aZ)c6\u0002/󏭙򳭞dj", 47 | "ibX": -10 48 | }, 49 | { 50 | "ibA": { 51 | "created": "1864-05-14T00:00:01.011Z", 52 | "name": "\u0007󾣮󝥖򣢱}₟5񙊧;7i\u000by\u0006$HU3fK\u0008\u001a}2򾏄6]", 53 | "id": -2 54 | }, 55 | "ibS": "^2C5񮢪񑽳", 56 | "ibX": -8 57 | }, 58 | { 59 | "ibA": { 60 | "created": "1864-06-08T00:00:01.011Z", 61 | "name": "T", 62 | "id": -15 63 | }, 64 | "ibS": "x\u001bゼ񜓜6𶁏񳥥;\u001fwo44N", 65 | "ibX": -1 66 | }, 67 | { 68 | "ibA": { 69 | "created": "1864-05-29T00:00:01.011Z", 70 | "name": null, 71 | "id": 19 72 | }, 73 | "ibS": "\u000f\u0000\rB9", 74 | "ibX": 29 75 | }, 76 | { 77 | "ibA": { 78 | "created": "1864-05-20T00:00:01.011Z", 79 | "name": null, 80 | "id": 13 81 | }, 82 | "ibS": "Ci1qpG=򖼐;M\u0016􍜫q񅋆^򑒠5a\u0003\u0001\u0010𽱥򑷖𡹾􄉫𺩌", 83 | "ibX": -9 84 | }, 85 | { 86 | "ibA": { 87 | "created": "1864-06-01T00:00:01.011Z", 88 | "name": "\u0003\u0019󷛧\r![\u0003ceJI񀜀]AU", 89 | "id": -30 90 | }, 91 | "ibS": "\u0012G_󺮘\u000e>\u001b󝱝󏘦𷿒\u0010t%違񁅘{𸾈D\u001ffXl\u0014𻯌\u001e(S\u000bd\u0010", 92 | "ibX": 6 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/PartiallyWrapped/PartiallyWrapped.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -797789241917621471, 3 | "samples": [ 4 | { 5 | "pw": { 6 | "wpa": { 7 | "Left": -30 8 | } 9 | } 10 | }, 11 | { 12 | "pw": { 13 | "wpa": { 14 | "Left": 27 15 | } 16 | } 17 | }, 18 | { 19 | "pw": { 20 | "wpa": { 21 | "Left": -1 22 | } 23 | } 24 | }, 25 | { 26 | "pw": { 27 | "wpa": { 28 | "Left": 8 29 | } 30 | } 31 | }, 32 | { 33 | "pw": { 34 | "wpa": { 35 | "Left": -17 36 | } 37 | } 38 | }, 39 | { 40 | "pw": { 41 | "wpa": { 42 | "Left": 19 43 | } 44 | } 45 | }, 46 | { 47 | "pw": { 48 | "wpa": { 49 | "Left": 28 50 | } 51 | } 52 | }, 53 | { 54 | "pw": { 55 | "wpa": { 56 | "Left": -14 57 | } 58 | } 59 | }, 60 | { 61 | "pw": { 62 | "wpa": { 63 | "Left": 7 64 | } 65 | } 66 | }, 67 | { 68 | "pw": { 69 | "wpa": { 70 | "Left": 20 71 | } 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/Person/Person.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 6820255037867830086, 3 | "samples": [ 4 | { 5 | "created": "1864-04-11T00:00:01.011Z", 6 | "name": "򛟽 \u0007~2GY󍂧\u000f$s󀢮\u0002@\u0017z\u0002\u000e\t", 7 | "id": -22 8 | }, 9 | { 10 | "created": "1864-04-25T00:00:01.011Z", 11 | "name": null, 12 | "id": 11 13 | }, 14 | { 15 | "created": "1864-04-12T00:00:01.011Z", 16 | "name": "{K2^X\u0005򚸳'G󖬆4򊌋򮵙񃹷𫡛1", 17 | "id": 12 18 | }, 19 | { 20 | "created": "1864-04-10T00:00:01.011Z", 21 | "name": null, 22 | "id": -1 23 | }, 24 | { 25 | "created": "1864-04-26T00:00:01.011Z", 26 | "name": "􎜨\u0001", 27 | "id": 24 28 | }, 29 | { 30 | "created": "1864-05-29T00:00:01.011Z", 31 | "name": "$Y\u000f,u\u001d󤻂m\u0005󕦛J/\u0000\u0008}", 32 | "id": 28 33 | }, 34 | { 35 | "created": "1864-06-03T00:00:01.011Z", 36 | "name": "kj񀞓Ma񢜕䅹󧣣nyLB񛥩񜽠}\u000c򙶿-w\u0010I􂷸𼐷_\n񎎪𭉵\u0010*.", 37 | "id": 0 38 | }, 39 | { 40 | "created": "1864-04-24T00:00:01.011Z", 41 | "name": null, 42 | "id": -26 43 | }, 44 | { 45 | "created": "1864-04-28T00:00:01.011Z", 46 | "name": "rED-X@0Pc򷂧qU󶗡om\u0011󣂆a󘐆\u0002󍖢𑅨Y+", 47 | "id": -10 48 | }, 49 | { 50 | "created": "1864-05-22T00:00:01.011Z", 51 | "name": null, 52 | "id": 24 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/ScrambledTypeParameterRefs/ScrambledTypeParameterRefs.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 3711347212009762416, 3 | "samples": [ 4 | { 5 | "stprb": 10, 6 | "stpre": -10, 7 | "stprd": -1, 8 | "stpra": 22, 9 | "stprf": 9, 10 | "stprc": 29 11 | }, 12 | { 13 | "stprb": -8, 14 | "stpre": -9, 15 | "stprd": -4, 16 | "stpra": 29, 17 | "stprf": 15, 18 | "stprc": 1 19 | }, 20 | { 21 | "stprb": -21, 22 | "stpre": -7, 23 | "stprd": 25, 24 | "stpra": 25, 25 | "stprf": 29, 26 | "stprc": -15 27 | }, 28 | { 29 | "stprb": 2, 30 | "stpre": -1, 31 | "stprd": 14, 32 | "stpra": -6, 33 | "stprf": -30, 34 | "stprc": -26 35 | }, 36 | { 37 | "stprb": 20, 38 | "stpre": 4, 39 | "stprd": -20, 40 | "stpra": 26, 41 | "stprf": 8, 42 | "stprc": -27 43 | }, 44 | { 45 | "stprb": 26, 46 | "stpre": 9, 47 | "stprd": -23, 48 | "stpra": 10, 49 | "stprf": 11, 50 | "stprc": -4 51 | }, 52 | { 53 | "stprb": 4, 54 | "stpre": -12, 55 | "stprd": -20, 56 | "stpra": 6, 57 | "stprf": 10, 58 | "stprc": -16 59 | }, 60 | { 61 | "stprb": -25, 62 | "stpre": 14, 63 | "stprd": -2, 64 | "stpra": 29, 65 | "stprf": 25, 66 | "stprc": 24 67 | }, 68 | { 69 | "stprb": 17, 70 | "stpre": 23, 71 | "stprd": 6, 72 | "stpra": -18, 73 | "stprf": -13, 74 | "stprc": -20 75 | }, 76 | { 77 | "stprb": 22, 78 | "stpre": 28, 79 | "stprd": -2, 80 | "stpra": -18, 81 | "stprf": -15, 82 | "stprc": -29 83 | } 84 | ] 85 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/SecondLayer/SecondLayer.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -23528167967323917, 3 | "samples": [ 4 | { 5 | "sl": { 6 | "wp2a": 22, 7 | "wp2b": -18 8 | }, 9 | "zed": "DtQƒÜ\u0005ß\u0003GS=e\u001e\u001c8ø\u0008\u000c]3\u000f«P\u000eSþòCî" 10 | }, 11 | { 12 | "sl": { 13 | "wp2a": 18, 14 | "wp2b": -22 15 | }, 16 | "zed": "{oh.\u0011gJÓzÔã<\u0013?lUwòà×\u0001û" 17 | }, 18 | { 19 | "sl": { 20 | "wp2a": -30, 21 | "wp2b": -24 22 | }, 23 | "zed": "¹[\u0004lkBý\u0018r¹œ" 24 | }, 25 | { 26 | "sl": { 27 | "wp2a": -8, 28 | "wp2b": -27 29 | }, 30 | "zed": "/@\u0000D•5ARæR7\u0013\u0004\u0019\u0007\u000fÓÚq—\u0010i\u001di" 31 | }, 32 | { 33 | "sl": { 34 | "wp2a": 28, 35 | "wp2b": -20 36 | }, 37 | "zed": "9+\u000e_K$,OJÌgh\u00198Ì/\u0015a9Ò» x£\u00189\u0014r" 38 | }, 39 | { 40 | "sl": { 41 | "wp2a": 17, 42 | "wp2b": 27 43 | }, 44 | "zed": "\u0016>ùÓAv31}'.{˜!:W‚d\u0005-§V\u0006\u0018É" 45 | }, 46 | { 47 | "sl": { 48 | "wp2a": 10, 49 | "wp2b": 16 50 | }, 51 | "zed": "'Õ~E4" 52 | }, 53 | { 54 | "sl": { 55 | "wp2a": 18, 56 | "wp2b": 26 57 | }, 58 | "zed": "v’ZnÄ|T\u0019Ó\u0019\u0017kÅ*Ê\u0011Ö¼‘4&z2Î" 59 | }, 60 | { 61 | "sl": { 62 | "wp2a": -16, 63 | "wp2b": 11 64 | }, 65 | "zed": "G¿¤B\u0006º'\u001fÁ|;k\u000eU\u000e5" 66 | }, 67 | { 68 | "sl": { 69 | "wp2a": 10, 70 | "wp2b": -13 71 | }, 72 | "zed": "P\u00030p3\u0001Z\u0010^b;r$\u0016'ßJp\u001a" 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/Simple/Simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 1801824973039036285, 3 | "samples": [ 4 | { 5 | "sa": 4, 6 | "sb": "򎯅6\u0019񩞴Y\u001e[t'=u\u0017^󧄔a,p" 37 | } 38 | }, 39 | { 40 | "choice": { 41 | "Right": 12 42 | } 43 | }, 44 | { 45 | "choice": { 46 | "Left": ",|B`][e\n@󙩊n1[\rh\u0010\u0007󕛺" 47 | } 48 | }, 49 | { 50 | "choice": { 51 | "Left": "W^\u001c𻝶򉙩]𓋷)U񩞓h\u0013򨧈+", 90 | -19.195050150777067 91 | ] 92 | } 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/TwoTypeParameters/TwoTypeParameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 1476930007050195571, 3 | "samples": [ 4 | { 5 | "ttpFirst": 14, 6 | "ttpSecond": -24, 7 | "ttpId": 10, 8 | "ttpThird": [ 9 | 25, 10 | 20 11 | ] 12 | }, 13 | { 14 | "ttpFirst": 3, 15 | "ttpSecond": 9, 16 | "ttpId": -12, 17 | "ttpThird": [ 18 | -23, 19 | 20 20 | ] 21 | }, 22 | { 23 | "ttpFirst": 6, 24 | "ttpSecond": -26, 25 | "ttpId": -14, 26 | "ttpThird": [ 27 | 19, 28 | -20 29 | ] 30 | }, 31 | { 32 | "ttpFirst": 3, 33 | "ttpSecond": -20, 34 | "ttpId": -28, 35 | "ttpThird": [ 36 | -22, 37 | -15 38 | ] 39 | }, 40 | { 41 | "ttpFirst": 9, 42 | "ttpSecond": 22, 43 | "ttpId": 1, 44 | "ttpThird": [ 45 | -1, 46 | 18 47 | ] 48 | }, 49 | { 50 | "ttpFirst": -18, 51 | "ttpSecond": -24, 52 | "ttpId": 10, 53 | "ttpThird": [ 54 | -10, 55 | -3 56 | ] 57 | }, 58 | { 59 | "ttpFirst": -4, 60 | "ttpSecond": 20, 61 | "ttpId": 3, 62 | "ttpThird": [ 63 | -12, 64 | 17 65 | ] 66 | }, 67 | { 68 | "ttpFirst": 13, 69 | "ttpSecond": -15, 70 | "ttpId": -29, 71 | "ttpThird": [ 72 | 21, 73 | -30 74 | ] 75 | }, 76 | { 77 | "ttpFirst": -16, 78 | "ttpSecond": -17, 79 | "ttpId": 2, 80 | "ttpThird": [ 81 | 6, 82 | -28 83 | ] 84 | }, 85 | { 86 | "ttpFirst": 29, 87 | "ttpSecond": -23, 88 | "ttpId": -18, 89 | "ttpThird": [ 90 | 26, 91 | 21 92 | ] 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/UnnamedProduct/UnnamedProduct.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 5356500220752652327, 3 | "samples": [ 4 | [ 5 | "\\`p\\\u0008\u000c𑸯򿭋", 6 | -19 7 | ], 8 | [ 9 | "񥋷𑀀񮗭", 10 | -20 11 | ], 12 | [ 13 | "`>\t6SG􇒪5U񢢢𗹾Y𰾳1󔔐d\u0016\u001a\u0013zj\u0014󽖴𾰫\u000b\u001bU", 14 | -12 15 | ], 16 | [ 17 | "񫯦\u0010N'񡩴\u0010@.\u001d2񍁦<\u0014$)1L򷮟tT\u0006$", 18 | -6 19 | ], 20 | [ 21 | "Lv񦉽񴮯", 22 | 3 23 | ], 24 | [ 25 | "2Q\u0010󏡒\u001d=+\u0005\u001c1\u000b3\u0019\u0016\u0003󺻠C\u001b*NE_", 26 | -1 27 | ], 28 | [ 29 | "teC񔇯g2巊\u00050", 30 | -26 31 | ], 32 | [ 33 | "񏎅\u001eVP{\u0006D뮰+J⨢\u0008", 34 | -23 35 | ], 36 | [ 37 | ";/i򹍶󢹋", 38 | -22 39 | ], 40 | [ 41 | "n<񦩘p񲩝{Q*BE[\u00148\u0004𺳹󐣮>񗆯𺉗\nZ򍟏", 42 | -5 43 | ] 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/User/User.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 4516830748937394304, 3 | "samples": [ 4 | { 5 | "userIdent": "􍤠񺳳DbS", 6 | "userPassword": null, 7 | "userTimezone": "󆧂^򾈵\u0015\u000f,񆙝\u0017" 8 | }, 9 | { 10 | "userIdent": "N\u0001O􎬱", 11 | "userPassword": null, 12 | "userTimezone": "k􆿈z=򻞒\u0012[\u001b_r\u0016x\u001e񣴀󃁰" 13 | }, 14 | { 15 | "userIdent": "]M", 16 | "userPassword": "󶁼\u0019\u000c\u0018-\"\"O\u000eV󷇔𬝻񞺄cD\u001d\u0006C\u0003~{-񀄵", 17 | "userTimezone": "򸗫򖖥S𠽯H\u0015񱒥X" 18 | }, 19 | { 20 | "userIdent": ",\u001f&R<\u0011\u0008", 21 | "userPassword": null, 22 | "userTimezone": "/𲄷G򟤏\u0011Y$-z^򋄢\u0008埵\u000b\u0008}\u0006O{w\u001f-󳷏" 23 | }, 24 | { 25 | "userIdent": "Qbd3⏡px^z[򧋇򍅈291=𫱙o淓}", 26 | "userPassword": null, 27 | "userTimezone": "" 28 | }, 29 | { 30 | "userIdent": "", 31 | "userPassword": "l\u0002񰓚]\u0003u\u0013v𜫀\t&%\t󎠴7", 32 | "userTimezone": "𛌣'𸫸򡂉62(󣊽񽺧J񗢎񥌩nFB" 33 | }, 34 | { 35 | "userIdent": "z/u\u0012tRG\u001dP=xS\u001e𨒍=H\u000c\u001a", 36 | "userPassword": "x,󒿲D򩼛򝝹򊣙\u001a󖐷\u001d^\u001dJj\"M(\u000b&􎞧񱃦򖜥Ub򴧡򞜙HB\u0019", 37 | "userTimezone": "M\u000e${7HxZ8\u0006c%Ap" 38 | }, 39 | { 40 | "userIdent": "\u0017H\u0011nrK", 41 | "userPassword": null, 42 | "userTimezone": "𧳏r,'FX𻾽򲙻" 43 | }, 44 | { 45 | "userIdent": "\u001d򪢢徹\u0000򄑜B7r]R,\u001ea{d񬆢򛝏\u0011򶢉\u001a𔺍󠔗\u0012\u001d", 46 | "userPassword": "𗔮𾶉\u0018򣖎\u0016/F0", 47 | "userTimezone": "ZN\u001cwZ񅓆EP\u000e񦘮󫴓􍋓k75v??󴑥\u00141 &;" 48 | }, 49 | { 50 | "userIdent": "񅘯\u0013\rP󒀸񔘖𹀂󼛓R󳹉1.\u0019J򦁋󐉠8", 51 | "userPassword": "+ꤻ\u0017Q򍜫@\u00040U5\u000b򴕪c\\/v󷴄d\\\u001a#", 52 | "userTimezone": "X󧕎!r򰣐F򉘫񛾦Bx" 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/UserId/UserId.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -9154406842179955350, 3 | "samples": [ 4 | 28, 5 | 30, 6 | -26, 7 | -3, 8 | 8, 9 | 19, 10 | 21, 11 | 30, 12 | 6, 13 | -2 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/WrapThree/WrapThree.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -497434135695006380, 3 | "samples": [ 4 | { 5 | "wp2a": 24, 6 | "wp2cb": [ 7 | 0, 8 | 0 9 | ], 10 | "wp2ab": [ 11 | -3, 12 | -22 13 | ], 14 | "wp2b": 15 15 | }, 16 | { 17 | "wp2a": -28, 18 | "wp2cb": [ 19 | 25, 20 | -14 21 | ], 22 | "wp2ab": [ 23 | 16, 24 | -11 25 | ], 26 | "wp2b": 8 27 | }, 28 | { 29 | "wp2a": -18, 30 | "wp2cb": [ 31 | -2, 32 | 13 33 | ], 34 | "wp2ab": [ 35 | -1, 36 | -4 37 | ], 38 | "wp2b": 0 39 | }, 40 | { 41 | "wp2a": -29, 42 | "wp2cb": [ 43 | 20, 44 | 11 45 | ], 46 | "wp2ab": [ 47 | -14, 48 | -30 49 | ], 50 | "wp2b": 26 51 | }, 52 | { 53 | "wp2a": 28, 54 | "wp2cb": [ 55 | 1, 56 | 22 57 | ], 58 | "wp2ab": [ 59 | 19, 60 | 21 61 | ], 62 | "wp2b": 28 63 | }, 64 | { 65 | "wp2a": 1, 66 | "wp2cb": [ 67 | 25, 68 | -27 69 | ], 70 | "wp2ab": [ 71 | -28, 72 | -28 73 | ], 74 | "wp2b": -4 75 | }, 76 | { 77 | "wp2a": 14, 78 | "wp2cb": [ 79 | -22, 80 | -8 81 | ], 82 | "wp2ab": [ 83 | 23, 84 | -24 85 | ], 86 | "wp2b": -3 87 | }, 88 | { 89 | "wp2a": 13, 90 | "wp2cb": [ 91 | -15, 92 | 26 93 | ], 94 | "wp2ab": [ 95 | 20, 96 | 6 97 | ], 98 | "wp2b": 16 99 | }, 100 | { 101 | "wp2a": -27, 102 | "wp2cb": [ 103 | 2, 104 | 10 105 | ], 106 | "wp2ab": [ 107 | -17, 108 | -1 109 | ], 110 | "wp2b": -29 111 | }, 112 | { 113 | "wp2a": -26, 114 | "wp2cb": [ 115 | -9, 116 | -28 117 | ], 118 | "wp2ab": [ 119 | 12, 120 | 12 121 | ], 122 | "wp2b": -30 123 | } 124 | ] 125 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/WrappedWrapper/WrappedWrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -4541499265744098089, 3 | "samples": [ 4 | { 5 | "ww": { 6 | "wpa": null 7 | } 8 | }, 9 | { 10 | "ww": { 11 | "wpa": 28 12 | } 13 | }, 14 | { 15 | "ww": { 16 | "wpa": null 17 | } 18 | }, 19 | { 20 | "ww": null 21 | }, 22 | { 23 | "ww": { 24 | "wpa": 3 25 | } 26 | }, 27 | { 28 | "ww": { 29 | "wpa": 17 30 | } 31 | }, 32 | { 33 | "ww": { 34 | "wpa": -5 35 | } 36 | }, 37 | { 38 | "ww": { 39 | "wpa": 29 40 | } 41 | }, 42 | { 43 | "ww": { 44 | "wpa": -29 45 | } 46 | }, 47 | { 48 | "ww": null 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/Wrapper/Wrapper.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -8767615348653689682, 3 | "samples": [ 4 | { 5 | "wpa": 10 6 | }, 7 | { 8 | "wpa": -8 9 | }, 10 | { 11 | "wpa": -15 12 | }, 13 | { 14 | "wpa": 3 15 | }, 16 | { 17 | "wpa": -17 18 | }, 19 | { 20 | "wpa": 13 21 | }, 22 | { 23 | "wpa": 0 24 | }, 25 | { 26 | "wpa": 15 27 | }, 28 | { 29 | "wpa": -6 30 | }, 31 | { 32 | "wpa": -21 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/product/Wrapper2/Wrapper2.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -3206408502528725740, 3 | "samples": [ 4 | { 5 | "wp2a": 17, 6 | "wp2b": 15 7 | }, 8 | { 9 | "wp2a": 1, 10 | "wp2b": -6 11 | }, 12 | { 13 | "wp2a": 27, 14 | "wp2b": 28 15 | }, 16 | { 17 | "wp2a": -1, 18 | "wp2b": 16 19 | }, 20 | { 21 | "wp2a": 9, 22 | "wp2b": -27 23 | }, 24 | { 25 | "wp2a": -27, 26 | "wp2b": 12 27 | }, 28 | { 29 | "wp2a": -27, 30 | "wp2b": 5 31 | }, 32 | { 33 | "wp2a": -14, 34 | "wp2b": -11 35 | }, 36 | { 37 | "wp2a": 19, 38 | "wp2b": 13 39 | }, 40 | { 41 | "wp2a": 19, 42 | "wp2b": -17 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/subs/A/A.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 6325633533011437127, 3 | "samples": [ 4 | -24, 5 | 16, 6 | 8, 7 | 2, 8 | -17, 9 | -17, 10 | 18, 11 | 19, 12 | -9, 13 | 19 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/subs/B/B.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -2094573881888746220, 3 | "samples": [ 4 | [ 5 | "*&5{\rë:W\u001d", 6 | -30 7 | ], 8 | [ 9 | "v\u0004\u001b\u0011I\u0010\u0012\u0017\u001d#d\u0004/¼\u0002Ól\u0018y¼\u001cxy5", 10 | 5 11 | ], 12 | [ 13 | "\u00189\u0012\u0011Z\u000eaü¢\u000e+:MSÔÞ\u001f?\\SuI", 14 | 2 15 | ], 16 | [ 17 | "ül=+t\u000f‰/q<—2,BZ\u001f¤x^Vbn\u001dr\u001d\t\u001d", 18 | 1 19 | ], 20 | [ 21 | "@\u001c\u0013\u000cÙ!C", 22 | 7 23 | ], 24 | [ 25 | "'.®", 26 | 5 27 | ], 28 | [ 29 | "°J\u0004!C´Oi_J[\u0004", 30 | -16 31 | ], 32 | [ 33 | "pD\u0000tirW^\u00014󻛤񈒇\u001bO򟀯" 27 | }, 28 | { 29 | "tag": "Name", 30 | "contents": "W\u0000\u0013k(" 31 | }, 32 | { 33 | "tag": "Name", 34 | "contents": "򯒴󜺃򀸣LAQ@g<󷃒|^󍕫򩈅\u0006!n(C" 35 | }, 36 | { 37 | "tag": "Name", 38 | "contents": "􅍘0\u000b6&)YBX\u00142\u00116򨂗=󄰀C-\u0004􈒌񫑕񨥉\u0001:𖓡\u001frw" 39 | }, 40 | { 41 | "tag": "Name", 42 | "contents": "" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/NewType/NewType.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -7933038495455321262, 3 | "samples": [ 4 | -30, 5 | -30, 6 | -7, 7 | -5, 8 | 30, 9 | -14, 10 | 26, 11 | -25, 12 | -12, 13 | 25 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/OnOrOff/Off.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 3228432956609522149, 3 | "samples": [ 4 | "Off", 5 | "Off", 6 | "Off", 7 | "Off", 8 | "Off", 9 | "Off", 10 | "Off", 11 | "Off", 12 | "Off", 13 | "Off" 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/OnOrOff/On.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 2014006095097997093, 3 | "samples": [ 4 | "On", 5 | "On", 6 | "On", 7 | "On", 8 | "On", 9 | "On", 10 | "On", 11 | "On", 12 | "On", 13 | "On" 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/Result/Error.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 6838542643758342093, 3 | "samples": [ 4 | { 5 | "tag": "Error", 6 | "contents": -15 7 | }, 8 | { 9 | "tag": "Error", 10 | "contents": -5 11 | }, 12 | { 13 | "tag": "Error", 14 | "contents": -14 15 | }, 16 | { 17 | "tag": "Error", 18 | "contents": -11 19 | }, 20 | { 21 | "tag": "Error", 22 | "contents": -2 23 | }, 24 | { 25 | "tag": "Error", 26 | "contents": 15 27 | }, 28 | { 29 | "tag": "Error", 30 | "contents": 12 31 | }, 32 | { 33 | "tag": "Error", 34 | "contents": 21 35 | }, 36 | { 37 | "tag": "Error", 38 | "contents": -10 39 | }, 40 | { 41 | "tag": "Error", 42 | "contents": -18 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/Result/Success.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -2235679539073054709, 3 | "samples": [ 4 | { 5 | "tag": "Success", 6 | "contents": 17 7 | }, 8 | { 9 | "tag": "Success", 10 | "contents": 20 11 | }, 12 | { 13 | "tag": "Success", 14 | "contents": 3 15 | }, 16 | { 17 | "tag": "Success", 18 | "contents": -6 19 | }, 20 | { 21 | "tag": "Success", 22 | "contents": 15 23 | }, 24 | { 25 | "tag": "Success", 26 | "contents": -14 27 | }, 28 | { 29 | "tag": "Success", 30 | "contents": -26 31 | }, 32 | { 33 | "tag": "Success", 34 | "contents": -7 35 | }, 36 | { 37 | "tag": "Success", 38 | "contents": -27 39 | }, 40 | { 41 | "tag": "Success", 42 | "contents": 21 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/SingleSum/SingleSum.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 5368290304339618907, 3 | "samples": [ 4 | [], 5 | [], 6 | [], 7 | [], 8 | [], 9 | [], 10 | [], 11 | [], 12 | [], 13 | [] 14 | ] 15 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/SumVariant/HasMixed.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -7061981691673967949, 3 | "samples": [ 4 | { 5 | "tag": "HasMixed", 6 | "contents": [ 7 | -15, 8 | "@-:g􈺢(󽵳򰫐*EL^􆹹A5c󠷳\u0005o𢭍􁼹\u001f4:", 9 | -17.240536744287486 10 | ] 11 | }, 12 | { 13 | "tag": "HasMixed", 14 | "contents": [ 15 | 21, 16 | "x\u0018(眛󐸶󧕋h899\u000bjg\u0014Vs|X;", 17 | -42.43297806958867 18 | ] 19 | }, 20 | { 21 | "tag": "HasMixed", 22 | "contents": [ 23 | -28, 24 | "_bF(𝥕\u0018$", 25 | 31.935440317553308 26 | ] 27 | }, 28 | { 29 | "tag": "HasMixed", 30 | "contents": [ 31 | -20, 32 | "jp3󙣣", 33 | -10.280395262412918 34 | ] 35 | }, 36 | { 37 | "tag": "HasMixed", 38 | "contents": [ 39 | 2, 40 | "\"}󇄪𜳪1@^򤌃񂆵񇜱V𥐦[\u00016򓽹IT9\nX𲉢\u001e:\u0000򈆽:W", 41 | -143.11809448070778 42 | ] 43 | }, 44 | { 45 | "tag": "HasMixed", 46 | "contents": [ 47 | 17, 48 | "\u0007}g4\u0005YO񂠡", 49 | -101.61797903510524 50 | ] 51 | }, 52 | { 53 | "tag": "HasMixed", 54 | "contents": [ 55 | -29, 56 | "𭌇􂏎𼞢󅜋\u0008FT74ZR\u0004d4q(s~G3𐘧񀍽", 57 | -47.18039748830254 58 | ] 59 | }, 60 | { 61 | "tag": "HasMixed", 62 | "contents": [ 63 | -23, 64 | "\u001a[Lt", 65 | -21.637548733423014 66 | ] 67 | }, 68 | { 69 | "tag": "HasMixed", 70 | "contents": [ 71 | 17, 72 | "S.e", 73 | -318.47993406783405 74 | ] 75 | }, 76 | { 77 | "tag": "HasMixed", 78 | "contents": [ 79 | -19, 80 | "\u0017e$Wb\u001f\u000c\u000f񽠙󑩅\u0008xN/󬷐􅩟𥢗梆8\u0006<\u0015mV$\u001f$\u000en", 81 | -5.220294304336202 82 | ] 83 | } 84 | ] 85 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/SumVariant/HasMultipleInts.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 3274979294768835177, 3 | "samples": [ 4 | { 5 | "tag": "HasMultipleInts", 6 | "contents": [ 7 | -24, 8 | 5 9 | ] 10 | }, 11 | { 12 | "tag": "HasMultipleInts", 13 | "contents": [ 14 | 5, 15 | 12 16 | ] 17 | }, 18 | { 19 | "tag": "HasMultipleInts", 20 | "contents": [ 21 | 26, 22 | 21 23 | ] 24 | }, 25 | { 26 | "tag": "HasMultipleInts", 27 | "contents": [ 28 | -12, 29 | -25 30 | ] 31 | }, 32 | { 33 | "tag": "HasMultipleInts", 34 | "contents": [ 35 | 8, 36 | -21 37 | ] 38 | }, 39 | { 40 | "tag": "HasMultipleInts", 41 | "contents": [ 42 | 16, 43 | 8 44 | ] 45 | }, 46 | { 47 | "tag": "HasMultipleInts", 48 | "contents": [ 49 | -28, 50 | -24 51 | ] 52 | }, 53 | { 54 | "tag": "HasMultipleInts", 55 | "contents": [ 56 | 29, 57 | 19 58 | ] 59 | }, 60 | { 61 | "tag": "HasMultipleInts", 62 | "contents": [ 63 | 19, 64 | 8 65 | ] 66 | }, 67 | { 68 | "tag": "HasMultipleInts", 69 | "contents": [ 70 | 0, 71 | 24 72 | ] 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/SumVariant/HasNameOrIdNumber.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": -1135875169984086649, 3 | "samples": [ 4 | { 5 | "tag": "HasNameOrIdNumber", 6 | "contents": [ 7 | { 8 | "tag": "Name", 9 | "contents": "\n񄉴\u0008V񋤠Pr&A\u000c!N\u0010q\u0015%", 27 | "b3": 16 28 | }, 29 | { 30 | "tag": "B2", 31 | "b2": "ez\u001619񶛗=񿾊4Q,aND񿔁z򏁥𢤏7k󷚔E&4WE\u000f>b'", 32 | "b3": -19 33 | }, 34 | { 35 | "tag": "B2", 36 | "b2": "K\r򈅣d)\u0005\u001ca4]򌤦󚄔-q3zgI<𯽐\u001bU𢤁\\C鄸", 37 | "b3": 23 38 | }, 39 | { 40 | "tag": "B2", 41 | "b2": "", 42 | "b3": -29 43 | }, 44 | { 45 | "tag": "B2", 46 | "b2": "x\u0010\r%n'Q,", 47 | "b3": 7 48 | }, 49 | { 50 | "tag": "B2", 51 | "b2": "󝆉+򺍎=", 52 | "b3": -18 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /test/interface/golden/golden/sum/WithTuple/WithTuple.json: -------------------------------------------------------------------------------- 1 | { 2 | "seed": 3731922109012887365, 3 | "samples": [ 4 | [ 5 | 17, 6 | -23 7 | ], 8 | [ 9 | 5, 10 | -3 11 | ], 12 | [ 13 | 16, 14 | 0 15 | ], 16 | [ 17 | -11, 18 | 1 19 | ], 20 | [ 21 | -17, 22 | 26 23 | ], 24 | [ 25 | 17, 26 | 16 27 | ], 28 | [ 29 | -1, 30 | 12 31 | ], 32 | [ 33 | 1, 34 | -3 35 | ], 36 | [ 37 | 10, 38 | -23 39 | ], 40 | [ 41 | 8, 42 | -28 43 | ] 44 | ] 45 | } -------------------------------------------------------------------------------- /test/interface/golden/options/NameOrIdNumber.ml: -------------------------------------------------------------------------------- 1 | type nameOrIdNumber = 2 | | Name of string 3 | | IdNumber of int 4 | 5 | let encodeNameOrIdNumber x = 6 | match x with 7 | | Name y0 -> 8 | Aeson.Encode.object_ 9 | [ ( "tag", Aeson.Encode.string "name" ) 10 | ; ( "contents", Aeson.Encode.string y0 ) 11 | ] 12 | | IdNumber y0 -> 13 | Aeson.Encode.object_ 14 | [ ( "tag", Aeson.Encode.string "idnumber" ) 15 | ; ( "contents", Aeson.Encode.int y0 ) 16 | ] 17 | 18 | let decodeNameOrIdNumber json = 19 | match Aeson.Decode.(field "tag" string json) with 20 | | "name" -> 21 | (match Aeson.Decode.(field "contents" string json) with 22 | | v -> Js_result.Ok (Name v) 23 | | exception Aeson.Decode.DecodeError message -> Js_result.Error ("name: " ^ message) 24 | ) 25 | | "idnumber" -> 26 | (match Aeson.Decode.(field "contents" int json) with 27 | | v -> Js_result.Ok (IdNumber v) 28 | | exception Aeson.Decode.DecodeError message -> Js_result.Error ("idnumber: " ^ message) 29 | ) 30 | | err -> Js_result.Error ("Unknown tag value found '" ^ err ^ "'.") 31 | | exception Aeson.Decode.DecodeError message -> Js_result.Error message 32 | -------------------------------------------------------------------------------- /test/interface/golden/options/NameOrIdNumber.mli: -------------------------------------------------------------------------------- 1 | type nameOrIdNumber = 2 | | Name of string 3 | | IdNumber of int 4 | 5 | val encodeNameOrIdNumber : nameOrIdNumber -> Js_json.t 6 | 7 | val decodeNameOrIdNumber : Js_json.t -> (nameOrIdNumber, string) Js_result.t 8 | -------------------------------------------------------------------------------- /test/interface/golden/options/Person.ml: -------------------------------------------------------------------------------- 1 | type person = 2 | { id : int 3 | ; name : (string) option 4 | ; created : Js_date.t 5 | } 6 | 7 | let encodePerson x = 8 | Aeson.Encode.object_ 9 | [ ( "ID", Aeson.Encode.int x.id ) 10 | ; ( "NAME", Aeson.Encode.optional Aeson.Encode.string x.name ) 11 | ; ( "CREATED", Aeson.Encode.date x.created ) 12 | ] 13 | 14 | let decodePerson json = 15 | match Aeson.Decode. 16 | { id = field "ID" int json 17 | ; name = optional (field "NAME" string) json 18 | ; created = field "CREATED" date json 19 | } 20 | with 21 | | v -> Js_result.Ok v 22 | | exception Aeson.Decode.DecodeError message -> Js_result.Error ("decodePerson: " ^ message) 23 | -------------------------------------------------------------------------------- /test/interface/golden/options/Person.mli: -------------------------------------------------------------------------------- 1 | type person = 2 | { id : int 3 | ; name : (string) option 4 | ; created : Js_date.t 5 | } 6 | 7 | val encodePerson : person -> Js_json.t 8 | 9 | val decodePerson : Js_json.t -> (person, string) Js_result.t 10 | -------------------------------------------------------------------------------- /test/interface/golden/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ocaml-export-spec", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "bsb -make-world", 9 | "start": "bsb -make-world -w", 10 | "clean": "bsb -clean-world", 11 | "test": "npm run build && jest" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "MIT", 16 | "dependencies": { 17 | "bs-aeson": "^3.0.0" 18 | }, 19 | "devDependencies": { 20 | "bs-aeson-spec": "^2.0.0", 21 | "bs-platform": "^3.1.5", 22 | "@glennsl/bs-jest": "^0.4.2", 23 | "bs-node": "github:buckletypes/bs-node" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/interface/golden/person.json: -------------------------------------------------------------------------------- 1 | {"id" : 0, "name" : "James", "created" : "2017-11-07T04:05:09.518Z"} 2 | -------------------------------------------------------------------------------- /test/interface/golden/product/Box.mli: -------------------------------------------------------------------------------- 1 | type 'a0 innerBox = 2 | { ibA : 'a0 3 | ; ibS : string 4 | ; ibX : int 5 | } 6 | 7 | val encodeInnerBox : ('a0 -> Js_json.t) -> 'a0 innerBox -> Js_json.t 8 | 9 | val decodeInnerBox : (Js_json.t -> ('a0, string) Belt.Result.t) -> Js_json.t -> ('a0 innerBox, string) Belt.Result.t 10 | 11 | type outerBox = 12 | | OuterBox of (Person.person) innerBox 13 | 14 | val encodeOuterBox : outerBox -> Js_json.t 15 | 16 | val decodeOuterBox : Js_json.t -> (outerBox, string) Belt.Result.t 17 | 18 | type auditAction = 19 | | Create 20 | | Delete 21 | | Update 22 | 23 | val encodeAuditAction : auditAction -> Js_json.t 24 | 25 | val decodeAuditAction : Js_json.t -> (auditAction, string) Belt.Result.t 26 | 27 | type 'a0 auditModel = 28 | { auditModel : 'a0 29 | ; originalId : string 30 | ; auditAction : auditAction 31 | ; editedBy : string 32 | ; editedOn : string 33 | } 34 | 35 | val encodeAuditModel : ('a0 -> Js_json.t) -> 'a0 auditModel -> Js_json.t 36 | 37 | val decodeAuditModel : (Js_json.t -> ('a0, string) Belt.Result.t) -> Js_json.t -> ('a0 auditModel, string) Belt.Result.t 38 | 39 | type user = 40 | { userIdent : string 41 | ; userPassword : (string) option 42 | ; userTimezone : string 43 | } 44 | 45 | val encodeUser : user -> Js_json.t 46 | 47 | val decodeUser : Js_json.t -> (user, string) Belt.Result.t 48 | 49 | type userAudit = 50 | | UserAudit of (user) auditModel 51 | 52 | val encodeUserAudit : userAudit -> Js_json.t 53 | 54 | val decodeUserAudit : Js_json.t -> (userAudit, string) Belt.Result.t 55 | -------------------------------------------------------------------------------- /test/interface/golden/product/Card.ml: -------------------------------------------------------------------------------- 1 | type suit = 2 | | Clubs 3 | | Diamonds 4 | | Hearts 5 | | Spades 6 | 7 | let encodeSuit x = 8 | match x with 9 | | Clubs -> 10 | Aeson.Encode.string "Clubs" 11 | | Diamonds -> 12 | Aeson.Encode.string "Diamonds" 13 | | Hearts -> 14 | Aeson.Encode.string "Hearts" 15 | | Spades -> 16 | Aeson.Encode.string "Spades" 17 | 18 | let decodeSuit json = 19 | match Js_json.decodeString json with 20 | | Some "Clubs" -> Belt.Result.Ok Clubs 21 | | Some "Diamonds" -> Belt.Result.Ok Diamonds 22 | | Some "Hearts" -> Belt.Result.Ok Hearts 23 | | Some "Spades" -> Belt.Result.Ok Spades 24 | | Some err -> Belt.Result.Error ("decodeSuit: unknown enumeration '" ^ err ^ "'.") 25 | | None -> Belt.Result.Error "decodeSuit: expected a top-level JSON string." 26 | 27 | type card = 28 | { cardSuit : suit 29 | ; cardValue : int 30 | } 31 | 32 | let encodeCard x = 33 | Aeson.Encode.object_ 34 | [ ( "cardSuit", encodeSuit x.cardSuit ) 35 | ; ( "cardValue", Aeson.Encode.int x.cardValue ) 36 | ] 37 | 38 | let decodeCard json = 39 | match Aeson.Decode. 40 | { cardSuit = field "cardSuit" (fun a -> unwrapResult (decodeSuit a)) json 41 | ; cardValue = field "cardValue" int json 42 | } 43 | with 44 | | v -> Belt.Result.Ok v 45 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeCard: " ^ message) 46 | -------------------------------------------------------------------------------- /test/interface/golden/product/Card.mli: -------------------------------------------------------------------------------- 1 | type suit = 2 | | Clubs 3 | | Diamonds 4 | | Hearts 5 | | Spades 6 | 7 | val encodeSuit : suit -> Js_json.t 8 | 9 | val decodeSuit : Js_json.t -> (suit, string) Belt.Result.t 10 | 11 | type card = 12 | { cardSuit : suit 13 | ; cardValue : int 14 | } 15 | 16 | val encodeCard : card -> Js_json.t 17 | 18 | val decodeCard : Js_json.t -> (card, string) Belt.Result.t 19 | -------------------------------------------------------------------------------- /test/interface/golden/product/Company.ml: -------------------------------------------------------------------------------- 1 | type company = 2 | { address : string 3 | ; employees : (Person.person) list 4 | } 5 | 6 | let encodeCompany x = 7 | Aeson.Encode.object_ 8 | [ ( "address", Aeson.Encode.string x.address ) 9 | ; ( "employees", (Aeson.Encode.list Person.encodePerson) x.employees ) 10 | ] 11 | 12 | let decodeCompany json = 13 | match Aeson.Decode. 14 | { address = field "address" string json 15 | ; employees = field "employees" (list (fun a -> unwrapResult (Person.decodePerson a))) json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeCompany: " ^ message) 20 | -------------------------------------------------------------------------------- /test/interface/golden/product/Company.mli: -------------------------------------------------------------------------------- 1 | type company = 2 | { address : string 3 | ; employees : (Person.person) list 4 | } 5 | 6 | val encodeCompany : company -> Js_json.t 7 | 8 | val decodeCompany : Js_json.t -> (company, string) Belt.Result.t 9 | -------------------------------------------------------------------------------- /test/interface/golden/product/ComplexProduct.ml: -------------------------------------------------------------------------------- 1 | type simple = 2 | { sa : int 3 | ; sb : string 4 | } 5 | 6 | let encodeSimple x = 7 | Aeson.Encode.object_ 8 | [ ( "sa", Aeson.Encode.int x.sa ) 9 | ; ( "sb", Aeson.Encode.string x.sb ) 10 | ] 11 | 12 | let decodeSimple json = 13 | match Aeson.Decode. 14 | { sa = field "sa" int json 15 | ; sb = field "sb" string json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeSimple: " ^ message) 20 | 21 | 22 | type complexProduct = 23 | { cp0 : (Person.person, (int) list) Aeson.Compatibility.Either.t 24 | ; cp1 : ((int * (string, float) Aeson.Compatibility.Either.t)) list 25 | ; cp2 : ((int) list) list 26 | ; cp3 : ((int) list) option 27 | ; cp4 : (simple, int) Aeson.Compatibility.Either.t 28 | } 29 | 30 | let encodeComplexProduct x = 31 | Aeson.Encode.object_ 32 | [ ( "cp0", (Aeson.Encode.either Person.encodePerson (Aeson.Encode.list Aeson.Encode.int)) x.cp0 ) 33 | ; ( "cp1", (Aeson.Encode.list (Aeson.Encode.pair Aeson.Encode.int (Aeson.Encode.either Aeson.Encode.string Aeson.Encode.float))) x.cp1 ) 34 | ; ( "cp2", (Aeson.Encode.list (Aeson.Encode.list Aeson.Encode.int)) x.cp2 ) 35 | ; ( "cp3", (Aeson.Encode.optional (Aeson.Encode.list Aeson.Encode.int)) x.cp3 ) 36 | ; ( "cp4", (Aeson.Encode.either encodeSimple Aeson.Encode.int) x.cp4 ) 37 | ] 38 | 39 | let decodeComplexProduct json = 40 | match Aeson.Decode. 41 | { cp0 = field "cp0" (either (fun a -> unwrapResult (Person.decodePerson a)) (list int)) json 42 | ; cp1 = field "cp1" (list (pair int (either string Aeson.Decode.float))) json 43 | ; cp2 = field "cp2" (list (list int)) json 44 | ; cp3 = field "cp3" (optional (list int)) json 45 | ; cp4 = field "cp4" (either (fun a -> unwrapResult (decodeSimple a)) int) json 46 | } 47 | with 48 | | v -> Belt.Result.Ok v 49 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeComplexProduct: " ^ message) 50 | -------------------------------------------------------------------------------- /test/interface/golden/product/ComplexProduct.mli: -------------------------------------------------------------------------------- 1 | type simple = 2 | { sa : int 3 | ; sb : string 4 | } 5 | 6 | val encodeSimple : simple -> Js_json.t 7 | 8 | val decodeSimple : Js_json.t -> (simple, string) Belt.Result.t 9 | 10 | 11 | type complexProduct = 12 | { cp0 : (Person.person, (int) list) Aeson.Compatibility.Either.t 13 | ; cp1 : ((int * (string, float) Aeson.Compatibility.Either.t)) list 14 | ; cp2 : ((int) list) list 15 | ; cp3 : ((int) list) option 16 | ; cp4 : (simple, int) Aeson.Compatibility.Either.t 17 | } 18 | 19 | val encodeComplexProduct : complexProduct -> Js_json.t 20 | 21 | val decodeComplexProduct : Js_json.t -> (complexProduct, string) Belt.Result.t 22 | -------------------------------------------------------------------------------- /test/interface/golden/product/CustomOption.ml: -------------------------------------------------------------------------------- 1 | type company2 = 2 | { address2 : string 3 | ; boss : (Person.person) option 4 | } 5 | 6 | let encodeCompany2 x = 7 | Aeson.Encode.object_ 8 | [ ( "address2", Aeson.Encode.string x.address2 ) 9 | ; ( "boss", Aeson.Encode.optional Person.encodePerson x.boss ) 10 | ] 11 | 12 | let decodeCompany2 json = 13 | match Aeson.Decode. 14 | { address2 = field "address2" string json 15 | ; boss = optional (field "boss" (fun a -> unwrapResult (Person.decodePerson a))) json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeCompany2: " ^ message) 20 | -------------------------------------------------------------------------------- /test/interface/golden/product/CustomOption.mli: -------------------------------------------------------------------------------- 1 | type company2 = 2 | { address2 : string 3 | ; boss : (Person.person) option 4 | } 5 | 6 | val encodeCompany2 : company2 -> Js_json.t 7 | 8 | val decodeCompany2 : Js_json.t -> (company2, string) Belt.Result.t 9 | -------------------------------------------------------------------------------- /test/interface/golden/product/Key.ml: -------------------------------------------------------------------------------- 1 | type sqlKey = 2 | | SqlKey of int 3 | 4 | let encodeSqlKey x = 5 | match x with 6 | | SqlKey y0 -> 7 | Aeson.Encode.int y0 8 | 9 | let decodeSqlKey json = 10 | match Aeson.Decode.int json with 11 | | v -> Belt.Result.Ok (SqlKey v) 12 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeSqlKey: " ^ msg) 13 | 14 | type userId = 15 | | UserId of sqlKey 16 | 17 | let encodeUserId x = 18 | match x with 19 | | UserId y0 -> 20 | encodeSqlKey y0 21 | 22 | let decodeUserId json = 23 | match (Aeson.Decode.unwrapResult (decodeSqlKey json)) with 24 | | v -> Belt.Result.Ok (UserId v) 25 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeUserId: " ^ msg) 26 | -------------------------------------------------------------------------------- /test/interface/golden/product/Key.mli: -------------------------------------------------------------------------------- 1 | type sqlKey = 2 | | SqlKey of int 3 | 4 | val encodeSqlKey : sqlKey -> Js_json.t 5 | 6 | val decodeSqlKey : Js_json.t -> (sqlKey, string) Belt.Result.t 7 | 8 | type userId = 9 | | UserId of sqlKey 10 | 11 | val encodeUserId : userId -> Js_json.t 12 | 13 | val decodeUserId : Js_json.t -> (userId, string) Belt.Result.t 14 | -------------------------------------------------------------------------------- /test/interface/golden/product/OneTypeParameter.ml: -------------------------------------------------------------------------------- 1 | type 'a0 oneTypeParameter = 2 | { otpId : int 3 | ; otpFirst : 'a0 4 | } 5 | 6 | let encodeOneTypeParameter encodeA0 x = 7 | Aeson.Encode.object_ 8 | [ ( "otpId", Aeson.Encode.int x.otpId ) 9 | ; ( "otpFirst", encodeA0 x.otpFirst ) 10 | ] 11 | 12 | let decodeOneTypeParameter decodeA0 json = 13 | match Aeson.Decode. 14 | { otpId = field "otpId" int json 15 | ; otpFirst = field "otpFirst" (fun a -> unwrapResult (decodeA0 a)) json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeOneTypeParameter: " ^ message) 20 | -------------------------------------------------------------------------------- /test/interface/golden/product/OneTypeParameter.mli: -------------------------------------------------------------------------------- 1 | type 'a0 oneTypeParameter = 2 | { otpId : int 3 | ; otpFirst : 'a0 4 | } 5 | 6 | val encodeOneTypeParameter : ('a0 -> Js_json.t) -> 'a0 oneTypeParameter -> Js_json.t 7 | 8 | val decodeOneTypeParameter : (Js_json.t -> ('a0, string) Belt.Result.t) -> Js_json.t -> ('a0 oneTypeParameter, string) Belt.Result.t 9 | -------------------------------------------------------------------------------- /test/interface/golden/product/Person.ml: -------------------------------------------------------------------------------- 1 | type person = 2 | { id : int 3 | ; name : (string) option 4 | ; created : Js_date.t 5 | } 6 | 7 | let encodePerson x = 8 | Aeson.Encode.object_ 9 | [ ( "id", Aeson.Encode.int x.id ) 10 | ; ( "name", (Aeson.Encode.optional Aeson.Encode.string) x.name ) 11 | ; ( "created", Aeson.Encode.date x.created ) 12 | ] 13 | 14 | let decodePerson json = 15 | match Aeson.Decode. 16 | { id = field "id" int json 17 | ; name = field "name" (optional string) json 18 | ; created = field "created" date json 19 | } 20 | with 21 | | v -> Belt.Result.Ok v 22 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodePerson: " ^ message) 23 | -------------------------------------------------------------------------------- /test/interface/golden/product/Person.mli: -------------------------------------------------------------------------------- 1 | type person = 2 | { id : int 3 | ; name : (string) option 4 | ; created : Js_date.t 5 | } 6 | 7 | val encodePerson : person -> Js_json.t 8 | 9 | val decodePerson : Js_json.t -> (person, string) Belt.Result.t 10 | -------------------------------------------------------------------------------- /test/interface/golden/product/SimpleChoice.ml: -------------------------------------------------------------------------------- 1 | type simpleChoice = 2 | { choice : (string, int) Aeson.Compatibility.Either.t 3 | } 4 | 5 | let encodeSimpleChoice x = 6 | Aeson.Encode.object_ 7 | [ ( "choice", Aeson.Encode.either Aeson.Encode.string Aeson.Encode.int x.choice ) 8 | ] 9 | 10 | let decodeSimpleChoice json = 11 | match Aeson.Decode. 12 | { choice = field "choice" (either string int) json 13 | } 14 | with 15 | | v -> Belt.Result.Ok v 16 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeSimpleChoice: " ^ message) 17 | -------------------------------------------------------------------------------- /test/interface/golden/product/SimpleChoice.mli: -------------------------------------------------------------------------------- 1 | type simpleChoice = 2 | { choice : (string, int) Aeson.Compatibility.Either.t 3 | } 4 | 5 | val encodeSimpleChoice : simpleChoice -> Js_json.t 6 | 7 | val decodeSimpleChoice : Js_json.t -> (simpleChoice, string) Belt.Result.t 8 | -------------------------------------------------------------------------------- /test/interface/golden/product/SubTypeParameter.ml: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1, 'a2) subTypeParameter = 2 | { listA : ('a0) list 3 | ; maybeB : ('a1) option 4 | ; tupleC : ('a2 * 'a1) 5 | } 6 | 7 | let encodeSubTypeParameter encodeA0 encodeA1 encodeA2 x = 8 | Aeson.Encode.object_ 9 | [ ( "listA", (Aeson.Encode.list encodeA0) x.listA ) 10 | ; ( "maybeB", (Aeson.Encode.optional encodeA1) x.maybeB ) 11 | ; ( "tupleC", (Aeson.Encode.pair encodeA2 encodeA1) x.tupleC ) 12 | ] 13 | 14 | let decodeSubTypeParameter decodeA0 decodeA1 decodeA2 json = 15 | match Aeson.Decode. 16 | { listA = field "listA" (list (fun a -> unwrapResult (decodeA0 a))) json 17 | ; maybeB = field "maybeB" (optional (fun a -> unwrapResult (decodeA1 a))) json 18 | ; tupleC = field "tupleC" (pair (fun a -> unwrapResult (decodeA2 a)) (fun a -> unwrapResult (decodeA1 a))) json 19 | } 20 | with 21 | | v -> Belt.Result.Ok v 22 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeSubTypeParameter: " ^ message) 23 | -------------------------------------------------------------------------------- /test/interface/golden/product/SubTypeParameter.mli: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1, 'a2) subTypeParameter = 2 | { listA : ('a0) list 3 | ; maybeB : ('a1) option 4 | ; tupleC : ('a2 * 'a1) 5 | } 6 | 7 | val encodeSubTypeParameter : ('a0 -> Js_json.t) -> ('a1 -> Js_json.t) -> ('a2 -> Js_json.t) -> ('a0, 'a1, 'a2) subTypeParameter -> Js_json.t 8 | 9 | val decodeSubTypeParameter : (Js_json.t -> ('a0, string) Belt.Result.t) -> (Js_json.t -> ('a1, string) Belt.Result.t) -> (Js_json.t -> ('a2, string) Belt.Result.t) -> Js_json.t -> (('a0, 'a1, 'a2) subTypeParameter, string) Belt.Result.t 10 | -------------------------------------------------------------------------------- /test/interface/golden/product/ThreeTypeParameters.ml: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1, 'a2) three = 2 | { threeId : int 3 | ; threeFirst : 'a0 4 | ; threeSecond : 'a1 5 | ; threeThird : 'a2 6 | ; threeString : string 7 | } 8 | 9 | let encodeThree encodeA0 encodeA1 encodeA2 x = 10 | Aeson.Encode.object_ 11 | [ ( "threeId", Aeson.Encode.int x.threeId ) 12 | ; ( "threeFirst", encodeA0 x.threeFirst ) 13 | ; ( "threeSecond", encodeA1 x.threeSecond ) 14 | ; ( "threeThird", encodeA2 x.threeThird ) 15 | ; ( "threeString", Aeson.Encode.string x.threeString ) 16 | ] 17 | 18 | let decodeThree decodeA0 decodeA1 decodeA2 json = 19 | match Aeson.Decode. 20 | { threeId = field "threeId" int json 21 | ; threeFirst = field "threeFirst" (fun a -> unwrapResult (decodeA0 a)) json 22 | ; threeSecond = field "threeSecond" (fun a -> unwrapResult (decodeA1 a)) json 23 | ; threeThird = field "threeThird" (fun a -> unwrapResult (decodeA2 a)) json 24 | ; threeString = field "threeString" string json 25 | } 26 | with 27 | | v -> Belt.Result.Ok v 28 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeThree: " ^ message) 29 | -------------------------------------------------------------------------------- /test/interface/golden/product/ThreeTypeParameters.mli: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1, 'a2) three = 2 | { threeId : int 3 | ; threeFirst : 'a0 4 | ; threeSecond : 'a1 5 | ; threeThird : 'a2 6 | ; threeString : string 7 | } 8 | 9 | val encodeThree : ('a0 -> Js_json.t) -> ('a1 -> Js_json.t) -> ('a2 -> Js_json.t) -> ('a0, 'a1, 'a2) three -> Js_json.t 10 | 11 | val decodeThree : (Js_json.t -> ('a0, string) Belt.Result.t) -> (Js_json.t -> ('a1, string) Belt.Result.t) -> (Js_json.t -> ('a2, string) Belt.Result.t) -> Js_json.t -> (('a0, 'a1, 'a2) three, string) Belt.Result.t 12 | -------------------------------------------------------------------------------- /test/interface/golden/product/TwoTypeParameters.ml: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1) twoTypeParameters = 2 | { ttpId : int 3 | ; ttpFirst : 'a0 4 | ; ttpSecond : 'a1 5 | ; ttpThird : ('a0 * 'a1) 6 | } 7 | 8 | let encodeTwoTypeParameters encodeA0 encodeA1 x = 9 | Aeson.Encode.object_ 10 | [ ( "ttpId", Aeson.Encode.int x.ttpId ) 11 | ; ( "ttpFirst", encodeA0 x.ttpFirst ) 12 | ; ( "ttpSecond", encodeA1 x.ttpSecond ) 13 | ; ( "ttpThird", (Aeson.Encode.pair encodeA0 encodeA1) x.ttpThird ) 14 | ] 15 | 16 | let decodeTwoTypeParameters decodeA0 decodeA1 json = 17 | match Aeson.Decode. 18 | { ttpId = field "ttpId" int json 19 | ; ttpFirst = field "ttpFirst" (fun a -> unwrapResult (decodeA0 a)) json 20 | ; ttpSecond = field "ttpSecond" (fun a -> unwrapResult (decodeA1 a)) json 21 | ; ttpThird = field "ttpThird" (pair (fun a -> unwrapResult (decodeA0 a)) (fun a -> unwrapResult (decodeA1 a))) json 22 | } 23 | with 24 | | v -> Belt.Result.Ok v 25 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeTwoTypeParameters: " ^ message) 26 | -------------------------------------------------------------------------------- /test/interface/golden/product/TwoTypeParameters.mli: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1) twoTypeParameters = 2 | { ttpId : int 3 | ; ttpFirst : 'a0 4 | ; ttpSecond : 'a1 5 | ; ttpThird : ('a0 * 'a1) 6 | } 7 | 8 | val encodeTwoTypeParameters : ('a0 -> Js_json.t) -> ('a1 -> Js_json.t) -> ('a0, 'a1) twoTypeParameters -> Js_json.t 9 | 10 | val decodeTwoTypeParameters : (Js_json.t -> ('a0, string) Belt.Result.t) -> (Js_json.t -> ('a1, string) Belt.Result.t) -> Js_json.t -> (('a0, 'a1) twoTypeParameters, string) Belt.Result.t 11 | -------------------------------------------------------------------------------- /test/interface/golden/product/UnnamedProduct.ml: -------------------------------------------------------------------------------- 1 | type unnamedProduct = 2 | | UnnamedProduct of string * int 3 | 4 | let encodeUnnamedProduct x = 5 | match x with 6 | | UnnamedProduct (y0,y1) -> 7 | Aeson.Encode.array [| Aeson.Encode.string y0 ; Aeson.Encode.int y1 |] 8 | 9 | let decodeUnnamedProduct json = 10 | match Js.Json.decodeArray json with 11 | | Some v -> 12 | (match Aeson.Decode.(string) v.(0) with 13 | | v0 -> 14 | (match Aeson.Decode.(int) v.(1) with 15 | | v1 -> 16 | Belt.Result.Ok (UnnamedProduct (v0, v1)) 17 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("UnnamedProduct: " ^ message) 18 | ) 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("UnnamedProduct: " ^ message) 20 | ) 21 | | None -> Belt.Result.Error ("UnnamedProduct expected an array.") 22 | -------------------------------------------------------------------------------- /test/interface/golden/product/UnnamedProduct.mli: -------------------------------------------------------------------------------- 1 | type unnamedProduct = 2 | | UnnamedProduct of string * int 3 | 4 | val encodeUnnamedProduct : unnamedProduct -> Js_json.t 5 | 6 | val decodeUnnamedProduct : Js_json.t -> (unnamedProduct, string) Belt.Result.t 7 | -------------------------------------------------------------------------------- /test/interface/golden/sum/NameOrIdNumber.ml: -------------------------------------------------------------------------------- 1 | type nameOrIdNumber = 2 | | Name of string 3 | | IdNumber of int 4 | 5 | let encodeNameOrIdNumber x = 6 | match x with 7 | | Name y0 -> 8 | Aeson.Encode.object_ 9 | [ ( "tag", Aeson.Encode.string "Name" ) 10 | ; ( "contents", Aeson.Encode.string y0 ) 11 | ] 12 | | IdNumber y0 -> 13 | Aeson.Encode.object_ 14 | [ ( "tag", Aeson.Encode.string "IdNumber" ) 15 | ; ( "contents", Aeson.Encode.int y0 ) 16 | ] 17 | 18 | let decodeNameOrIdNumber json = 19 | match Aeson.Decode.(field "tag" string json) with 20 | | "Name" -> 21 | (match Aeson.Decode.(field "contents" string json) with 22 | | v -> Belt.Result.Ok (Name v) 23 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("Name: " ^ message) 24 | ) 25 | | "IdNumber" -> 26 | (match Aeson.Decode.(field "contents" int json) with 27 | | v -> Belt.Result.Ok (IdNumber v) 28 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("IdNumber: " ^ message) 29 | ) 30 | | err -> Belt.Result.Error ("Unknown tag value found '" ^ err ^ "'.") 31 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error message 32 | -------------------------------------------------------------------------------- /test/interface/golden/sum/NameOrIdNumber.mli: -------------------------------------------------------------------------------- 1 | type nameOrIdNumber = 2 | | Name of string 3 | | IdNumber of int 4 | 5 | val encodeNameOrIdNumber : nameOrIdNumber -> Js_json.t 6 | 7 | val decodeNameOrIdNumber : Js_json.t -> (nameOrIdNumber, string) Belt.Result.t 8 | -------------------------------------------------------------------------------- /test/interface/golden/sum/NewType.ml: -------------------------------------------------------------------------------- 1 | type newType = 2 | | NewType of int 3 | 4 | let encodeNewType x = 5 | match x with 6 | | NewType y0 -> 7 | Aeson.Encode.int y0 8 | 9 | let decodeNewType json = 10 | match Aeson.Decode.int json with 11 | | v -> Belt.Result.Ok (NewType v) 12 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeNewType: " ^ msg) 13 | -------------------------------------------------------------------------------- /test/interface/golden/sum/NewType.mli: -------------------------------------------------------------------------------- 1 | type newType = 2 | | NewType of int 3 | 4 | val encodeNewType : newType -> Js_json.t 5 | 6 | val decodeNewType : Js_json.t -> (newType, string) Belt.Result.t 7 | -------------------------------------------------------------------------------- /test/interface/golden/sum/OnOrOff.ml: -------------------------------------------------------------------------------- 1 | type onOrOff = 2 | | On 3 | | Off 4 | 5 | let encodeOnOrOff x = 6 | match x with 7 | | On -> 8 | Aeson.Encode.string "On" 9 | | Off -> 10 | Aeson.Encode.string "Off" 11 | 12 | let decodeOnOrOff json = 13 | match Js_json.decodeString json with 14 | | Some "On" -> Belt.Result.Ok On 15 | | Some "Off" -> Belt.Result.Ok Off 16 | | Some err -> Belt.Result.Error ("decodeOnOrOff: unknown enumeration '" ^ err ^ "'.") 17 | | None -> Belt.Result.Error "decodeOnOrOff: expected a top-level JSON string." 18 | -------------------------------------------------------------------------------- /test/interface/golden/sum/OnOrOff.mli: -------------------------------------------------------------------------------- 1 | type onOrOff = 2 | | On 3 | | Off 4 | 5 | val encodeOnOrOff : onOrOff -> Js_json.t 6 | 7 | val decodeOnOrOff : Js_json.t -> (onOrOff, string) Belt.Result.t 8 | -------------------------------------------------------------------------------- /test/interface/golden/sum/Result.ml: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1) result = 2 | | Success of 'a0 3 | | Error of 'a1 4 | 5 | let encodeResult encodeA0 encodeA1 x = 6 | match x with 7 | | Success y0 -> 8 | Aeson.Encode.object_ 9 | [ ( "tag", Aeson.Encode.string "Success" ) 10 | ; ( "contents", encodeA0 y0 ) 11 | ] 12 | | Error y0 -> 13 | Aeson.Encode.object_ 14 | [ ( "tag", Aeson.Encode.string "Error" ) 15 | ; ( "contents", encodeA1 y0 ) 16 | ] 17 | 18 | let decodeResult decodeA0 decodeA1 json = 19 | match Aeson.Decode.(field "tag" string json) with 20 | | "Success" -> 21 | (match Aeson.Decode.(field "contents" (fun a -> unwrapResult (decodeA0 a)) json) with 22 | | v -> Belt.Result.Ok (Success v) 23 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("Success: " ^ message) 24 | ) 25 | | "Error" -> 26 | (match Aeson.Decode.(field "contents" (fun a -> unwrapResult (decodeA1 a)) json) with 27 | | v -> Belt.Result.Ok (Error v) 28 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("Error: " ^ message) 29 | ) 30 | | err -> Belt.Result.Error ("Unknown tag value found '" ^ err ^ "'.") 31 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error message 32 | -------------------------------------------------------------------------------- /test/interface/golden/sum/Result.mli: -------------------------------------------------------------------------------- 1 | type ('a0, 'a1) result = 2 | | Success of 'a0 3 | | Error of 'a1 4 | 5 | val encodeResult : ('a0 -> Js_json.t) -> ('a1 -> Js_json.t) -> ('a0, 'a1) result -> Js_json.t 6 | 7 | val decodeResult : (Js_json.t -> ('a0, string) Belt.Result.t) -> (Js_json.t -> ('a1, string) Belt.Result.t) -> Js_json.t -> (('a0, 'a1) result, string) Belt.Result.t 8 | -------------------------------------------------------------------------------- /test/interface/golden/sum/SingleSum.ml: -------------------------------------------------------------------------------- 1 | type singleSum = 2 | | SingleSum 3 | 4 | let encodeSingleSum x = 5 | match x with 6 | | SingleSum -> 7 | Aeson.Encode.list Aeson.Encode.int [] 8 | 9 | let decodeSingleSum json = 10 | match (Aeson.Decode.(list int json)) with 11 | | _ -> Belt.Result.Ok SingleSum 12 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeSingleSum: expected a top-level empty JSON array. Got: " ^ message) 13 | -------------------------------------------------------------------------------- /test/interface/golden/sum/SingleSum.mli: -------------------------------------------------------------------------------- 1 | type singleSum = 2 | | SingleSum 3 | 4 | val encodeSingleSum : singleSum -> Js_json.t 5 | 6 | val decodeSingleSum : Js_json.t -> (singleSum, string) Belt.Result.t 7 | -------------------------------------------------------------------------------- /test/interface/golden/sum/SumVariant.mli: -------------------------------------------------------------------------------- 1 | type sumVariant = 2 | | HasNothing 3 | | HasSingleInt of int 4 | | HasSingleTuple of (int * int) 5 | | HasMultipleInts of int * int 6 | | HasMultipleTuples of (int * int) * (int * int) 7 | | HasMixed of int * string * float 8 | | HasNameOrIdNumber of NameOrIdNumber.nameOrIdNumber * int 9 | 10 | val encodeSumVariant : sumVariant -> Js_json.t 11 | 12 | val decodeSumVariant : Js_json.t -> (sumVariant, string) Belt.Result.t 13 | -------------------------------------------------------------------------------- /test/interface/golden/sum/SumWithRecord.ml: -------------------------------------------------------------------------------- 1 | type sumWithRecordA1 = 2 | { a1 : int 3 | } 4 | 5 | type sumWithRecordB2 = 6 | { b2 : string 7 | ; b3 : int 8 | } 9 | 10 | type sumWithRecord = 11 | | A1 of sumWithRecordA1 12 | | B2 of sumWithRecordB2 13 | 14 | let encodeSumWithRecordA1 x = 15 | Aeson.Encode.object_ 16 | [ ( "a1", Aeson.Encode.int x.a1 ) 17 | ] 18 | 19 | let encodeSumWithRecordB2 x = 20 | Aeson.Encode.object_ 21 | [ ( "b2", Aeson.Encode.string x.b2 ) 22 | ; ( "b3", Aeson.Encode.int x.b3 ) 23 | ] 24 | 25 | let encodeSumWithRecord x = 26 | match x with 27 | | A1 y0 -> 28 | (match (Js.Json.decodeObject (encodeSumWithRecordA1 y0)) with 29 | | Some dict -> 30 | Js.Dict.set dict "tag" (Js.Json.string "A1"); 31 | Js.Json.object_ dict 32 | | None -> 33 | Aeson.Encode.object_ [] 34 | ) 35 | | B2 y0 -> 36 | (match (Js.Json.decodeObject (encodeSumWithRecordB2 y0)) with 37 | | Some dict -> 38 | Js.Dict.set dict "tag" (Js.Json.string "B2"); 39 | Js.Json.object_ dict 40 | | None -> 41 | Aeson.Encode.object_ [] 42 | ) 43 | 44 | let decodeSumWithRecordA1 json = 45 | match Aeson.Decode. 46 | { a1 = field "a1" int json 47 | } 48 | with 49 | | v -> Belt.Result.Ok v 50 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeSumWithRecordA1: " ^ message) 51 | 52 | let decodeSumWithRecordB2 json = 53 | match Aeson.Decode. 54 | { b2 = field "b2" string json 55 | ; b3 = field "b3" int json 56 | } 57 | with 58 | | v -> Belt.Result.Ok v 59 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeSumWithRecordB2: " ^ message) 60 | 61 | let decodeSumWithRecord json = 62 | match Aeson.Decode.(field "tag" string json) with 63 | | "A1" -> 64 | (match decodeSumWithRecordA1 json with 65 | | Belt.Result.Ok v -> Belt.Result.Ok (A1 v) 66 | | Belt.Result.Error message -> Belt.Result.Error ("decodeSumWithRecord: " ^ message) 67 | ) 68 | | "B2" -> 69 | (match decodeSumWithRecordB2 json with 70 | | Belt.Result.Ok v -> Belt.Result.Ok (B2 v) 71 | | Belt.Result.Error message -> Belt.Result.Error ("decodeSumWithRecord: " ^ message) 72 | ) 73 | | err -> Belt.Result.Error ("Unknown tag value found '" ^ err ^ "'.") 74 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error message 75 | -------------------------------------------------------------------------------- /test/interface/golden/sum/SumWithRecord.mli: -------------------------------------------------------------------------------- 1 | type sumWithRecordA1 = 2 | { a1 : int 3 | } 4 | 5 | type sumWithRecordB2 = 6 | { b2 : string 7 | ; b3 : int 8 | } 9 | 10 | type sumWithRecord = 11 | | A1 of sumWithRecordA1 12 | | B2 of sumWithRecordB2 13 | 14 | val encodeSumWithRecordA1 : sumWithRecordA1 -> Js_json.t 15 | 16 | val encodeSumWithRecordB2 : sumWithRecordB2 -> Js_json.t 17 | 18 | val encodeSumWithRecord : sumWithRecord -> Js_json.t 19 | 20 | val decodeSumWithRecordA1 : Js_json.t -> (sumWithRecordA1, string) Belt.Result.t 21 | 22 | val decodeSumWithRecordB2 : Js_json.t -> (sumWithRecordB2, string) Belt.Result.t 23 | 24 | val decodeSumWithRecord : Js_json.t -> (sumWithRecord, string) Belt.Result.t 25 | -------------------------------------------------------------------------------- /test/interface/golden/sum/WithTuple.ml: -------------------------------------------------------------------------------- 1 | type withTuple = 2 | | WithTuple of (int * int) 3 | 4 | let encodeWithTuple x = 5 | match x with 6 | | WithTuple y0 -> 7 | (Aeson.Encode.pair Aeson.Encode.int Aeson.Encode.int) y0 8 | 9 | let decodeWithTuple json = 10 | match Aeson.Decode.(pair int int) json with 11 | | v -> Belt.Result.Ok (WithTuple v) 12 | | exception Aeson.Decode.DecodeError msg -> Belt.Result.Error ("decodeWithTuple: " ^ msg) 13 | -------------------------------------------------------------------------------- /test/interface/golden/sum/WithTuple.mli: -------------------------------------------------------------------------------- 1 | type withTuple = 2 | | WithTuple of (int * int) 3 | 4 | val encodeWithTuple : withTuple -> Js_json.t 5 | 6 | val decodeWithTuple : Js_json.t -> (withTuple, string) Belt.Result.t 7 | -------------------------------------------------------------------------------- /test/interface/golden/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: { 5 | onping: './lib/js/src/App.js' 6 | }, 7 | output: { 8 | path: path.join(__dirname, "bundledOutputs"), 9 | filename: '[name].js', 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /test/ocaml/Business.ml: -------------------------------------------------------------------------------- 1 | type business = 2 | { taxId : string 3 | ; owner : person 4 | ; employees : (person) list 5 | ; companyVehicle : (automobile) option 6 | } 7 | 8 | let encodeBusiness x = 9 | Aeson.Encode.object_ 10 | [ ( "taxId", Aeson.Encode.string x.taxId ) 11 | ; ( "owner", encodePerson x.owner ) 12 | ; ( "employees", (Aeson.Encode.list encodePerson) x.employees ) 13 | ; ( "companyVehicle", Aeson.Encode.optional encodeAutomobile x.companyVehicle ) 14 | ] 15 | 16 | let decodeBusiness json = 17 | match Aeson.Decode. 18 | { taxId = field "taxId" string json 19 | ; owner = field "owner" (fun a -> unwrapResult (decodePerson a)) json 20 | ; employees = field "employees" (list (fun a -> unwrapResult (decodePerson a))) json 21 | ; companyVehicle = optional (field "companyVehicle" (fun a -> unwrapResult (decodeAutomobile a))) json 22 | } 23 | with 24 | | v -> Belt.Result.Ok v 25 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeBusiness: " ^ message) 26 | -------------------------------------------------------------------------------- /test/ocaml/Business.mli: -------------------------------------------------------------------------------- 1 | type business = 2 | { taxId : string 3 | ; owner : person 4 | ; employees : (person) list 5 | ; companyVehicle : (automobile) option 6 | } 7 | 8 | val encodeBusiness : business -> Js_json.t 9 | 10 | val decodeBusiness : Js_json.t -> (business, string) Belt.Result.t 11 | -------------------------------------------------------------------------------- /test/ocaml/NonGenericType.ml: -------------------------------------------------------------------------------- 1 | type nonGenericType = 2 | { ngA : string 3 | ; ngB : int 4 | } 5 | 6 | let encodeNonGenericType x = 7 | Aeson.Encode.object_ 8 | [ ( "ngA", Aeson.Encode.string x.ngA) 9 | ; ( "ngB", Aeson.Encode.int x.ngB) 10 | ] 11 | 12 | let decodeNonGenericType json = 13 | match Aeson.Decode. 14 | { ngA = field "ngA" string json 15 | ; ngB = field "ngB" int json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeNonGenericType: " ^ message) 20 | -------------------------------------------------------------------------------- /test/ocaml/NonGenericType.mli: -------------------------------------------------------------------------------- 1 | type nonGenericType = 2 | { ngA : string 3 | ; ngB : int 4 | } 5 | 6 | val encodeNonGenericType : nonGenericType -> Js_json.t 7 | 8 | val decodeNonGenericType : Js_json.t -> (nonGenericType, string) Belt.Result.t 9 | -------------------------------------------------------------------------------- /test/ocaml/Person.ml: -------------------------------------------------------------------------------- 1 | type person = 2 | { id : int 3 | ; name : (string) option 4 | } 5 | 6 | let encodePerson x = 7 | Aeson.Encode.object_ 8 | [ ( "id", Aeson.Encode.int x.id ) 9 | ; ( "name", Aeson.Encode.optional Aeson.Encode.string x.name ) 10 | ] 11 | 12 | let decodePerson json = 13 | match Aeson.Decode. 14 | { id = field "id" int json 15 | ; name = optional (field "name" string) json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodePerson: " ^ message) 20 | -------------------------------------------------------------------------------- /test/ocaml/Person.mli: -------------------------------------------------------------------------------- 1 | type person = 2 | { id : int 3 | ; name : (string) option 4 | } 5 | 6 | val encodePerson : person -> Js_json.t 7 | 8 | val decodePerson : Js_json.t -> (person, string) Belt.Result.t 9 | -------------------------------------------------------------------------------- /test/ocaml/Simple.ml: -------------------------------------------------------------------------------- 1 | type simple = 2 | { sa : int 3 | ; sb : string 4 | } 5 | 6 | let encodeSimple x = 7 | Aeson.Encode.object_ 8 | [ ( "sa", Aeson.Encode.int x.sa ) 9 | ; ( "sb", Aeson.Encode.string x.sb ) 10 | ] 11 | 12 | let decodeSimple json = 13 | match Aeson.Decode. 14 | { sa = field "sa" int json 15 | ; sb = field "sb" string json 16 | } 17 | with 18 | | v -> Belt.Result.Ok v 19 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeSimple: " ^ message) 20 | -------------------------------------------------------------------------------- /test/ocaml/Simple.mli: -------------------------------------------------------------------------------- 1 | type simple = 2 | { sa : int 3 | ; sb : string 4 | } 5 | 6 | val encodeSimple : simple -> Js_json.t 7 | 8 | val decodeSimple : Js_json.t -> (simple, string) Belt.Result.t 9 | -------------------------------------------------------------------------------- /test/ocaml/Wrapper.ml: -------------------------------------------------------------------------------- 1 | type ('a, 'b) wrapper = 2 | { wrapperA : 'a 3 | ; wrapperB : 'b 4 | ; wrapperC : string 5 | } 6 | 7 | let encodeWrapper a b x = 8 | Aeson.Encode.object_ 9 | [ ( "wrapperA", a x.wrapperA ) 10 | ; ( "wrapperB", b x.wrapperB ) 11 | ; ( "wrapperC", Aeson.Encode.string x.wrapperC ) 12 | ] 13 | 14 | let decodeWrapper a b json = 15 | match Aeson.Decode. 16 | { wrapperA = field "wrapperA" (fun x -> unwrapResult (a x)) json 17 | ; wrapperB = field "wrapperB" (fun x -> unwrapResult (b x)) json 18 | ; wrapperC = field "wrapperC" string json 19 | } 20 | with 21 | | v -> Belt.Result.Ok v 22 | | exception Aeson.Decode.DecodeError message -> Belt.Result.Error ("decodeWrapper: " ^ message) 23 | -------------------------------------------------------------------------------- /test/ocaml/Wrapper.mli: -------------------------------------------------------------------------------- 1 | type ('a, 'b) wrapper = 2 | { wrapperA : 'a 3 | ; wrapperB : 'b 4 | ; wrapperC : string 5 | } 6 | 7 | val encodeWrapper : ('a -> Js_json.t) -> ('b -> Js_json.t) -> ('a, 'b) wrapper -> Js_json.t 8 | 9 | val decodeWrapper : (Js_json.t -> ('a, string) Belt.Result.t) -> (Js_json.t -> ('b, string) Belt.Result.t) -> Js_json.t -> (('a, 'b) wrapper, string) Belt.Result.t 10 | --------------------------------------------------------------------------------