Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions contrib/msggen/examples/generator_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#! /usr/bin/python3
"""
Example of usage msggen module.

This example introduces a fake generator to understand how the
package works, If you would like to see a real generator example
try to see the Rust generator in the `msggen/gen/rust.py`

author: https://github.com/vincenzopalazzo
"""
from msggen.gen.generator import GeneratorChain, IGenerator
from msggen import Service
from msggen.utils import load_jsonrpc_service


class MonkylangGen(IGenerator):
"""This is the custom generator that implements a monkylang generator
that uses the interface handler IGenerator."""

def generate(self, service: Service):
self.write('println("Monky")')


def register_monkylang_gen(generator_chain: GeneratorChain):
"""Helper function to register the custom generator, and
load the correct path of the json schema."""
file = '<your_path_of_result>'
dest = open(file, 'w')
generator_chain.add_generator(MonkylangGen(dest))


if __name__ == '__main__':
schema_dir = '<path_of_json_schema_dir>'
service = load_jsonrpc_service(schema_dir=schema_dir)
generator_chain = GeneratorChain()
register_monkylang_gen(generator_chain)
generator_chain.generate(service)
153 changes: 18 additions & 135 deletions contrib/msggen/msggen/__main__.py
Original file line number Diff line number Diff line change
@@ -1,153 +1,31 @@
from msggen.model import Method, CompositeField, Service
from msggen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator
from msggen.rust import RustGenerator
from pathlib import Path
import subprocess
import json
from msggen.gen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator
from msggen.gen.rust import RustGenerator
from msggen.gen.generator import GeneratorChain
from msggen.utils import repo_root, load_jsonrpc_service


# Sometimes we want to rename a method, due to a name clash
method_name_override = {
"Connect": "ConnectPeer",
}


def repo_root():
path = subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
return Path(path.strip().decode('UTF-8'))


def load_jsonrpc_method(name):
"""Load a method based on the file naming conventions for the JSON-RPC.
"""
base_path = (repo_root() / "doc" / "schemas").resolve()
req_file = base_path / f"{name.lower()}.request.json"
resp_file = base_path / f"{name.lower()}.schema.json"
request = CompositeField.from_js(json.load(open(req_file)), path=name)
response = CompositeField.from_js(json.load(open(resp_file)), path=name)

# Normalize the method request and response typename so they no
# longer conflict.
request.typename += "Request"
response.typename += "Response"

return Method(
name=method_name_override.get(name, name),
request=request,
response=response,
)


def load_jsonrpc_service():
method_names = [
"Getinfo",
"ListPeers",
"ListFunds",
"SendPay",
"ListChannels",
"AddGossip",
"AutoCleanInvoice",
"CheckMessage",
"Close",
"Connect",
"CreateInvoice",
"Datastore",
"CreateOnion",
"DelDatastore",
"DelExpiredInvoice",
"DelInvoice",
"Invoice",
"ListDatastore",
"ListInvoices",
"SendOnion",
"ListSendPays",
"ListTransactions",
"Pay",
"ListNodes",
"WaitAnyInvoice",
"WaitInvoice",
"WaitSendPay",
"NewAddr",
"Withdraw",
"KeySend",
"FundPsbt",
"SendPsbt",
"SignPsbt",
"UtxoPsbt",
"TxDiscard",
"TxPrepare",
"TxSend",
# "decodepay",
# "decode",
# "delpay",
# "disableoffer",
"Disconnect",
"Feerates",
# "fetchinvoice",
# "fundchannel_cancel",
# "fundchannel_complete",
# "fundchannel",
# "fundchannel_start",
# "funderupdate",
# "getlog",
"GetRoute",
# "getsharedsecret",
"ListForwards",
# "listoffers",
"ListPays",
# "multifundchannel",
# "multiwithdraw",
# "offerout",
# "offer",
# "openchannel_abort",
# "openchannel_bump",
# "openchannel_init",
# "openchannel_signed",
# "openchannel_update",
# "parsefeerate",
"Ping",
# "plugin",
# "reserveinputs",
# "sendcustommsg",
# "sendinvoice",
# "sendonionmessage",
# "setchannelfee",
"SignMessage",
# "unreserveinputs",
# "waitblockheight",
# "ListConfigs",
# "check", # No point in mapping this one
# "Stop", # Breaks a core assumption (root is an object) can't map unless we change this
# "notifications", # No point in mapping this
# "help",
]
methods = [load_jsonrpc_method(name) for name in method_names]
service = Service(name="Node", methods=methods)
service.includes = ['primitives.proto'] # Make sure we have the primitives included.
return service


def gengrpc(service, meta):
def add_handler_gen_grpc(generator_chain: GeneratorChain, meta):
"""Load all mapped RPC methods, wrap them in a Service, and split them into messages.
"""
fname = repo_root() / "cln-grpc" / "proto" / "node.proto"
dest = open(fname, "w")
GrpcGenerator(dest, meta).generate(service)
generator_chain.add_generator(GrpcGenerator(dest, meta))

fname = repo_root() / "cln-grpc" / "src" / "convert.rs"
dest = open(fname, "w")
GrpcConverterGenerator(dest).generate(service)
GrpcUnconverterGenerator(dest).generate(service)
generator_chain.add_generator(GrpcConverterGenerator(dest))
generator_chain.add_generator(GrpcUnconverterGenerator(dest))

fname = repo_root() / "cln-grpc" / "src" / "server.rs"
dest = open(fname, "w")
GrpcServerGenerator(dest).generate(service)
generator_chain.add_generator(GrpcServerGenerator(dest))


def genrustjsonrpc(service):
def add_handler_gen_rust_jsonrpc(generator_chain: GeneratorChain):
fname = repo_root() / "cln-rpc" / "src" / "model.rs"
dest = open(fname, "w")
RustGenerator(dest).generate(service)
generator_chain.add_generator(RustGenerator(dest))


def load_msggen_meta():
Expand All @@ -163,8 +41,13 @@ def write_msggen_meta(meta):
def run():
service = load_jsonrpc_service()
meta = load_msggen_meta()
gengrpc(service, meta)
genrustjsonrpc(service)
generator_chain = GeneratorChain()

add_handler_gen_grpc(generator_chain, meta)
add_handler_gen_rust_jsonrpc(generator_chain)

generator_chain.generate(service)

write_msggen_meta(meta)


Expand Down
3 changes: 3 additions & 0 deletions contrib/msggen/msggen/gen/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .generator import IGenerator, GeneratorChain # noqa
from .grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator # noqa
from .rust import RustGenerator # noqa
36 changes: 36 additions & 0 deletions contrib/msggen/msggen/gen/generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""
Generator interface!

author: https://github.com/vincenzopalazzo
"""
from abc import ABC, abstractmethod

from msggen.model import Service


class IGenerator(ABC):
"""
Chain of responsibility handler that need to be
implemented by all the generators.
"""

@abstractmethod
def generate(self, service: Service):
pass


class GeneratorChain:
"""
Chain responsibility pattern implementation to generalize
the generation method.
"""

def __init__(self):
self.generators = []

def add_generator(self, generator: IGenerator) -> None:
self.generators.append(generator)

def generate(self, service: Service) -> None:
for _, generator in enumerate(self.generators):
generator.generate(service)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# A grpc model
from .model import ArrayField, Field, CompositeField, EnumField, PrimitiveField, Service
from msggen.model import ArrayField, Field, CompositeField, EnumField, PrimitiveField, Service
from msggen.gen import IGenerator
from typing import TextIO, List, Dict, Any
from textwrap import indent, dedent
import re
Expand Down Expand Up @@ -51,7 +52,7 @@
}


class GrpcGenerator:
class GrpcGenerator(IGenerator):
"""A generator that generates protobuf files.
"""

Expand Down Expand Up @@ -235,7 +236,7 @@ def generate(self, service: Service) -> None:
self.generate_message(message)


class GrpcConverterGenerator:
class GrpcConverterGenerator(IGenerator):
def __init__(self, dest: TextIO):
self.dest = dest
self.logger = logging.getLogger("msggen.grpc.GrpcConversionGenerator")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
import sys
import re

from .model import (ArrayField, CompositeField, EnumField,
PrimitiveField, Service)
from msggen.model import (ArrayField, CompositeField, EnumField,
PrimitiveField, Service)
from msggen.gen.generator import IGenerator

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -196,7 +197,7 @@ def gen_composite(c) -> Tuple[str, str]:
return ("", r)


class RustGenerator:
class RustGenerator(IGenerator):
def __init__(self, dest: TextIO):
self.dest = dest

Expand Down
1 change: 1 addition & 0 deletions contrib/msggen/msggen/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .utils import load_jsonrpc_method, load_jsonrpc_service, repo_root # noqa
Loading