diff --git a/.msggen.json b/.msggen.json index 899e574c41e4..c83b74a8665f 100644 --- a/.msggen.json +++ b/.msggen.json @@ -1270,6 +1270,7 @@ "CreateinvoiceRequest": { "CreateInvoice.invstring": 1, "CreateInvoice.label": 2, + "CreateInvoice.label_int": 4, "CreateInvoice.preimage": 3 }, "CreateinvoiceResponse": { @@ -1323,6 +1324,7 @@ "Datastore.hex": 2, "Datastore.key": 5, "Datastore.key[]": 1, + "Datastore.key_string": 7, "Datastore.mode": 3, "Datastore.string": 6 }, @@ -1338,7 +1340,8 @@ "DatastoreUsage.datastoreusage.total_bytes": 2 }, "DatastoreusageRequest": { - "DatastoreUsage.key": 1 + "DatastoreUsage.key": 1, + "DatastoreUsage.key_string": 2 }, "DatastoreusageResponse": { "DatastoreUsage.datastoreusage": 1 @@ -1524,7 +1527,8 @@ "DeldatastoreRequest": { "DelDatastore.generation": 2, "DelDatastore.key": 3, - "DelDatastore.key[]": 1 + "DelDatastore.key[]": 1, + "DelDatastore.key_string": 4 }, "DeldatastoreResponse": { "DelDatastore.generation": 2, @@ -1544,6 +1548,7 @@ "DelinvoiceRequest": { "DelInvoice.desconly": 3, "DelInvoice.label": 1, + "DelInvoice.label_u64": 4, "DelInvoice.status": 2 }, "DelinvoiceResponse": { @@ -2056,8 +2061,11 @@ "Invoice.description": 2, "Invoice.expiry": 7, "Invoice.exposeprivatechannels": 8, + "Invoice.exposeprivatechannels_arr_scid": 12, + "Invoice.exposeprivatechannels_scid": 13, "Invoice.fallbacks[]": 4, "Invoice.label": 3, + "Invoice.label_int": 11, "Invoice.msatoshi": 1, "Invoice.preimage": 5 }, @@ -2716,7 +2724,8 @@ }, "ListdatastoreRequest": { "ListDatastore.key": 2, - "ListDatastore.key[]": 1 + "ListDatastore.key[]": 1, + "ListDatastore.key_string": 3 }, "ListdatastoreResponse": { "ListDatastore.datastore[]": 1 @@ -2843,6 +2852,7 @@ "ListInvoices.index": 5, "ListInvoices.invstring": 2, "ListInvoices.label": 1, + "ListInvoices.label_int": 8, "ListInvoices.limit": 7, "ListInvoices.offer_id": 4, "ListInvoices.payment_hash": 3, @@ -3364,6 +3374,7 @@ "OfferRequest": { "Offer.absolute_expiry": 6, "Offer.amount": 1, + "Offer.amount_currency": 16, "Offer.description": 2, "Offer.fronting_nodes[]": 15, "Offer.issuer": 3, @@ -3758,7 +3769,9 @@ "SetconfigRequest": { "SetConfig.config": 1, "SetConfig.transient": 3, - "SetConfig.val": 2 + "SetConfig.val": 2, + "SetConfig.val_bool": 5, + "SetConfig.val_int": 4 }, "SetconfigResponse": { "SetConfig.config": 1 @@ -4055,7 +4068,8 @@ "WaitInvoice.paid_outpoint.txid": 1 }, "WaitinvoiceRequest": { - "WaitInvoice.label": 1 + "WaitInvoice.label": 1, + "WaitInvoice.label_int": 2 }, "WaitinvoiceResponse": { "WaitInvoice.amount_msat": 6, @@ -6045,6 +6059,10 @@ "added": "pre-v0.10.1", "deprecated": null }, + "Datastore.key[]": { + "added": "pre-v0.10.1", + "deprecated": null + }, "Datastore.mode": { "added": "pre-v0.10.1", "deprecated": null @@ -6073,6 +6091,10 @@ "added": "v23.11", "deprecated": null }, + "DatastoreUsage.key[]": { + "added": "v23.11", + "deprecated": null + }, "Decode": { "added": "v23.05", "deprecated": null @@ -6829,6 +6851,10 @@ "added": "pre-v0.10.1", "deprecated": null }, + "DelDatastore.key[]": { + "added": "pre-v0.10.1", + "deprecated": null + }, "DelDatastore.string": { "added": "pre-v0.10.1", "deprecated": null @@ -8357,6 +8383,10 @@ "added": "pre-v0.10.1", "deprecated": null }, + "Invoice.exposeprivatechannels[]": { + "added": "pre-v0.10.1", + "deprecated": null + }, "Invoice.fallbacks[]": { "added": "pre-v0.10.1", "deprecated": null @@ -10185,6 +10215,10 @@ "added": "pre-v0.10.1", "deprecated": null }, + "ListDatastore.key[]": { + "added": "pre-v0.10.1", + "deprecated": null + }, "ListForwards": { "added": "pre-v0.10.1", "deprecated": null diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 639c51614df8..a13c8b656cc0 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -586,7 +586,10 @@ message ConnectAddress { message CreateinvoiceRequest { string invstring = 1; - string label = 2; + oneof label { + string label_string = 2; + sint64 label_int = 4; + } bytes preimage = 3; } @@ -620,6 +623,10 @@ message CreateinvoicePaidOutpoint { uint32 outnum = 2; } +message DatastoreRequestarr_stringWrapper { + repeated string items = 1; +} + message DatastoreRequest { // Datastore.mode enum DatastoreMode { @@ -632,19 +639,29 @@ message DatastoreRequest { optional bytes hex = 2; optional DatastoreMode mode = 3; optional uint64 generation = 4; - repeated string key = 5; + oneof key { + DatastoreRequestarr_stringWrapper key_arr_string = 5; + string key_string = 7; + } optional string string = 6; } message DatastoreResponse { + repeated string key = 1; optional uint64 generation = 2; optional bytes hex = 3; optional string string = 4; - repeated string key = 5; +} + +message DatastoreusageRequestarr_stringWrapper { + repeated string items = 1; } message DatastoreusageRequest { - repeated string key = 1; + oneof key { + DatastoreusageRequestarr_stringWrapper key_arr_string = 1; + string key_string = 2; + } } message DatastoreusageResponse { @@ -673,16 +690,23 @@ message CreateonionHops { bytes payload = 2; } +message DeldatastoreRequestarr_stringWrapper { + repeated string items = 1; +} + message DeldatastoreRequest { optional uint64 generation = 2; - repeated string key = 3; + oneof key { + DeldatastoreRequestarr_stringWrapper key_arr_string = 3; + string key_string = 4; + } } message DeldatastoreResponse { + repeated string key = 1; optional uint64 generation = 2; optional bytes hex = 3; optional string string = 4; - repeated string key = 5; } message DelinvoiceRequest { @@ -692,7 +716,10 @@ message DelinvoiceRequest { EXPIRED = 1; UNPAID = 2; } - string label = 1; + oneof label { + string label_string = 1; + uint64 label_u64 = 4; + } DelinvoiceStatus status = 2; optional bool desconly = 3; } @@ -780,14 +807,25 @@ message RecoverchannelResponse { repeated string stubs = 1; } +message InvoiceRequestarr_scidWrapper { + repeated string items = 1; +} + message InvoiceRequest { string description = 2; - string label = 3; + oneof label { + string label_string = 3; + sint64 label_int = 11; + } repeated string fallbacks = 4; optional bytes preimage = 5; optional uint32 cltv = 6; optional uint64 expiry = 7; - repeated string exposeprivatechannels = 8; + oneof exposeprivatechannels { + bool exposeprivatechannels_bool = 8; + InvoiceRequestarr_scidWrapper exposeprivatechannels_arr_scid = 12; + string exposeprivatechannels_scid = 13; + } optional bool deschashonly = 9; AmountOrAny amount_msat = 10; } @@ -854,8 +892,15 @@ message ListinvoicerequestsInvoicerequests { optional string label = 6; } +message ListdatastoreRequestarr_stringWrapper { + repeated string items = 1; +} + message ListdatastoreRequest { - repeated string key = 2; + oneof key { + ListdatastoreRequestarr_stringWrapper key_arr_string = 2; + string key_string = 3; + } } message ListdatastoreResponse { @@ -875,7 +920,10 @@ message ListinvoicesRequest { CREATED = 0; UPDATED = 1; } - optional string label = 1; + oneof label { + string label_string = 1; + sint64 label_int = 8; + } optional string invstring = 2; optional bytes payment_hash = 3; optional string offer_id = 4; @@ -1163,7 +1211,10 @@ message WaitanyinvoicePaidOutpoint { } message WaitinvoiceRequest { - string label = 1; + oneof label { + string label_string = 1; + sint64 label_int = 2; + } } message WaitinvoiceResponse { @@ -2416,7 +2467,10 @@ message MultiwithdrawResponse { } message OfferRequest { - string amount = 1; + oneof amount { + AmountOrAny amount_msat_or_any = 1; + string amount_currency = 16; + } optional string description = 2; optional string issuer = 3; optional string label = 4; @@ -2706,7 +2760,11 @@ message SetchannelChannels { message SetconfigRequest { string config = 1; - optional string val = 2; + oneof val { + string val_string = 2; + sint64 val_int = 4; + bool val_bool = 5; + } optional bool transient = 3; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 21775bada8da..6b32bcfbb5e6 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -526,7 +526,7 @@ impl From for pb::DatastoreResponse { Self { generation: c.generation, // Rule #2 for type u64? hex: c.hex.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? - // Field: Datastore.key + // Field: Datastore.key[] key: c.key.into_iter().map(|i| i.into()).collect(), // Rule #3 for type string string: c.string, // Rule #2 for type string? } @@ -569,7 +569,7 @@ impl From for pb::DeldatastoreResponse { Self { generation: c.generation, // Rule #2 for type u64? hex: c.hex.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? - // Field: DelDatastore.key + // Field: DelDatastore.key[] key: c.key.into_iter().map(|i| i.into()).collect(), // Rule #3 for type string string: c.string, // Rule #2 for type string? } @@ -4799,37 +4799,62 @@ impl From for pb::ConnectRequest { } } +impl From for pb::createinvoice_request::Label { + fn from(c: requests::CreateinvoiceLabel) -> Self { + match c { +requests::CreateinvoiceLabel::String(v) => pb::createinvoice_request::Label::LabelString(v), +requests::CreateinvoiceLabel::Int(v) => pb::createinvoice_request::Label::LabelInt(v), + } + } +} + #[allow(unused_variables)] impl From for pb::CreateinvoiceRequest { fn from(c: requests::CreateinvoiceRequest) -> Self { Self { invstring: c.invstring, // Rule #2 for type string - label: c.label, // Rule #2 for type string + label: Some(c.label.into()), preimage: hex::decode(&c.preimage).unwrap(), // Rule #2 for type hex } } } +impl From for pb::datastore_request::Key { + fn from(c: requests::DatastoreKey) -> Self { + match c { +requests::DatastoreKey::ArrayOfString(v) => pb::datastore_request::Key::KeyArrString(pb::DatastoreRequestarrStringWrapper { items: v.into_iter().map(|i| i).collect() }), +requests::DatastoreKey::String(v) => pb::datastore_request::Key::KeyString(v), + } + } +} + #[allow(unused_variables)] impl From for pb::DatastoreRequest { fn from(c: requests::DatastoreRequest) -> Self { Self { generation: c.generation, // Rule #2 for type u64? hex: c.hex.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? - // Field: Datastore.key - key: c.key.into_iter().map(|i| i.into()).collect(), // Rule #3 for type string + key: Some(c.key.into()), mode: c.mode.map(|v| v as i32), string: c.string, // Rule #2 for type string? } } } +impl From for pb::datastoreusage_request::Key { + fn from(c: requests::DatastoreusageKey) -> Self { + match c { +requests::DatastoreusageKey::ArrayOfString(v) => pb::datastoreusage_request::Key::KeyArrString(pb::DatastoreusageRequestarrStringWrapper { items: v.into_iter().map(|i| i).collect() }), +requests::DatastoreusageKey::String(v) => pb::datastoreusage_request::Key::KeyString(v), + } + } +} + #[allow(unused_variables)] impl From for pb::DatastoreusageRequest { fn from(c: requests::DatastoreusageRequest) -> Self { Self { - // Field: DatastoreUsage.key - key: c.key.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + key: c.key.map(|v| v.into()), } } } @@ -4857,13 +4882,30 @@ impl From for pb::CreateonionRequest { } } +impl From for pb::deldatastore_request::Key { + fn from(c: requests::DeldatastoreKey) -> Self { + match c { +requests::DeldatastoreKey::ArrayOfString(v) => pb::deldatastore_request::Key::KeyArrString(pb::DeldatastoreRequestarrStringWrapper { items: v.into_iter().map(|i| i).collect() }), +requests::DeldatastoreKey::String(v) => pb::deldatastore_request::Key::KeyString(v), + } + } +} + #[allow(unused_variables)] impl From for pb::DeldatastoreRequest { fn from(c: requests::DeldatastoreRequest) -> Self { Self { generation: c.generation, // Rule #2 for type u64? - // Field: DelDatastore.key - key: c.key.into_iter().map(|i| i.into()).collect(), // Rule #3 for type string + key: Some(c.key.into()), + } + } +} + +impl From for pb::delinvoice_request::Label { + fn from(c: requests::DelinvoiceLabel) -> Self { + match c { +requests::DelinvoiceLabel::String(v) => pb::delinvoice_request::Label::LabelString(v), +requests::DelinvoiceLabel::U64(v) => pb::delinvoice_request::Label::LabelU64(v), } } } @@ -4873,7 +4915,7 @@ impl From for pb::DelinvoiceRequest { fn from(c: requests::DelinvoiceRequest) -> Self { Self { desconly: c.desconly, // Rule #2 for type boolean? - label: c.label, // Rule #2 for type string + label: Some(c.label.into()), status: c.status as i32, } } @@ -4936,6 +4978,25 @@ impl From for pb::RecoverchannelRequest { } } +impl From for pb::invoice_request::Exposeprivatechannels { + fn from(c: requests::InvoiceExposeprivatechannels) -> Self { + match c { +requests::InvoiceExposeprivatechannels::Bool(v) => pb::invoice_request::Exposeprivatechannels::ExposeprivatechannelsBool(v), +requests::InvoiceExposeprivatechannels::ArrayOfShortChannelId(v) => pb::invoice_request::Exposeprivatechannels::ExposeprivatechannelsArrScid(pb::InvoiceRequestarrScidWrapper { items: v.into_iter().map(|i| i.to_string()).collect() }), +requests::InvoiceExposeprivatechannels::ShortChannelId(v) => pb::invoice_request::Exposeprivatechannels::ExposeprivatechannelsScid(v.to_string()), + } + } +} + +impl From for pb::invoice_request::Label { + fn from(c: requests::InvoiceLabel) -> Self { + match c { +requests::InvoiceLabel::String(v) => pb::invoice_request::Label::LabelString(v), +requests::InvoiceLabel::Int(v) => pb::invoice_request::Label::LabelInt(v), + } + } +} + #[allow(unused_variables)] impl From for pb::InvoiceRequest { fn from(c: requests::InvoiceRequest) -> Self { @@ -4945,11 +5006,10 @@ impl From for pb::InvoiceRequest { deschashonly: c.deschashonly, // Rule #2 for type boolean? description: c.description, // Rule #2 for type string expiry: c.expiry, // Rule #2 for type u64? - // Field: Invoice.exposeprivatechannels - exposeprivatechannels: c.exposeprivatechannels.map(|arr| arr.into_iter().map(|i| i.to_string()).collect()).unwrap_or(vec![]), // Rule #3 + exposeprivatechannels: c.exposeprivatechannels.map(|v| v.into()), // Field: Invoice.fallbacks[] fallbacks: c.fallbacks.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - label: c.label, // Rule #2 for type string + label: Some(c.label.into()), preimage: c.preimage.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? } } @@ -4988,12 +5048,29 @@ impl From for pb::ListinvoicerequestsReque } } +impl From for pb::listdatastore_request::Key { + fn from(c: requests::ListdatastoreKey) -> Self { + match c { +requests::ListdatastoreKey::ArrayOfString(v) => pb::listdatastore_request::Key::KeyArrString(pb::ListdatastoreRequestarrStringWrapper { items: v.into_iter().map(|i| i).collect() }), +requests::ListdatastoreKey::String(v) => pb::listdatastore_request::Key::KeyString(v), + } + } +} + #[allow(unused_variables)] impl From for pb::ListdatastoreRequest { fn from(c: requests::ListdatastoreRequest) -> Self { Self { - // Field: ListDatastore.key - key: c.key.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + key: c.key.map(|v| v.into()), + } + } +} + +impl From for pb::listinvoices_request::Label { + fn from(c: requests::ListinvoicesLabel) -> Self { + match c { +requests::ListinvoicesLabel::String(v) => pb::listinvoices_request::Label::LabelString(v), +requests::ListinvoicesLabel::Int(v) => pb::listinvoices_request::Label::LabelInt(v), } } } @@ -5004,7 +5081,7 @@ impl From for pb::ListinvoicesRequest { Self { index: c.index.map(|v| v as i32), invstring: c.invstring, // Rule #2 for type string? - label: c.label, // Rule #2 for type string? + label: c.label.map(|v| v.into()), limit: c.limit, // Rule #2 for type u32? offer_id: c.offer_id, // Rule #2 for type string? payment_hash: c.payment_hash.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? @@ -5119,11 +5196,20 @@ impl From for pb::WaitanyinvoiceRequest { } } +impl From for pb::waitinvoice_request::Label { + fn from(c: requests::WaitinvoiceLabel) -> Self { + match c { +requests::WaitinvoiceLabel::String(v) => pb::waitinvoice_request::Label::LabelString(v), +requests::WaitinvoiceLabel::Int(v) => pb::waitinvoice_request::Label::LabelInt(v), + } + } +} + #[allow(unused_variables)] impl From for pb::WaitinvoiceRequest { fn from(c: requests::WaitinvoiceRequest) -> Self { Self { - label: c.label, // Rule #2 for type string + label: Some(c.label.into()), } } } @@ -5620,12 +5706,21 @@ impl From for pb::MultiwithdrawRequest { } } +impl From for pb::offer_request::Amount { + fn from(c: requests::OfferAmount) -> Self { + match c { +requests::OfferAmount::MsatOrAny(v) => pb::offer_request::Amount::AmountMsatOrAny(v.into()), +requests::OfferAmount::Currency(v) => pb::offer_request::Amount::AmountCurrency(v), + } + } +} + #[allow(unused_variables)] impl From for pb::OfferRequest { fn from(c: requests::OfferRequest) -> Self { Self { absolute_expiry: c.absolute_expiry, // Rule #2 for type u64? - amount: c.amount, // Rule #2 for type string + amount: Some(c.amount.into()), description: c.description, // Rule #2 for type string? // Field: Offer.fronting_nodes[] fronting_nodes: c.fronting_nodes.map(|arr| arr.into_iter().map(|i| i.serialize().to_vec()).collect()).unwrap_or(vec![]), // Rule #3 @@ -5803,13 +5898,23 @@ impl From for pb::SetchannelRequest { } } +impl From for pb::setconfig_request::Val { + fn from(c: requests::SetconfigVal) -> Self { + match c { +requests::SetconfigVal::String(v) => pb::setconfig_request::Val::ValString(v), +requests::SetconfigVal::Int(v) => pb::setconfig_request::Val::ValInt(v), +requests::SetconfigVal::Bool(v) => pb::setconfig_request::Val::ValBool(v), + } + } +} + #[allow(unused_variables)] impl From for pb::SetconfigRequest { fn from(c: requests::SetconfigRequest) -> Self { Self { config: c.config, // Rule #2 for type string transient: c.transient, // Rule #2 for type boolean? - val: c.val, // Rule #2 for type string? + val: c.val.map(|v| v.into()), } } } @@ -6610,35 +6715,62 @@ impl From for requests::ConnectRequest { } } +impl From for requests::CreateinvoiceLabel { + fn from(c: pb::createinvoice_request::Label) -> Self { + match c { +pb::createinvoice_request::Label::LabelString(v) => requests::CreateinvoiceLabel::String(v), +pb::createinvoice_request::Label::LabelInt(v) => requests::CreateinvoiceLabel::Int(v), + } + } +} + #[allow(unused_variables)] impl From for requests::CreateinvoiceRequest { fn from(c: pb::CreateinvoiceRequest) -> Self { Self { invstring: c.invstring, // Rule #1 for type string - label: c.label, // Rule #1 for type string + label: c.label.unwrap().into(), preimage: hex::encode(&c.preimage), // Rule #1 for type hex } } } +impl From for requests::DatastoreKey { + fn from(c: pb::datastore_request::Key) -> Self { + match c { +pb::datastore_request::Key::KeyArrString(v) => requests::DatastoreKey::ArrayOfString(v.items.into_iter().map(|s| s.into()).collect()), +pb::datastore_request::Key::KeyString(v) => requests::DatastoreKey::String(v), + } + } +} + #[allow(unused_variables)] impl From for requests::DatastoreRequest { fn from(c: pb::DatastoreRequest) -> Self { Self { generation: c.generation, // Rule #1 for type u64? hex: c.hex.map(|v| hex::encode(v)), // Rule #1 for type hex? - key: c.key.into_iter().map(|s| s.into()).collect(), // Rule #4 + key: c.key.unwrap().into(), mode: c.mode.map(|v| v.try_into().unwrap()), string: c.string, // Rule #1 for type string? } } } +impl From for requests::DatastoreusageKey { + fn from(c: pb::datastoreusage_request::Key) -> Self { + match c { +pb::datastoreusage_request::Key::KeyArrString(v) => requests::DatastoreusageKey::ArrayOfString(v.items.into_iter().map(|s| s.into()).collect()), +pb::datastoreusage_request::Key::KeyString(v) => requests::DatastoreusageKey::String(v), + } + } +} + #[allow(unused_variables)] impl From for requests::DatastoreusageRequest { fn from(c: pb::DatastoreusageRequest) -> Self { Self { - key: Some(c.key.into_iter().map(|s| s.into()).collect()), // Rule #4 + key: c.key.map(|v| v.into()), } } } @@ -6665,12 +6797,30 @@ impl From for requests::CreateonionRequest { } } +impl From for requests::DeldatastoreKey { + fn from(c: pb::deldatastore_request::Key) -> Self { + match c { +pb::deldatastore_request::Key::KeyArrString(v) => requests::DeldatastoreKey::ArrayOfString(v.items.into_iter().map(|s| s.into()).collect()), +pb::deldatastore_request::Key::KeyString(v) => requests::DeldatastoreKey::String(v), + } + } +} + #[allow(unused_variables)] impl From for requests::DeldatastoreRequest { fn from(c: pb::DeldatastoreRequest) -> Self { Self { generation: c.generation, // Rule #1 for type u64? - key: c.key.into_iter().map(|s| s.into()).collect(), // Rule #4 + key: c.key.unwrap().into(), + } + } +} + +impl From for requests::DelinvoiceLabel { + fn from(c: pb::delinvoice_request::Label) -> Self { + match c { +pb::delinvoice_request::Label::LabelString(v) => requests::DelinvoiceLabel::String(v), +pb::delinvoice_request::Label::LabelU64(v) => requests::DelinvoiceLabel::U64(v), } } } @@ -6680,7 +6830,7 @@ impl From for requests::DelinvoiceRequest { fn from(c: pb::DelinvoiceRequest) -> Self { Self { desconly: c.desconly, // Rule #1 for type boolean? - label: c.label, // Rule #1 for type string + label: c.label.unwrap().into(), status: c.status.try_into().unwrap(), } } @@ -6742,6 +6892,25 @@ impl From for requests::RecoverchannelRequest { } } +impl From for requests::InvoiceExposeprivatechannels { + fn from(c: pb::invoice_request::Exposeprivatechannels) -> Self { + match c { +pb::invoice_request::Exposeprivatechannels::ExposeprivatechannelsBool(v) => requests::InvoiceExposeprivatechannels::Bool(v), +pb::invoice_request::Exposeprivatechannels::ExposeprivatechannelsArrScid(v) => requests::InvoiceExposeprivatechannels::ArrayOfShortChannelId(v.items.into_iter().map(|s| cln_rpc::primitives::ShortChannelId::from_str(&s).unwrap()).collect()), +pb::invoice_request::Exposeprivatechannels::ExposeprivatechannelsScid(v) => requests::InvoiceExposeprivatechannels::ShortChannelId(cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), + } + } +} + +impl From for requests::InvoiceLabel { + fn from(c: pb::invoice_request::Label) -> Self { + match c { +pb::invoice_request::Label::LabelString(v) => requests::InvoiceLabel::String(v), +pb::invoice_request::Label::LabelInt(v) => requests::InvoiceLabel::Int(v), + } + } +} + #[allow(unused_variables)] impl From for requests::InvoiceRequest { fn from(c: pb::InvoiceRequest) -> Self { @@ -6751,9 +6920,9 @@ impl From for requests::InvoiceRequest { deschashonly: c.deschashonly, // Rule #1 for type boolean? description: c.description, // Rule #1 for type string expiry: c.expiry, // Rule #1 for type u64? - exposeprivatechannels: Some(c.exposeprivatechannels.into_iter().map(|s| cln_rpc::primitives::ShortChannelId::from_str(&s).unwrap()).collect()), // Rule #4 + exposeprivatechannels: c.exposeprivatechannels.map(|v| v.into()), fallbacks: Some(c.fallbacks.into_iter().map(|s| s.into()).collect()), // Rule #4 - label: c.label, // Rule #1 for type string + label: c.label.unwrap().into(), preimage: c.preimage.map(|v| hex::encode(v)), // Rule #1 for type hex? } } @@ -6792,11 +6961,29 @@ impl From for requests::ListinvoicerequestsReque } } +impl From for requests::ListdatastoreKey { + fn from(c: pb::listdatastore_request::Key) -> Self { + match c { +pb::listdatastore_request::Key::KeyArrString(v) => requests::ListdatastoreKey::ArrayOfString(v.items.into_iter().map(|s| s.into()).collect()), +pb::listdatastore_request::Key::KeyString(v) => requests::ListdatastoreKey::String(v), + } + } +} + #[allow(unused_variables)] impl From for requests::ListdatastoreRequest { fn from(c: pb::ListdatastoreRequest) -> Self { Self { - key: Some(c.key.into_iter().map(|s| s.into()).collect()), // Rule #4 + key: c.key.map(|v| v.into()), + } + } +} + +impl From for requests::ListinvoicesLabel { + fn from(c: pb::listinvoices_request::Label) -> Self { + match c { +pb::listinvoices_request::Label::LabelString(v) => requests::ListinvoicesLabel::String(v), +pb::listinvoices_request::Label::LabelInt(v) => requests::ListinvoicesLabel::Int(v), } } } @@ -6807,7 +6994,7 @@ impl From for requests::ListinvoicesRequest { Self { index: c.index.map(|v| v.try_into().unwrap()), invstring: c.invstring, // Rule #1 for type string? - label: c.label, // Rule #1 for type string? + label: c.label.map(|v| v.into()), limit: c.limit, // Rule #1 for type u32? offer_id: c.offer_id, // Rule #1 for type string? payment_hash: c.payment_hash.map(|v| hex::encode(v)), // Rule #1 for type hex? @@ -6920,11 +7107,20 @@ impl From for requests::WaitanyinvoiceRequest { } } +impl From for requests::WaitinvoiceLabel { + fn from(c: pb::waitinvoice_request::Label) -> Self { + match c { +pb::waitinvoice_request::Label::LabelString(v) => requests::WaitinvoiceLabel::String(v), +pb::waitinvoice_request::Label::LabelInt(v) => requests::WaitinvoiceLabel::Int(v), + } + } +} + #[allow(unused_variables)] impl From for requests::WaitinvoiceRequest { fn from(c: pb::WaitinvoiceRequest) -> Self { Self { - label: c.label, // Rule #1 for type string + label: c.label.unwrap().into(), } } } @@ -7408,12 +7604,21 @@ impl From for requests::MultiwithdrawRequest { } } +impl From for requests::OfferAmount { + fn from(c: pb::offer_request::Amount) -> Self { + match c { +pb::offer_request::Amount::AmountMsatOrAny(v) => requests::OfferAmount::MsatOrAny(v.into()), +pb::offer_request::Amount::AmountCurrency(v) => requests::OfferAmount::Currency(v), + } + } +} + #[allow(unused_variables)] impl From for requests::OfferRequest { fn from(c: pb::OfferRequest) -> Self { Self { absolute_expiry: c.absolute_expiry, // Rule #1 for type u64? - amount: c.amount, // Rule #1 for type string + amount: c.amount.unwrap().into(), description: c.description, // Rule #1 for type string? fronting_nodes: Some(c.fronting_nodes.into_iter().map(|s| PublicKey::from_slice(&s).unwrap()).collect()), // Rule #4 issuer: c.issuer, // Rule #1 for type string? @@ -7587,13 +7792,23 @@ impl From for requests::SetchannelRequest { } } +impl From for requests::SetconfigVal { + fn from(c: pb::setconfig_request::Val) -> Self { + match c { +pb::setconfig_request::Val::ValString(v) => requests::SetconfigVal::String(v), +pb::setconfig_request::Val::ValInt(v) => requests::SetconfigVal::Int(v), +pb::setconfig_request::Val::ValBool(v) => requests::SetconfigVal::Bool(v), + } + } +} + #[allow(unused_variables)] impl From for requests::SetconfigRequest { fn from(c: pb::SetconfigRequest) -> Self { Self { config: c.config, // Rule #1 for type string transient: c.transient, // Rule #1 for type boolean? - val: c.val, // Rule #1 for type string? + val: c.val.map(|v| v.into()), } } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 6e666cdf47bf..ea061624c0f0 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -2,7 +2,7 @@ // This file was automatically generated using the following command: // // ```bash -// contrib/msggen/msggen/__main__.py -s generate +// -c // ``` // // Do not edit this file, it'll be overwritten. Rather edit the schema that @@ -773,10 +773,17 @@ pub mod requests { "connect" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum CreateinvoiceLabel { + String(String), + Int(i64), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreateinvoiceRequest { pub invstring: String, - pub label: String, + pub label: CreateinvoiceLabel, pub preimage: String, } @@ -839,6 +846,13 @@ pub mod requests { } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum DatastoreKey { + ArrayOfString(Vec), + String(String), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DatastoreRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -849,7 +863,7 @@ pub mod requests { pub mode: Option, #[serde(skip_serializing_if = "Option::is_none")] pub string: Option, - pub key: Vec, + pub key: DatastoreKey, } impl From for Request { @@ -869,10 +883,17 @@ pub mod requests { "datastore" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum DatastoreusageKey { + ArrayOfString(Vec), + String(String), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DatastoreusageRequest { - #[serde(skip_serializing_if = "crate::is_none_or_empty")] - pub key: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub key: Option, } impl From for Request { @@ -925,11 +946,18 @@ pub mod requests { "createonion" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum DeldatastoreKey { + ArrayOfString(Vec), + String(String), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DeldatastoreRequest { #[serde(skip_serializing_if = "Option::is_none")] pub generation: Option, - pub key: Vec, + pub key: DeldatastoreKey, } impl From for Request { @@ -983,13 +1011,20 @@ pub mod requests { } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum DelinvoiceLabel { + String(String), + U64(u64), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DelinvoiceRequest { #[serde(skip_serializing_if = "Option::is_none")] pub desconly: Option, // Path `DelInvoice.status` pub status: DelinvoiceStatus, - pub label: String, + pub label: DelinvoiceLabel, } impl From for Request { @@ -1147,6 +1182,21 @@ pub mod requests { "recoverchannel" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum InvoiceExposeprivatechannels { + Bool(bool), + ArrayOfShortChannelId(Vec), + ShortChannelId(ShortChannelId), + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum InvoiceLabel { + String(String), + Int(i64), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct InvoiceRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -1156,14 +1206,14 @@ pub mod requests { #[serde(skip_serializing_if = "Option::is_none")] pub expiry: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub exposeprivatechannels: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub preimage: Option, #[serde(skip_serializing_if = "crate::is_none_or_empty")] - pub exposeprivatechannels: Option>, - #[serde(skip_serializing_if = "crate::is_none_or_empty")] pub fallbacks: Option>, pub amount_msat: AmountOrAny, pub description: String, - pub label: String, + pub label: InvoiceLabel, } impl From for Request { @@ -1261,10 +1311,17 @@ pub mod requests { "listinvoicerequests" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum ListdatastoreKey { + ArrayOfString(Vec), + String(String), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListdatastoreRequest { - #[serde(skip_serializing_if = "crate::is_none_or_empty")] - pub key: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub key: Option, } impl From for Request { @@ -1314,6 +1371,13 @@ pub mod requests { } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum ListinvoicesLabel { + String(String), + Int(i64), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListinvoicesRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -1321,7 +1385,7 @@ pub mod requests { #[serde(skip_serializing_if = "Option::is_none")] pub invstring: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub label: Option, + pub label: Option, #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -1637,9 +1701,16 @@ pub mod requests { "waitanyinvoice" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum WaitinvoiceLabel { + String(String), + Int(i64), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitinvoiceRequest { - pub label: String, + pub label: WaitinvoiceLabel, } impl From for Request { @@ -3117,6 +3188,13 @@ pub mod requests { "multiwithdraw" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum OfferAmount { + MsatOrAny(AmountOrAny), + Currency(String), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct OfferRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -3145,7 +3223,7 @@ pub mod requests { pub single_use: Option, #[serde(skip_serializing_if = "crate::is_none_or_empty")] pub fronting_nodes: Option>, - pub amount: String, + pub amount: OfferAmount, } impl From for Request { @@ -3525,12 +3603,20 @@ pub mod requests { "setchannel" } } + #[derive(Clone, Debug, Deserialize, Serialize)] + #[serde(untagged)] + pub enum SetconfigVal { + String(String), + Int(i64), + Bool(bool), + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SetconfigRequest { #[serde(skip_serializing_if = "Option::is_none")] pub transient: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub val: Option, + pub val: Option, pub config: String, } diff --git a/contrib/msggen/msggen/checks.py b/contrib/msggen/msggen/checks.py index 26818370bd30..934ba776ed13 100644 --- a/contrib/msggen/msggen/checks.py +++ b/contrib/msggen/msggen/checks.py @@ -15,6 +15,10 @@ def recurse(f: model.Field): if isinstance(f, model.ArrayField): self.visit(f.itemtype) recurse(f.itemtype) + elif isinstance(f, model.UnionField): + for v in f.variants: + self.visit(v) + recurse(v) elif isinstance(f, model.CompositeField): for c in f.fields: self.visit(c) diff --git a/contrib/msggen/msggen/gen/grpc/convert.py b/contrib/msggen/msggen/gen/grpc/convert.py index a65c447db4fc..e61f64d104f0 100644 --- a/contrib/msggen/msggen/gen/grpc/convert.py +++ b/contrib/msggen/msggen/gen/grpc/convert.py @@ -1,6 +1,7 @@ # A grpc model -from msggen.model import ArrayField, CompositeField, EnumField, PrimitiveField, Service -from msggen.gen.grpc.util import notification_typename_overrides +from msggen.model import ArrayField, CompositeField, EnumField, PrimitiveField, UnionField, Service +from msggen.gen.grpc.util import notification_typename_overrides, camel_to_snake, union_variant_suffix +from msggen.gen.rpc.rust import union_variant_name from msggen.gen import IGenerator from typing import TextIO from textwrap import indent, dedent @@ -17,6 +18,86 @@ def generate_array(self, prefix, field: ArrayField, override): if isinstance(field.itemtype, CompositeField): self.generate_composite(prefix, field.itemtype, override) + def union_variant_conversion(self, f, val="v"): + """Generate the conversion expression for a single union variant value.""" + if isinstance(f, PrimitiveField): + mapping = { + "short_channel_id": f"{val}.to_string()", + "short_channel_id_dir": f"{val}.to_string()", + "pubkey": f"{val}.serialize().to_vec()", + "hex": f"hex::decode({val}).unwrap()", + "txid": f"hex::decode({val}).unwrap()", + "hash": f">::as_ref(&{val}).to_vec()", + "secret": f"{val}.to_vec()", + "msat": f"{val}.into()", + "msat_or_all": f"{val}.into()", + "msat_or_any": f"{val}.into()", + "sat": f"{val}.into()", + "sat_or_all": f"{val}.into()", + "feerate": f"{val}.into()", + "outpoint": f"{val}.into()", + }.get(f.typename, val) + return mapping + elif isinstance(f, ArrayField): + inner_mapping = { + "short_channel_id": "i.to_string()", + "short_channel_id_dir": "i.to_string()", + "pubkey": "i.serialize().to_vec()", + "hex": "hex::decode(i).unwrap()", + "txid": "hex::decode(i).unwrap()", + }.get(f.itemtype.typename, "i.into()") + return f"{val}.into_iter().map(|i| {inner_mapping}).collect()" + elif isinstance(f, EnumField): + return f"{val} as i32" + elif isinstance(f, CompositeField): + return f"{val}.into()" + return val + + def generate_union(self, prefix, field: UnionField, parent_typename, override=None): + """Generate From impl for a union type (cln-rpc enum -> pb oneof).""" + if override is None: + override = lambda x: x + + typename = str(field.typename) + pbname = override(self.to_camel_case(str(override(parent_typename)))) + pb_mod = camel_to_snake(pbname) + oneof_name = field.normalized() + # The prost enum name is CamelCase of the oneof field name + pb_oneof_enum = self.to_camel_case(oneof_name[0].upper() + oneof_name[1:]) + + self.write( + f"""\ + impl From<{prefix}::{typename}> for pb::{pb_mod}::{pb_oneof_enum} {{ + fn from(c: {prefix}::{typename}) -> Self {{ + match c {{ + """ + ) + + for v in field.variants: + vname = union_variant_name(v) + suffix = union_variant_suffix(v) + pb_variant = self.to_camel_case(f"{oneof_name}_{suffix}") + pb_variant = pb_variant[0].upper() + pb_variant[1:] + if isinstance(v, ArrayField): + wrapper_name = override(f"{parent_typename}{suffix}Wrapper") + wrapper_pb = self.to_camel_case(str(wrapper_name)) + self.write( + f" {prefix}::{typename}::{vname}(v) => pb::{pb_mod}::{pb_oneof_enum}::{pb_variant}(pb::{wrapper_pb} {{ items: v.into_iter().map(|i| {self.union_variant_conversion(v.itemtype, 'i')}).collect() }}),\n" + ) + else: + self.write( + f" {prefix}::{typename}::{vname}(v) => pb::{pb_mod}::{pb_oneof_enum}::{pb_variant}({self.union_variant_conversion(v)}),\n" + ) + + self.write( + f"""\ + }} + }} + }} + + """ + ) + def generate_composite(self, prefix, field: CompositeField, override=None): """Generates the conversions from JSON-RPC to GRPC.""" if field.omit(): @@ -34,6 +115,8 @@ def generate_composite(self, prefix, field: CompositeField, override=None): self.generate_array(prefix, f, override) elif isinstance(f, CompositeField): self.generate_composite(prefix, f, override) + elif isinstance(f, UnionField): + self.generate_union(prefix, f, str(field.typename), override) pbname = override(self.to_camel_case(str(override(field.typename)))) @@ -148,6 +231,13 @@ def generate_composite(self, prefix, field: CompositeField, override=None): else: rhs = f"c.{name}.map(|v| v.into())" self.write(f"{name}: {rhs},\n", numindent=3) + + elif isinstance(f, UnionField): + if not f.optional: + self.write(f"{name}: Some(c.{name}.into()),\n", numindent=3) + else: + self.write(f"{name}: c.{name}.map(|v| v.into()),\n", numindent=3) + self.write( f"""\ }} diff --git a/contrib/msggen/msggen/gen/grpc/proto.py b/contrib/msggen/msggen/gen/grpc/proto.py index 3f2b981ab644..730295feff43 100644 --- a/contrib/msggen/msggen/gen/grpc/proto.py +++ b/contrib/msggen/msggen/gen/grpc/proto.py @@ -8,6 +8,7 @@ typemap, method_name_overrides, notification_typename_overrides, + union_variant_suffix, ) from msggen.model import ( ArrayField, @@ -15,6 +16,7 @@ CompositeField, EnumField, PrimitiveField, + UnionField, Service, MethodName, TypeName, @@ -177,6 +179,32 @@ def generate_enum(self, e: EnumField, indent=0, typename_override=None): self.write(f"""{prefix}}}\n""", False) + def union_variant_proto_type(self, f, parent_typename): + """Return the protobuf type name for a union variant field.""" + if isinstance(f, PrimitiveField): + return typemap.get(f.typename, f.typename) + elif isinstance(f, EnumField): + return str(f.typename) + elif isinstance(f, CompositeField): + return str(f.typename) + elif isinstance(f, ArrayField): + # oneof cannot contain repeated, so we need a wrapper message + return f"{parent_typename}{union_variant_suffix(f)}Wrapper" + return "bytes" + + def generate_union_wrapper_messages(self, u: UnionField, parent_typename, typename_override=None): + """Generate wrapper messages for array variants inside a union (oneof can't contain repeated).""" + if typename_override is None: + typename_override = lambda x: x + + for v in u.variants: + if isinstance(v, ArrayField): + wrapper_name = f"{parent_typename}{union_variant_suffix(v)}Wrapper" + item_type = typemap.get(v.itemtype.typename, v.itemtype.typename) + self.write(f"\nmessage {typename_override(wrapper_name)} {{\n", False) + self.write(f"\trepeated {item_type} items = 1;\n", False) + self.write(f"}}\n", False) + def generate_message(self, message: CompositeField, typename_override=None): if message.omit(): return @@ -186,6 +214,11 @@ def generate_message(self, message: CompositeField, typename_override=None): if typename_override is None: typename_override = lambda x: x + # Generate wrapper messages for any union fields first + for f in message.fields: + if isinstance(f, UnionField): + self.generate_union_wrapper_messages(f, str(message.typename), typename_override) + self.write( f""" message {typename_override(message.typename)} {{ @@ -203,7 +236,26 @@ def generate_message(self, message: CompositeField, typename_override=None): opt = "optional " if f.optional and not (isinstance(f, PrimitiveField) and f.typename == "string_map") else "" - if isinstance(f, ArrayField): + if isinstance(f, UnionField): + self.write(f"\toneof {f.normalized()} {{\n", False) + first_variant = True + for v in f.variants: + suffix = union_variant_suffix(v) + vname = f"{f.normalized()}_{suffix}" + vtype = self.union_variant_proto_type(v, str(message.typename)) + vtype = typename_override(vtype) + if first_variant: + # Reuse the original field number for backward compat + vnum = i + first_variant = False + else: + # Allocate new numbers for additional variants + parent = ".".join(f.path.split(".")[:-1]) + vfield = PrimitiveField(suffix, f"{parent}.{f.normalized()}_{suffix}", "", added=f.added, deprecated=f.deprecated) + vnum = self.field2number(message.typename, vfield) + self.write(f"\t\t{vtype} {vname} = {vnum};\n", False) + self.write(f"\t}}\n", False) + elif isinstance(f, ArrayField): typename = f.override( typemap.get(f.itemtype.typename, f.itemtype.typename) ) @@ -264,5 +316,8 @@ def gather_subfields(field: Field) -> List[Field]: elif isinstance(field, ArrayField): fields = [] fields.extend(gather_subfields(field.itemtype)) + elif isinstance(field, UnionField): + for v in field.variants: + fields.extend(gather_subfields(v)) return fields diff --git a/contrib/msggen/msggen/gen/grpc/unconvert.py b/contrib/msggen/msggen/gen/grpc/unconvert.py index 73984f75a47a..4c7a34abda23 100644 --- a/contrib/msggen/msggen/gen/grpc/unconvert.py +++ b/contrib/msggen/msggen/gen/grpc/unconvert.py @@ -1,6 +1,8 @@ # A grpc model -from msggen.model import ArrayField, CompositeField, EnumField, PrimitiveField, Service +from msggen.model import ArrayField, CompositeField, EnumField, PrimitiveField, UnionField, Service from msggen.gen.grpc.convert import GrpcConverterGenerator +from msggen.gen.grpc.util import camel_to_snake, union_variant_suffix +from msggen.gen.rpc.rust import union_variant_name class GrpcUnconverterGenerator(GrpcConverterGenerator): @@ -12,6 +14,79 @@ def generate(self, service: Service): # TODO Temporarily disabled since the use of overrides is lossy # self.generate_responses(service) + def unconvert_variant_value(self, f, val="v"): + """Generate the reverse conversion expression for a union variant value (pb -> cln-rpc).""" + if isinstance(f, PrimitiveField): + return { + "short_channel_id": f"cln_rpc::primitives::ShortChannelId::from_str(&{val}).unwrap()", + "short_channel_id_dir": f"cln_rpc::primitives::ShortChannelIdDir::from_str(&{val}).unwrap()", + "pubkey": f"PublicKey::from_slice(&{val}).unwrap()", + "hex": f"hex::encode({val})", + "txid": f"hex::encode({val})", + "hash": f"Sha256::from_slice(&{val}).unwrap()", + "secret": f"{val}.try_into().unwrap()", + "msat": f"{val}.into()", + "msat_or_all": f"{val}.into()", + "msat_or_any": f"{val}.into()", + "sat": f"{val}.into()", + "sat_or_all": f"{val}.into()", + "feerate": f"{val}.into()", + "outpoint": f"{val}.into()", + }.get(f.typename, val) + elif isinstance(f, ArrayField): + inner_mapping = { + "short_channel_id": "cln_rpc::primitives::ShortChannelId::from_str(&s).unwrap()", + "short_channel_id_dir": "cln_rpc::primitives::ShortChannelIdDir::from_str(&s).unwrap()", + "pubkey": "PublicKey::from_slice(&s).unwrap()", + "hex": "hex::encode(s)", + "txid": "hex::encode(s)", + }.get(f.itemtype.typename, "s.into()") + return f"{val}.items.into_iter().map(|s| {inner_mapping}).collect()" + elif isinstance(f, EnumField): + return f"{val}.try_into().unwrap()" + elif isinstance(f, CompositeField): + return f"{val}.into()" + return val + + def generate_union_unconvert(self, prefix, field: UnionField, parent_typename, override=None): + """Generate From impl for pb oneof -> cln-rpc union type.""" + if override is None: + override = lambda x: x + + typename = str(field.typename) + pbname = self.to_camel_case(str(parent_typename)) + pb_mod = camel_to_snake(pbname) + oneof_name = field.normalized() + pb_oneof_enum = self.to_camel_case(oneof_name[0].upper() + oneof_name[1:]) + + self.write( + f"""\ + impl From for {prefix}::{typename} {{ + fn from(c: pb::{pb_mod}::{pb_oneof_enum}) -> Self {{ + match c {{ + """ + ) + + for v in field.variants: + vname = union_variant_name(v) + suffix = union_variant_suffix(v) + pb_variant = self.to_camel_case(f"{oneof_name}_{suffix}") + pb_variant = pb_variant[0].upper() + pb_variant[1:] + conv = self.unconvert_variant_value(v) + + self.write( + f" pb::{pb_mod}::{pb_oneof_enum}::{pb_variant}(v) => {prefix}::{typename}::{vname}({conv}),\n" + ) + + self.write( + f"""\ + }} + }} + }} + + """ + ) + def generate_composite(self, prefix, field: CompositeField, override=None) -> None: # First pass: generate any sub-fields before we generate the # top-level field itself. @@ -26,6 +101,8 @@ def generate_composite(self, prefix, field: CompositeField, override=None) -> No self.generate_array(prefix, f, override) elif isinstance(f, CompositeField): self.generate_composite(prefix, f, override) + elif isinstance(f, UnionField): + self.generate_union_unconvert(prefix, f, str(field.typename), override) has_deprecated = any([f.deprecated for f in field.fields]) deprecated = ",deprecated" if has_deprecated else "" @@ -146,6 +223,12 @@ def generate_composite(self, prefix, field: CompositeField, override=None) -> No rhs = f"c.{name}.map(|v| v.into())" self.write(f"{name}: {rhs},\n", numindent=3) + elif isinstance(f, UnionField): + if not f.optional: + self.write(f"{name}: c.{name}.unwrap().into(),\n", numindent=3) + else: + self.write(f"{name}: c.{name}.map(|v| v.into()),\n", numindent=3) + self.write( f"""\ }} diff --git a/contrib/msggen/msggen/gen/grpc/util.py b/contrib/msggen/msggen/gen/grpc/util.py index 267691d59be6..0334dee5534c 100644 --- a/contrib/msggen/msggen/gen/grpc/util.py +++ b/contrib/msggen/msggen/gen/grpc/util.py @@ -78,3 +78,36 @@ def camel_to_snake(camel_case: str): snake = re.sub(r"(?'*field.dims}" + elif isinstance(field, EnumField): + return str(field.typename) + elif isinstance(field, CompositeField): + return str(field.typename) + else: + return "serde_json::Value" + + +def gen_union(u, meta): + defi, decl = "", "" + if u.omit(): + return "", "" + + typename = str(u.typename) + + # Generate sub-type declarations for composite/enum variants + for v in u.variants: + if isinstance(v, CompositeField): + _, vdecl = gen_composite(v, meta) + decl += vdecl + elif isinstance(v, EnumField): + _, vdecl = gen_enum(v, meta, None) + decl += vdecl + + decl += f"#[derive(Clone, Debug, Deserialize, Serialize)]\n" + decl += f"#[serde(untagged)]\n" + decl += f"pub enum {typename} {{\n" + + for v in u.variants: + vname = union_variant_name(v) + vtype = union_variant_type(v) + decl += f" {vname}({vtype}),\n" + + decl += "}\n\n" + + name = u.name.normalized() + org = str(u.name) + if u.deprecated: + defi += " #[deprecated]\n" + defi += rename_if_necessary(org, name) + if not u.optional: + defi += f" pub {name}: {typename},\n" + else: + defi += f' #[serde(skip_serializing_if = "Option::is_none")]\n' + defi += f" pub {name}: Option<{typename}>,\n" + + return defi, decl + + def gen_field(field, meta, override=None): if field.omit(): return ("", "") - if isinstance(field, CompositeField): + if isinstance(field, UnionField): + return gen_union(field, meta) + elif isinstance(field, CompositeField): return gen_composite(field, meta, override) elif isinstance(field, EnumField): return gen_enum(field, meta, override) @@ -240,6 +338,8 @@ def gen_array(a, meta, override=None): itemtype = a.itemtype.typename elif isinstance(a.itemtype, EnumField): itemtype = a.itemtype.typename + elif isinstance(a.itemtype, UnionField): + itemtype = a.itemtype.typename if itemtype is None: return ("", "") # Override said not to include diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index a48336a97145..d5df7e5eb1a5 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -157,6 +157,9 @@ def gather_subfields(field: Field) -> List[Field]: elif isinstance(field, ArrayField): fields = [] fields.extend(gather_subfields(field.itemtype)) + elif isinstance(field, UnionField): + for v in field.variants: + fields.extend(gather_subfields(v)) return fields @@ -282,6 +285,9 @@ def merge_dicts(dict1, dict2): if isinstance(field, ArrayField): field.itemtype.path = fpath + elif "oneOf" in ftype: + field = UnionField.from_js(ftype, fpath) + elif "type" not in ftype: logger.warning(f"Unmanaged {fpath}, it doesn't have a type") continue @@ -391,13 +397,15 @@ def from_js(cls, js, path): elif child_js["type"] in PrimitiveField.types: itemtype = PrimitiveField( - child_js["type"], path, child_js.get("description", "") + child_js["type"], path, child_js.get("description", ""), + added=child_js.get("added", None), deprecated=child_js.get("deprecated", None), ) elif child_js["type"] == "array": itemtype = ArrayField.from_js(path, child_js) variants.append(itemtype) - return UnionField(path, js.get('description', None), variants) + return UnionField(path, js.get('description', None), variants, + added=js.get('added', None), deprecated=js.get('deprecated', None)) class PrimitiveField(Field): @@ -495,11 +503,6 @@ def __str__(self): return f"Command[name={self.name}, fields=[{fieldnames}]]" -OfferStringField = PrimitiveField("string", None, None, added=None, deprecated=None) -InvoiceLabelField = PrimitiveField("string", None, None, added=None, deprecated=None) -DatastoreKeyField = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None) -DatastoreUsageKeyField = ArrayField(itemtype=PrimitiveField("string", None, None, added="v23.11", deprecated=None), dims=1, path=None, description=None, added="v23.11", deprecated=None) -InvoiceExposeprivatechannelsField = ArrayField(itemtype=PrimitiveField("short_channel_id", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None) PayExclude = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None) RenePayExclude = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added="v24.08", deprecated=None) RoutehintListField = PrimitiveField( @@ -509,7 +512,6 @@ def __str__(self): added=None, deprecated=None ) -SetConfigValField = PrimitiveField("string", None, None, added=None, deprecated=None) DecodeRoutehintListField = PrimitiveField( "DecodeRoutehintList", None, @@ -517,6 +519,7 @@ def __str__(self): added=None, deprecated=None ) +OfferStringField = PrimitiveField("string", None, None, added=None, deprecated=None) CreateRuneRestrictionsField = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None) CheckRuneParamsField = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None) ChainMovesExtraTagsField = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None) @@ -535,24 +538,12 @@ def __str__(self): # Override fields with manually managed types, fieldpath -> field mapping overrides = { - 'Invoice.label': InvoiceLabelField, - 'DelInvoice.label': InvoiceLabelField, - 'ListInvoices.label': InvoiceLabelField, - 'Datastore.key': DatastoreKeyField, - 'DelDatastore.key': DatastoreKeyField, - 'ListDatastore.key': DatastoreKeyField, - 'Invoice.exposeprivatechannels': InvoiceExposeprivatechannelsField, 'Pay.exclude': PayExclude, 'RenePay.exclude': RenePayExclude, 'KeySend.routehints': RoutehintListField, 'KeySend.extratlvs': TlvStreamField, 'Decode.routes': DecodeRoutehintListField, - 'CreateInvoice.label': InvoiceLabelField, - 'DatastoreUsage.key': DatastoreUsageKeyField, - 'WaitInvoice.label': InvoiceLabelField, 'Offer.recurrence_base': OfferStringField, - 'Offer.amount': OfferStringField, - 'SetConfig.val': SetConfigValField, 'CreateRune.restrictions': CreateRuneRestrictionsField, 'CheckRune.params': CheckRuneParamsField, "ListChainMoves.chainmoves[].extra_tags": ChainMovesExtraTagsField, diff --git a/contrib/msggen/msggen/patch.py b/contrib/msggen/msggen/patch.py index 98c80050ed1c..48bf4805414a 100644 --- a/contrib/msggen/msggen/patch.py +++ b/contrib/msggen/msggen/patch.py @@ -28,6 +28,10 @@ def recurse(f: model.Field, inherited_added: Optional[str] = None, inherited_dep if isinstance(f, model.ArrayField): self.visit(f.itemtype, f, inherited_added=f.added or inherited_added, inherited_deprecated=f.deprecated or inherited_deprecated) recurse(f.itemtype, inherited_added=f.added or inherited_added, inherited_deprecated=f.deprecated or inherited_deprecated) + elif isinstance(f, model.UnionField): + for v in f.variants: + self.visit(v, f, inherited_added=f.added or inherited_added, inherited_deprecated=f.deprecated or inherited_deprecated) + recurse(v, inherited_added=f.added or inherited_added, inherited_deprecated=f.deprecated or inherited_deprecated) elif isinstance(f, model.CompositeField): for c in f.fields: self.visit(c, f, inherited_added=inherited_added, inherited_deprecated=inherited_deprecated)