git.iko.soy/rowtype-yoga/purescript-protobuf
on master help sign in
Plugin is running
d8aff82James Brock
README.md+11−5
@@ -9,8 +9,8 @@ supported.
9## Runtime library
9## Runtime library
10
10
11This library operates on
11This library operates on
12−[`ArrayBuffer`](https://pursuit.purescript.org/packages/purescript-arraybuffer-types/docs/Data.ArrayBuffer.Types#t:ArrayBuffer), so works in both
12+[`ArrayBuffer`](https://pursuit.purescript.org/packages/purescript-arraybuffer-types/docs/Data.ArrayBuffer.Types#t:ArrayBuffer), so
13−[Node](https://pursuit.purescript.org/packages/purescript-node-buffer/docs/Node.Buffer.Class)
13+[works in both Node](https://pursuit.purescript.org/packages/purescript-node-buffer/docs/Node.Buffer.Class)
14and browser runtime environments.
14and browser runtime environments.
15
15
16## Compile-time Code Generation
16## Compile-time Code Generation
@@ -19,14 +19,20 @@ The `shell.nix` environment provides
19
19
20* The Purescript toolchain
20* The Purescript toolchain
21* The `protoc` compiler
21* The `protoc` compiler
22+* The `protoc` compiler plugin for Purescript. The `bin/protoc-gen-purescript`
22−* The `bin/protoc-gen-purescript` executable on the `PATH`
23+ executable is on the `PATH`
23 [so that `protoc` can find it](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.compiler.plugin).
24 [so that `protoc` can find it](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.compiler.plugin).
24
25
26+Enter the Nix shell.
27+
28+```
29+nix-shell
30+```
31+
25−Enter the Nix shell and run `protoc` with the directory `OUT_DIR` name
32+Run `protoc` with the directory `OUT_DIR` name
26for the generated `.purs` files, and the `$PROTO_FILES` list of input
33for the generated `.purs` files, and the `$PROTO_FILES` list of input
27`.proto` files.
34`.proto` files.
28
35
29```
36```
30−nix-shell
31protoc --purescript_out=$OUT_DIR $PROTO_FILES
37protoc --purescript_out=$OUT_DIR $PROTO_FILES
32```
38```
bin/protoc-gen-purescript+1−1
@@ -1 +1 @@
1−cd ..; spago run --quiet --main ProtocPlugin.Main --node-args "$@"
1+spago run --main ProtocPlugin.Main
package-lock.jsonnew file+13
@@ -0,0 +1,13 @@
1+{
2+ "name": "purescript-protobuf",
3+ "version": "0.1.0",
4+ "lockfileVersion": 1,
5+ "requires": true,
6+ "dependencies": {
7+ "long": {
8+ "version": "4.0.0",
9+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
10+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
11+ }
12+ }
13+}
package.jsonnew file+10
@@ -0,0 +1,10 @@
1+{
2+ "name": "purescript-protobuf",
3+ "version": "0.1.0",
4+ "description": "Google Protocol Buffers",
5+ "author": "Cross Compass, LLC.",
6+ "license": "BSD-3-Clause",
7+ "dependencies": {
8+ "long": "^4.0.0"
9+ }
10+}
shell.nix+2−1
@@ -23,6 +23,7 @@ pkgs.mkShell {
23 echo "Purescript Protobuf development environment."
23 echo "Purescript Protobuf development environment."
24 echo "To build purescript-protobuf, run this command:"
24 echo "To build purescript-protobuf, run this command:"
25 echo ""
25 echo ""
26+ echo " npm install"
26 echo " spago build"
27 echo " spago build"
27 echo ""
28 echo ""
28 echo "To test purescript-protobuf, run this command:"
29 echo "To test purescript-protobuf, run this command:"
@@ -31,7 +32,7 @@ pkgs.mkShell {
31 echo ""
32 echo ""
32 echo "To generate Purescript files from .proto files, run this command:"
33 echo "To generate Purescript files from .proto files, run this command:"
33 echo ""
34 echo ""
34− echo " protoc --purescript_out=\$OUT_DIR \$PROTO_FILES"
35+ echo " protoc --purescript_out=./test test/test.proto"
35 echo ""
36 echo ""
36 '';
37 '';
37 LC_ALL = "C.UTF-8"; # https://github.com/purescript/spago/issues/507
38 LC_ALL = "C.UTF-8"; # https://github.com/purescript/spago/issues/507
src/Protobuf/Common.purs+5
@@ -8,6 +8,8 @@ import Prelude
8import Data.UInt (UInt)
8import Data.UInt (UInt)
9import Data.Enum (class Enum, fromEnum, class BoundedEnum, Cardinality(..))
9import Data.Enum (class Enum, fromEnum, class BoundedEnum, Cardinality(..))
10import Data.Maybe (Maybe(..))
10import Data.Maybe (Maybe(..))
11+import Data.Generic.Rep(class Generic)
12+import Data.Generic.Rep.Show (genericShow)
11
13
12type FieldNumber = UInt
14type FieldNumber = UInt
13
15
@@ -19,6 +21,7 @@ data WireType
19 | Bits32
21 | Bits32
20
22
21derive instance eqWireType :: Eq WireType
23derive instance eqWireType :: Eq WireType
24+derive instance genericWireType :: Generic WireType _
22
25
23instance ordWireType :: Ord WireType
26instance ordWireType :: Ord WireType
24 where
27 where
@@ -53,3 +56,5 @@ instance boundedEnumWireType :: BoundedEnum WireType
53 fromEnum LenDel = 2
56 fromEnum LenDel = 2
54 fromEnum Bits32 = 5
57 fromEnum Bits32 = 5
55
58
59+instance showWireType :: Show WireType where show = genericShow
60+
src/ProtocPlugin/Main.purs+73−35
@@ -9,11 +9,13 @@ import Data.Array (snoc)
9import Data.Foldable (foldl)
9import Data.Foldable (foldl)
10import Data.Symbol (SProxy(..))
10import Data.Symbol (SProxy(..))
11import Data.Tuple (Tuple(..))
11import Data.Tuple (Tuple(..))
12+import Data.Generic.Rep(class Generic)
13+import Data.Generic.Rep.Show (genericShow)
12import Data.Long.Unsigned (toInt)
14import Data.Long.Unsigned (toInt)
13import Data.UInt (UInt)
15import Data.UInt (UInt)
14import Data.UInt as UInt
16import Data.UInt as UInt
15
17
16−import Text.Parsing.Parser (ParserT, fail)
18+import Text.Parsing.Parser (ParserT, fail, runParserT)
17import Text.Parsing.Parser.Combinators (manyTill)
19import Text.Parsing.Parser.Combinators (manyTill)
18import Text.Parsing.Parser.DataView (eof, takeN)
20import Text.Parsing.Parser.DataView (eof, takeN)
19
21
@@ -24,11 +26,12 @@ import Protobuf.Decode as Decode
24-- import Protobuf.Encode as Encode
26-- import Protobuf.Encode as Encode
25import Protobuf.Common (WireType(..))
27import Protobuf.Common (WireType(..))
26
28
27−import Node.Process (stdin, stdout)
29+import Node.Process (stdin, stdout, stderr)
28import Node.Stream (read, writeString, onReadable)
30import Node.Stream (read, writeString, onReadable)
29import Node.Buffer (toArrayBuffer)
31import Node.Buffer (toArrayBuffer)
30import Node.Encoding (Encoding(..))
32import Node.Encoding (Encoding(..))
31−import Data.ArrayBuffer.ArrayBuffer as AB
33+-- import Data.ArrayBuffer.ArrayBuffer as AB
34+import Data.ArrayBuffer.DataView as DV
32import Data.ArrayBuffer.Types (DataView)
35import Data.ArrayBuffer.Types (DataView)
33
36
34main :: Effect Unit
37main :: Effect Unit
@@ -39,31 +42,34 @@ main = do
39 Nothing -> pure unit
42 Nothing -> pure unit
40 Just stdinbuf -> do
43 Just stdinbuf -> do
41 stdinab <- toArrayBuffer stdinbuf
44 stdinab <- toArrayBuffer stdinbuf
42− void $ writeString stdout UTF8 (show $ AB.byteLength stdinab) (pure unit)
45+ -- void $ writeString stdout UTF8 (show $ AB.byteLength stdinab) (pure unit)
46+ let stdinview = DV.whole stdinab
47+ request <- runParserT stdinview parseCodeGeneratorRequest
48+ void $ writeString stderr UTF8 (show request) (pure unit)
43
49
44−type CodeGeneratorRequestBuilder = RecordB.Builder CodeGeneratorRequestRow CodeGeneratorRequestRow
50+type CodeGeneratorRequestBuilder = RecordB.Builder CodeGeneratorRequestR CodeGeneratorRequestR
45
51
46−parseCodeGeneratorRequest :: ParserT DataView Effect CodeGeneratorRequestRow
52+parseCodeGeneratorRequest :: ParserT DataView Effect CodeGeneratorRequest
47parseCodeGeneratorRequest = do
53parseCodeGeneratorRequest = do
48 builders <- manyTill parseField eof
54 builders <- manyTill parseField eof
49− pure $ build (foldl (>>>) identity builders) defaultCodeGeneratorRequest
55+ pure $ CodeGeneratorRequest $ build (foldl (>>>) identity builders) defaultCodeGeneratorRequest
50 where
56 where
51 parseField :: ParserT DataView Effect CodeGeneratorRequestBuilder
57 parseField :: ParserT DataView Effect CodeGeneratorRequestBuilder
52 parseField = do
58 parseField = do
53 Tuple fieldNumber wireType <- Decode.tag32
59 Tuple fieldNumber wireType <- Decode.tag32
54 case unit of
60 case unit of
55− _ | fieldNumber == fn_CodeGeneratorRequestRow_file_to_generate -> do
61+ _ | fieldNumber == fn_CodeGeneratorRequest_file_to_generate -> do
56 x <- Decode.string
62 x <- Decode.string
57− pure $ modify fs_CodeGeneratorRequestRow_file_to_generate $ flip snoc x
63+ pure $ modify fs_CodeGeneratorRequest_file_to_generate $ flip snoc x
58− | fieldNumber == fn_CodeGeneratorRequestRow_parameter -> do
64+ | fieldNumber == fn_CodeGeneratorRequest_parameter -> do
59 x <- Decode.string
65 x <- Decode.string
60− pure $ modify fs_CodeGeneratorRequestRow_parameter $ const $ Just x
66+ pure $ modify fs_CodeGeneratorRequest_parameter $ const $ Just x
61− -- | fieldNumber == fn_CodeGeneratorRequestRow_proto_file -> do
67+ -- | fieldNumber == fn_CodeGeneratorRequest_proto_file -> do
62 -- x <- parseFileDescriptorProto
68 -- x <- parseFileDescriptorProto
63− -- pure $ modify fs_CodeGeneratorRequestRow_proto_file $ flip snoc x
69+ -- pure $ modify fs_CodeGeneratorRequest_proto_file $ flip snoc x
64− -- | fieldNumber == fn_CodeGeneratorRequestRow_compiler_version -> do
70+ -- | fieldNumber == fn_CodeGeneratorRequest_compiler_version -> do
65 -- x <- parseVersion
71 -- x <- parseVersion
66− -- pure $ modify fs_CodeGeneratorRequestRow_compiler_version $ const $ Just x
72+ -- pure $ modify fs_CodeGeneratorRequest_compiler_version $ const $ Just x
67 _ -> parseUnknownField wireType *> pure identity
73 _ -> parseUnknownField wireType *> pure identity
68
74
69 -- IMPORTANT For embedded message fields, the parser merges multiple instances of the same field,
75 -- IMPORTANT For embedded message fields, the parser merges multiple instances of the same field,
@@ -86,44 +92,56 @@ parseUnknownField wireType = case wireType of
86
92
87-- https://pursuit.purescript.org/packages/purescript-record
93-- https://pursuit.purescript.org/packages/purescript-record
88
94
95+-- IMPORTANT We need to wrap our structural record types in a nominal
96+-- data type so that we can nest records.
97+-- https://github.com/purescript/documentation/blob/master/errors/CycleInTypeSynonym.md
98+-- And so that we can assign instances?
99+newtype CodeGeneratorRequest = CodeGeneratorRequest CodeGeneratorRequestR
100+derive instance genericCodeGeneratorRequest :: Generic CodeGeneratorRequest _
101+instance showCodeGeneratorRequest :: Show CodeGeneratorRequest where show = genericShow
89-- | Data type for a CodeGenerationRequest message.
102-- | Data type for a CodeGenerationRequest message.
90-- | https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.compiler.plugin.pb
103-- | https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.compiler.plugin.pb
91−-- data CodeGeneratorRequest = CodeGeneratorRequest CodeGeneratorRequestRow
104+type CodeGeneratorRequestR =
92−-- derive instance showCodeGeneratorRequest :: Show CodeGeneratorRequest
93−type CodeGeneratorRequestRow =
94 { file_to_generate :: Array String -- 1
105 { file_to_generate :: Array String -- 1
95 , parameter :: Maybe String -- 2
106 , parameter :: Maybe String -- 2
96 , proto_file :: Array FileDescriptorProto -- 15
107 , proto_file :: Array FileDescriptorProto -- 15
97 , compiler_version :: Maybe Version -- 3
108 , compiler_version :: Maybe Version -- 3
98 }
109 }
110+
99−defaultCodeGeneratorRequest :: CodeGeneratorRequestRow
111+defaultCodeGeneratorRequest :: CodeGeneratorRequestR
100defaultCodeGeneratorRequest =
112defaultCodeGeneratorRequest =
101 { file_to_generate: []
113 { file_to_generate: []
102 , parameter: Nothing
114 , parameter: Nothing
103 , proto_file: []
115 , proto_file: []
104 , compiler_version: Nothing
116 , compiler_version: Nothing
105 }
117 }
106−fs_CodeGeneratorRequestRow_file_to_generate = SProxy :: SProxy "file_to_generate"
118+fs_CodeGeneratorRequest_file_to_generate = SProxy :: SProxy "file_to_generate"
107−fs_CodeGeneratorRequestRow_parameter = SProxy :: SProxy "parameter"
119+fs_CodeGeneratorRequest_parameter = SProxy :: SProxy "parameter"
108−fs_CodeGeneratorRequestRow_proto_file = SProxy :: SProxy "proto_file"
120+fs_CodeGeneratorRequest_proto_file = SProxy :: SProxy "proto_file"
109−fs_CodeGeneratorRequestRow_compiler_version = SProxy :: SProxy "compiler_version"
121+fs_CodeGeneratorRequest_compiler_version = SProxy :: SProxy "compiler_version"
110−fn_CodeGeneratorRequestRow_file_to_generate = UInt.fromInt 1 :: UInt
122+fn_CodeGeneratorRequest_file_to_generate = UInt.fromInt 1 :: UInt
111−fn_CodeGeneratorRequestRow_parameter = UInt.fromInt 2 :: UInt
123+fn_CodeGeneratorRequest_parameter = UInt.fromInt 2 :: UInt
112−fn_CodeGeneratorRequestRow_proto_file = UInt.fromInt 15 :: UInt
124+fn_CodeGeneratorRequest_proto_file = UInt.fromInt 15 :: UInt
113−fn_CodeGeneratorRequestRow_compiler_version = UInt.fromInt 3 :: UInt
125+fn_CodeGeneratorRequest_compiler_version = UInt.fromInt 3 :: UInt
114
126
115-- | The version number of protocol compiler.
127-- | The version number of protocol compiler.
116−data Version = Version
128+newtype Version = Version VersionR
129+derive instance genericVersion :: Generic Version _
130+instance showVersion :: Show Version where show = genericShow
131+type VersionR =
117 { major :: Maybe Int --1
132 { major :: Maybe Int --1
118 , minor :: Maybe Int --2
133 , minor :: Maybe Int --2
119 , patch :: Maybe Int --3
134 , patch :: Maybe Int --3
120 , suffix :: Maybe String -- 4
135 , suffix :: Maybe String -- 4
121 }
136 }
122
137
138+newtype FileDescriptorProto = FileDescriptorProto FileDescriptorProtoR
139+derive instance genericFileDescriptorProto :: Generic FileDescriptorProto _
140+instance showFileDescriptorProto :: Show FileDescriptorProto where show = const "" -- TODO genericShow
123-- | Describes a complete .proto file.
141-- | Describes a complete .proto file.
124-- | https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor.pb
142-- | https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor.pb
125-- | The syntax for decoding this is "proto2"?
143-- | The syntax for decoding this is "proto2"?
126−data FileDescriptorProto = FileDescriptorProto
144+type FileDescriptorProtoR =
127 { name :: Maybe String -- 1
145 { name :: Maybe String -- 1
128 , package :: Maybe String -- 2
146 , package :: Maybe String -- 2
129 , dependency :: Array String -- 3
147 , dependency :: Array String -- 3
@@ -137,8 +155,11 @@ data FileDescriptorProto = FileDescriptorProto
137 , syntax :: Maybe String -- 12
155 , syntax :: Maybe String -- 12
138 }
156 }
139
157
158+newtype DescriptorProto = DescriptorProto DescriptorProtoR
159+derive instance genericDescriptorProto :: Generic DescriptorProto _
160+instance showDescriptorProto :: Show DescriptorProto where show = const "" -- TODO genericShow
140-- | Describes a message type.
161-- | Describes a message type.
141−data DescriptorProto = DescriptorProto
162+type DescriptorProtoR =
142 { name :: Maybe String -- 1
163 { name :: Maybe String -- 1
143 , field :: Array FieldDescriptorProto -- 2
164 , field :: Array FieldDescriptorProto -- 2
144 , extension :: Array FieldDescriptorProto -- 6
165 , extension :: Array FieldDescriptorProto -- 6
@@ -150,7 +171,10 @@ data DescriptorProto = DescriptorProto
150 -- TODO eh who cares about reserved ranges
171 -- TODO eh who cares about reserved ranges
151 }
172 }
152
173
153−data DescriptorProto_ExtensionRange = DescriptorProto_ExtensionRange
174+newtype DescriptorProto_ExtensionRange = DescriptorProto_ExtensionRange DescriptorProto_ExtensionRangeR
175+derive instance genericDescriptorProto_ExtensionRange :: Generic DescriptorProto_ExtensionRange _
176+instance showDescriptorProto_ExtensionRange :: Show DescriptorProto_ExtensionRange where show = genericShow
177+type DescriptorProto_ExtensionRangeR =
154 { start :: Maybe Int -- 1
178 { start :: Maybe Int -- 1
155 , end :: Maybe Int -- 2
179 , end :: Maybe Int -- 2
156 -- TODO , options :: Maybe ExtensionRangeOptions
180 -- TODO , options :: Maybe ExtensionRangeOptions
@@ -163,9 +187,14 @@ data DescriptorProto_ExtensionRange = DescriptorProto_ExtensionRange
163-- }
187-- }
164
188
165data FieldDescriptorProto_Label = OPTIONAL | REQUIRED | REPEATED
189data FieldDescriptorProto_Label = OPTIONAL | REQUIRED | REPEATED
190+derive instance genericFieldDescriptorProto_Label :: Generic FieldDescriptorProto_Label _
191+instance showFieldDescriptorProto_Label :: Show FieldDescriptorProto_Label where show = genericShow
166
192
167-- | Describes a field within a message.
193-- | Describes a field within a message.
168−data FieldDescriptorProto = FieldDescriptorProto
194+newtype FieldDescriptorProto = FieldDescriptorProto FieldDescriptorProtoR
195+derive instance genericFieldDescriptorProto :: Generic FieldDescriptorProto _
196+instance showFieldDescriptorProto :: Show FieldDescriptorProto where show = genericShow
197+type FieldDescriptorProtoR =
169 { name :: Maybe String -- 1
198 { name :: Maybe String -- 1
170 , number :: Maybe Int -- 3
199 , number :: Maybe Int -- 3
171 , label :: Maybe FieldDescriptorProto_Label -- 4
200 , label :: Maybe FieldDescriptorProto_Label -- 4
@@ -179,13 +208,19 @@ data FieldDescriptorProto = FieldDescriptorProto
179 }
208 }
180
209
181-- | Describes a oneof.
210-- | Describes a oneof.
182−data OneofDescriptorProto = OneofDescriptorProto
211+newtype OneofDescriptorProto = OneofDescriptorProto OneofDescriptorProtoR
212+derive instance genericOneofDescriptorProto :: Generic OneofDescriptorProto _
213+instance showOneofDescriptorProto :: Show OneofDescriptorProto where show = genericShow
214+type OneofDescriptorProtoR =
183 { name :: Maybe String -- 1
215 { name :: Maybe String -- 1
184 -- TODO , options :: Maybe OneofOptions -- 2
216 -- TODO , options :: Maybe OneofOptions -- 2
185 }
217 }
186
218
187-- | Describes an enum type.
219-- | Describes an enum type.
188−data EnumDescriptorProto = EnumDescriptorProto
220+newtype EnumDescriptorProto = EnumDescriptorProto EnumDescriptorProtoR
221+derive instance genericEnumDescriptorProto :: Generic EnumDescriptorProto _
222+instance showEnumDescriptorProto :: Show EnumDescriptorProto where show = genericShow
223+type EnumDescriptorProtoR =
189 { name :: Maybe String -- 1
224 { name :: Maybe String -- 1
190 , value :: Array EnumValueDescriptorProto -- 2
225 , value :: Array EnumValueDescriptorProto -- 2
191 -- TODO , options :: Maybe EnumOptions -- 3
226 -- TODO , options :: Maybe EnumOptions -- 3
@@ -194,7 +229,10 @@ data EnumDescriptorProto = EnumDescriptorProto
194 }
229 }
195
230
196-- | Describes a value within an enum.
231-- | Describes a value within an enum.
197−data EnumValueDescriptorProto = EnumValueDescriptorProto
232+newtype EnumValueDescriptorProto = EnumValueDescriptorProto EnumValueDescriptorProtoR
233+derive instance genericEnumValueDescriptorProto :: Generic EnumValueDescriptorProto _
234+instance showEnumValueDescriptorProto :: Show EnumValueDescriptorProto where show = genericShow
235+type EnumValueDescriptorProtoR =
198 { name :: Maybe String -- 1
236 { name :: Maybe String -- 1
199 , number :: Maybe Int -- 2
237 , number :: Maybe Int -- 2
200 -- TODO , options :: Maybe EnumValueOptions -- 3
238 -- TODO , options :: Maybe EnumValueOptions -- 3
test/test.protonew file+5
@@ -0,0 +1,5 @@
1+syntax = "proto3";
2+
3+message Test2 {
4+ string b = 2;
5+}