From 15276b7ff533526e0a660e0f0c5cda404fe84c32 Mon Sep 17 00:00:00 2001 From: Erik De Smedt Date: Wed, 7 Feb 2024 15:04:07 +0100 Subject: [PATCH] msggen: Add notification-structs to `cln_rpc` In Core Lightning notifications are JSON-messages. This commit introduces structs that can be used to parse the notification messages. Using `msggen` all required tructs are automatically generated --- cln-rpc/src/notifications.rs | 96 ++++++++++++++++++- contrib/msggen/msggen/__main__.py | 6 +- contrib/msggen/msggen/gen/rpc/__init__.py | 4 + contrib/msggen/msggen/gen/rpc/notification.py | 46 +++++++++ 4 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 contrib/msggen/msggen/gen/rpc/__init__.py create mode 100644 contrib/msggen/msggen/gen/rpc/notification.py diff --git a/cln-rpc/src/notifications.rs b/cln-rpc/src/notifications.rs index 4256350f9..17326dcbc 100644 --- a/cln-rpc/src/notifications.rs +++ b/cln-rpc/src/notifications.rs @@ -1,4 +1,94 @@ -use serde::{Deserialize, Serialize}; +// This file is autogenerated by `msggen` +// Do not edit it manually, your changes will be overwritten + + + +use crate::primitives::*; +use serde::{Serialize, Deserialize}; +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum Notification { + #[serde(rename = "block_added")] + BlockAdded(BlockAddedNotification), + #[serde(rename = "channel_open_failed")] + ChannelOpenFailed(ChannelOpenFailedNotification), + #[serde(rename = "channel_opened")] + ChannelOpened(ChannelOpenedNotification), + #[serde(rename = "connect")] + Connect(ConnectNotification), + #[serde(rename = "custommsg")] + CustomMsg(CustomMsgNotification), +} + + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct BlockAddedNotification { + #[serde(skip_serializing_if = "Option::is_none")] + pub hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub height: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ChannelOpenFailedNotification { + #[serde(skip_serializing_if = "Option::is_none")] + pub channel_id: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ChannelOpenedNotification { + #[serde(skip_serializing_if = "Option::is_none")] + pub channel_ready: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub funding_msat: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub funding_txid: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, +} + +#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +pub enum ConnectDirection { + #[serde(rename = "in")] + IN = 0, + #[serde(rename = "out")] + OUT = 1, +} + +impl TryFrom for ConnectDirection { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ConnectDirection::IN), + 1 => Ok(ConnectDirection::OUT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ConnectDirection", o)), + } + } +} + +impl ToString for ConnectDirection { + fn to_string(&self) -> String { + match self { + ConnectDirection::IN => "IN", + ConnectDirection::OUT => "OUT", + }.to_string() + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ConnectNotification { + #[serde(skip_serializing_if = "Option::is_none")] + pub address: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub direction: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CustomMsgNotification { + #[serde(skip_serializing_if = "Option::is_none")] + pub payload: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub peer_id: Option, +} -#[derive(Debug, Deserialize, Serialize)] -pub enum Notification {} diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 7c6b638f5..cb0124e9b 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -4,7 +4,7 @@ import argparse from pathlib import Path from msggen.gen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator from msggen.gen.grpc2py import Grpc2PyGenerator -from msggen.gen.rpc.rust import RustGenerator +from msggen.gen.rpc import RustGenerator, NotificationGenerator from msggen.gen.generator import GeneratorChain from msggen.utils import load_jsonrpc_service, combine_schemas import logging @@ -49,6 +49,10 @@ def add_handler_gen_rust_jsonrpc(generator_chain: GeneratorChain, meta): dest = open(fname, "w") generator_chain.add_generator(RustGenerator(dest, meta)) + fname = Path("cln-rpc") / "src" / "notifications.rs" + dest = open(fname, "w") + generator_chain.add_generator(NotificationGenerator(dest, meta)) + def load_msggen_meta(): meta = json.load(open('.msggen.json', 'r')) diff --git a/contrib/msggen/msggen/gen/rpc/__init__.py b/contrib/msggen/msggen/gen/rpc/__init__.py new file mode 100644 index 000000000..dee5ba87e --- /dev/null +++ b/contrib/msggen/msggen/gen/rpc/__init__.py @@ -0,0 +1,4 @@ +from msggen.gen.rpc.notification import NotificationGenerator +from msggen.gen.rpc.rust import RustGenerator + +__all__ = [RustGenerator, NotificationGenerator] diff --git a/contrib/msggen/msggen/gen/rpc/notification.py b/contrib/msggen/msggen/gen/rpc/notification.py new file mode 100644 index 000000000..8cc8e466e --- /dev/null +++ b/contrib/msggen/msggen/gen/rpc/notification.py @@ -0,0 +1,46 @@ +import logging +from textwrap import dedent, indent +from typing import Any, Dict, Optional, TextIO + +from msggen.model import Service +from msggen.gen.generator import IGenerator +from msggen.gen.rpc.rust import gen_composite + + +class NotificationGenerator(IGenerator): + + def __init__(self, dest: TextIO, meta: Dict[str, Any]): + self.dest = dest + self.logger = logging.getLogger(__name__) + self.meta = meta + + def write(self, text: str, numindent: Optional[int] = None) -> None: + raw = dedent(text) + if numindent is not None: + raw = indent(text, " " * numindent) + + self.dest.write(raw) + + def generate_enum(self, service: Service): + self.write("#[derive(Clone, Debug, Deserialize, Serialize)]\n") + self.write("pub enum Notification {\n") + for notification in service.notifications: + tn = notification.typename + name = notification.name + self.write(f'#[serde(rename = "{name}")]\n', numindent=1) + self.write(f"{tn}({tn}Notification),\n", numindent=1), + self.write("}\n") + + def generate(self, service: Service) -> None: + self.write("// This file is autogenerated by `msggen`\n") + self.write("// Do not edit it manually, your changes will be overwritten\n\n\n") + self.write("\n") + self.write("use crate::primitives::*;\n") + self.write("use serde::{Serialize, Deserialize};\n") + + self.generate_enum(service) + self.write("\n\n") + + for notification in service.notifications: + _, resp_decl = gen_composite(notification.response, self.meta) + self.write(resp_decl)