Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
23dff57
add #ifdef/#ifndef support to xdr parser and generator
leighmcculloch Mar 17, 2026
342d0c8
remove unnecessary reference on VARIANTS_STR
leighmcculloch Mar 17, 2026
fb663d4
change VARIANTS and VARIANTS_STR from slices to fixed-size arrays
leighmcculloch Mar 17, 2026
c95d121
derive VARIANTS from _VARIANTS using const loop
leighmcculloch Mar 17, 2026
67af0d8
add ifdef support to xdr enum and union generator
leighmcculloch Mar 17, 2026
ab7948a
add ifdef xdr types with lowercase feature names
leighmcculloch Mar 18, 2026
ead1e64
remove `#ifndef` and `#elif` preprocessor directive support
leighmcculloch Mar 18, 2026
b3e3c19
remove `#ifndef`, `#elif` xdr types and related features
leighmcculloch Mar 18, 2026
232cb14
update curr submodule
leighmcculloch Mar 18, 2026
fbe493e
regenerate curr xdr types from updated submodule
leighmcculloch Mar 18, 2026
867e966
remove comments from `_VARIANTS` const slices
leighmcculloch Mar 20, 2026
1ee5ae7
remove test ifdef types and related generated code
leighmcculloch Mar 20, 2026
2a74ddf
remove enable_extra_types and variant_a features
leighmcculloch Mar 20, 2026
027fac6
Merge branch 'main' into ifdef
leighmcculloch Mar 23, 2026
ff3c145
detect unclosed #ifdef in enum/union bodies
leighmcculloch Mar 20, 2026
f1ddabd
fix default on cfg-gated first enum/union member
leighmcculloch Mar 20, 2026
ebeb78e
add tests for inline #ifdef in enum/union bodies
leighmcculloch Mar 20, 2026
b0cfab9
add end-to-end generator tests for #ifdef cfg
leighmcculloch Mar 20, 2026
aa676df
make VARIANTS and VARIANTS_STR loop patterns consistent
leighmcculloch Mar 20, 2026
0908d3c
document feature name lowercasing in CfgExpr::render
leighmcculloch Mar 20, 2026
ddbce41
compute cfg_by_name in single pass over definitions
leighmcculloch Mar 20, 2026
388af14
use HashMap import instead of fully-qualified paths
leighmcculloch Mar 20, 2026
acf952d
regenerate curr and next xdr types
leighmcculloch Mar 20, 2026
ca899cb
add peek_position and extract position_from_byte_offset
leighmcculloch Mar 23, 2026
d3df314
expand generator tests to assert full output snippets
leighmcculloch Mar 23, 2026
aa3dad3
Merge branch 'main' into ifdef
leighmcculloch Mar 24, 2026
ddf3dc8
remove curr and next and build off xdr main with features
leighmcculloch Mar 25, 2026
8180843
update
leighmcculloch Mar 25, 2026
d12c41f
remove redundant `use stellar_xdr` from tests
leighmcculloch Mar 25, 2026
9be473e
Merge branch 'main' into ifdef-refac
leighmcculloch Mar 25, 2026
968445f
remove blank lines and unused serde_json imports
leighmcculloch Mar 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
version: 0.5.16
- name: Run minimal clippy checks on wasm32v1-none
if: matrix.sys.target == 'wasm32v1-none'
run: cargo clippy --target ${{ matrix.sys.target }} --no-default-features --features curr,next
run: cargo clippy --target ${{ matrix.sys.target }} --no-default-features
- name: Run full clippy checks on other targets
if: matrix.sys.target != 'wasm32v1-none'
run: cargo hack clippy $CARGO_HACK_ARGS --target ${{ matrix.sys.target }} --all-targets
Expand Down
10 changes: 3 additions & 7 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
[submodule "xdr/next"]
path = xdr/next
[submodule "xdr"]
path = xdr
url = https://github.com/stellar/stellar-xdr
branch = next
[submodule "xdr/curr"]
path = xdr/curr
url = https://github.com/stellar/stellar-xdr
branch = curr
branch = main
12 changes: 3 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## XDR Generator

This repository has an XDR-to-Rust code generator (`xdr-generator-rust/`) that generates Rust types from XDR definitions. It outputs to `src/*/generated.rs`.
This repository has an XDR-to-Rust code generator (`xdr-generator-rust/`) that generates Rust types from XDR definitions. It outputs to `src/generated.rs`.

## How to Regenerate From XDR
To regenerate types from XDR definitions:
Expand All @@ -15,18 +15,12 @@ To regenerate types from XDR definitions:

The `--init` flag is only required for the first time setting up the local
project. `--remote` flag will make sure to fetch the latest changes from
from the remote-tracking branches `curr` and `next` at [stellar/stellar-xdr].

If you have multiple remotes specified in the submodules (e.g. one
*tracking `stellar/stellar-xdr`, the other tracking `your-fork/stellar-xdr`),
make sure the remote that tracks [stellar/stellar-xdr] match with what's
specifies in the `.git/config` or `.gitsubmodules` (with `.git/config` taking
precedence. If neither file specifies it, then `origin` is used).
the remote-tracking branch `main` at [stellar/stellar-xdr].

2. Recompile and test

```console
make clean generate
```

When the regenerated types are ready to be merged, make sure to commit the regenerated code file `src/curr/generated.rs`, `src/next/generated.rs`, the version string file `xdr/curr-version`, `xdr/next-version`, as well as the submodule files `xdr/curr`, `xdr/next`.
When the regenerated types are ready to be merged, make sure to commit the regenerated code file `src/generated.rs`, the version string file `xdr-version`, as well as the submodule file `xdr`.
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ serde_json = "1.0.89"
bytes-lit = "0.0.5"

[features]
default = ["std", "curr"]
default = ["std"]
std = ["alloc", "dep:sha2"]
alloc = ["dep:hex", "dep:stellar-strkey", "escape-bytes/alloc", "dep:ethnum"]
curr = []
next = []

# Features from the XDR
test_feature = []

# Features dependent on optional dependencies.
base64 = ["std", "dep:base64"]
Expand All @@ -56,7 +57,7 @@ hex = []
rand = ["dep:rand"]

# Features for the CLI.
cli = ["std", "curr", "next", "base64", "serde", "serde_json", "schemars", "arbitrary", "rand", "dep:clap", "dep:thiserror", "dep:ethnum"]
cli = ["std", "base64", "serde", "serde_json", "schemars", "arbitrary", "rand", "dep:clap", "dep:thiserror", "dep:ethnum"]

[package.metadata.docs.rs]
all-features = true
Expand Down
42 changes: 13 additions & 29 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test:

build: generate
cargo hack clippy $(CARGO_HACK_ARGS) --all-targets
cargo clippy --no-default-features --features curr,next --release --target wasm32v1-none
cargo clippy --no-default-features --release --target wasm32v1-none

doc:
cargo test --doc --all-features
Expand All @@ -30,48 +30,32 @@ readme:
watch:
cargo watch --clear --watch-when-idle --shell '$(MAKE)'

generate: generate-files xdr/curr-version xdr/next-version xdr-json/curr xdr-json/next
generate: generate-files xdr-version xdr-json

CUSTOM_DEFAULT_IMPL=TransactionEnvelope
CUSTOM_STR_IMPL=PublicKey,AccountId,ContractId,MuxedAccount,MuxedAccountMed25519,SignerKey,SignerKeyEd25519SignedPayload,NodeId,ScAddress,AssetCode,AssetCode4,AssetCode12,ClaimableBalanceId,PoolId,MuxedEd25519Account,Int128Parts,UInt128Parts,Int256Parts,UInt256Parts

generate-files: src/curr/generated.rs src/next/generated.rs
generate-files: src/generated.rs

src/curr/generated.rs: $(sort $(wildcard xdr/curr/*.x))
src/generated.rs: $(sort $(wildcard xdr/*.x))
cargo run --manifest-path xdr-generator-rust/generator/Cargo.toml -- \
$(addprefix --input ,$(sort $(wildcard xdr/curr/*.x))) \
$(addprefix --input ,$(sort $(wildcard xdr/*.x))) \
--output $@ \
--custom-default $(CUSTOM_DEFAULT_IMPL) \
--custom-str $(CUSTOM_STR_IMPL)
rustfmt $@

src/next/generated.rs: $(sort $(wildcard xdr/next/*.x))
cargo run --manifest-path xdr-generator-rust/generator/Cargo.toml -- \
$(addprefix --input ,$(sort $(wildcard xdr/next/*.x))) \
--output $@ \
--custom-default $(CUSTOM_DEFAULT_IMPL) \
--custom-str $(CUSTOM_STR_IMPL)
rustfmt $@

xdr/curr-version: $(wildcard .git/modules/xdr/curr/**/*) $(wildcard xdr/curr/*.x)
git submodule status -- xdr/curr | sed 's/^ *//g' | cut -f 1 -d " " | tr -d '\n' | tr -d '+' > xdr/curr-version

xdr/next-version: $(wildcard .git/modules/xdr/next/**/*) $(wildcard xdr/next/*.x)
git submodule status -- xdr/next | sed 's/^ *//g' | cut -f 1 -d " " | tr -d '\n' | tr -d '+' > xdr/next-version

xdr-json/curr: src/curr/generated.rs
mkdir -p xdr-json/curr
cargo run --features cli -- +curr types schema-files --out-dir xdr-json/curr
xdr-version: $(wildcard .git/modules/xdr/**/*) $(wildcard xdr/*.x)
git submodule status -- xdr | sed 's/^ *//g' | cut -f 1 -d " " | tr -d '\n' | tr -d '+' > xdr-version

xdr-json/next: src/next/generated.rs
mkdir -p xdr-json/next
cargo run --features cli -- +next types schema-files --out-dir xdr-json/next
xdr-json: src/generated.rs
mkdir -p xdr-json
cargo run --features cli -- types schema-files --out-dir xdr-json

clean:
rm -f src/*/generated.rs
rm -f xdr/*-version
rm -fr xdr-json/curr
rm -fr xdr-json/next
rm -f src/generated.rs
rm -f xdr-version
rm -fr xdr-json
cargo clean --quiet

fmt:
Expand Down
17 changes: 4 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ stellar-xdr = { version = "...", default-features = true, features = [] }

#### Features

The crate has several features, tiers of functionality, ancillary
functionality, and channels of XDR.
The crate has several features, tiers of functionality, and ancillary
functionality.

Default features: `std`, `curr`.
Default features: `std`.

Tiers of functionality:

Expand Down Expand Up @@ -57,15 +57,6 @@ Automatically enabled when serde is enabled.
Features marked experimental may disappear at anytime, see breaking changes
at anytime, or and may be minimal implementations instead of complete.

Channels of XDR:

- `curr` – XDR types built from the `stellar/stellar-xdr` `curr` branch.
- `next` – XDR types built from the `stellar/stellar-xdr` `next` branch.

If a single channel is enabled the types are available at the root of the
crate. If multiple channels are enabled they are available in modules at
the root of the crate.

### CLI

To use the CLI:
Expand All @@ -85,7 +76,7 @@ AAAAA...

Parse a `ScSpecEntry` stream from a contract:
```console
stellar-xdr +next decode --type ScSpecEntry --input stream-base64 --output json-formatted << -
stellar-xdr decode --type ScSpecEntry --input stream-base64 --output json-formatted << -
AAAAA...
-
```
Expand Down
File renamed without changes.
41 changes: 17 additions & 24 deletions src/cli/compare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@ use std::{

use clap::{Args, ValueEnum};

use crate::cli::Channel;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("unknown type {0}, choose one of {1:?}")]
UnknownType(String, &'static [&'static str]),
#[error("error decoding XDR: {0}")]
ReadXdrCurr(#[from] crate::curr::Error),
#[error("error decoding XDR: {0}")]
ReadXdrNext(#[from] crate::next::Error),
ReadXdr(#[from] crate::Error),
#[error("error reading file: {0}")]
ReadFile(std::io::Error),
#[error("error writing output: {0}")]
Expand Down Expand Up @@ -62,34 +58,36 @@ impl Default for InputFormat {
}
}

// TODO: Remove run_x macro, it exists only to reduce the diff from when curr/next
// channels existed and each had their own run_curr/run_next invocation.
macro_rules! run_x {
($f:ident, $m:ident) => {
($f:ident) => {
fn $f(&self) -> Result<(), Error> {
let f1 = File::open(&self.left).map_err(Error::ReadFile)?;
let f2 = File::open(&self.right).map_err(Error::ReadFile)?;
let r#type = crate::$m::TypeVariant::from_str(&self.r#type).map_err(|_| {
Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR)
let r#type = crate::TypeVariant::from_str(&self.r#type).map_err(|_| {
Error::UnknownType(self.r#type.clone(), &crate::TypeVariant::VARIANTS_STR)
})?;
let (t1, t2) = match self.input {
InputFormat::Single => {
let t1 = {
let mut l1 = crate::$m::Limited::new(f1, crate::$m::Limits::none());
crate::$m::Type::read_xdr_to_end(r#type, &mut l1)?
let mut l1 = crate::Limited::new(f1, crate::Limits::none());
crate::Type::read_xdr_to_end(r#type, &mut l1)?
};
let t2 = {
let mut l = crate::$m::Limited::new(f2, crate::$m::Limits::none());
crate::$m::Type::read_xdr_to_end(r#type, &mut l)?
let mut l = crate::Limited::new(f2, crate::Limits::none());
crate::Type::read_xdr_to_end(r#type, &mut l)?
};
(t1, t2)
}
InputFormat::SingleBase64 => {
let t1 = {
let mut l = crate::$m::Limited::new(f1, crate::$m::Limits::none());
crate::$m::Type::read_xdr_base64_to_end(r#type, &mut l)?
let mut l = crate::Limited::new(f1, crate::Limits::none());
crate::Type::read_xdr_base64_to_end(r#type, &mut l)?
};
let t2 = {
let mut l = crate::$m::Limited::new(f2, crate::$m::Limits::none());
crate::$m::Type::read_xdr_base64_to_end(r#type, &mut l)?
let mut l = crate::Limited::new(f2, crate::Limits::none());
crate::Type::read_xdr_base64_to_end(r#type, &mut l)?
};
(t1, t2)
}
Expand All @@ -107,19 +105,14 @@ impl Cmd {
/// ## Errors
///
/// If the command is configured with state that is invalid.
pub fn run(&self, channel: &Channel) -> Result<(), Error> {
let result = match channel {
Channel::Curr => self.run_curr(),
Channel::Next => self.run_next(),
};

pub fn run(&self) -> Result<(), Error> {
let result = self.run_inner();
match result {
Ok(()) => Ok(()),
Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e),
}
}

run_x!(run_curr, curr);
run_x!(run_next, next);
run_x!(run_inner);
}
45 changes: 20 additions & 25 deletions src/cli/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ use std::{fmt::Debug, str::FromStr};
use clap::{Args, ValueEnum};
use serde::Serialize;

use crate::cli::{util, Channel};
use crate::cli::util;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("unknown type {0}, choose one of {1:?}")]
UnknownType(String, &'static [&'static str]),
#[error("error decoding XDR: {0}")]
ReadXdrCurr(#[from] crate::curr::Error),
#[error("error decoding XDR: {0}")]
ReadXdrNext(#[from] crate::next::Error),
ReadXdr(#[from] crate::Error),
#[error("error reading file: {0}")]
ReadFile(std::io::Error),
#[error("error writing output: {0}")]
Expand Down Expand Up @@ -75,40 +73,42 @@ impl Default for OutputFormat {
}
}

// TODO: Remove run_x macro, it exists only to reduce the diff from when curr/next
// channels existed and each had their own run_curr/run_next invocation.
macro_rules! run_x {
($f:ident, $m:ident) => {
($f:ident) => {
fn $f(&self) -> Result<(), Error> {
let mut input = util::parse_input(&self.input).map_err(Error::ReadFile)?;
let r#type = crate::$m::TypeVariant::from_str(&self.r#type).map_err(|_| {
Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR)
let r#type = crate::TypeVariant::from_str(&self.r#type).map_err(|_| {
Error::UnknownType(self.r#type.clone(), &crate::TypeVariant::VARIANTS_STR)
})?;
for f in &mut input {
match self.input_format {
InputFormat::Single => {
let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
let t = crate::$m::Type::read_xdr_to_end(r#type, &mut l)?;
let mut l = crate::Limited::new(f, crate::Limits::none());
let t = crate::Type::read_xdr_to_end(r#type, &mut l)?;
self.out(&t)?;
}
InputFormat::SingleBase64 => {
let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
let t = crate::$m::Type::read_xdr_base64_to_end(r#type, &mut l)?;
let mut l = crate::Limited::new(f, crate::Limits::none());
let t = crate::Type::read_xdr_base64_to_end(r#type, &mut l)?;
self.out(&t)?;
}
InputFormat::Stream => {
let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
for t in crate::$m::Type::read_xdr_iter(r#type, &mut l) {
let mut l = crate::Limited::new(f, crate::Limits::none());
for t in crate::Type::read_xdr_iter(r#type, &mut l) {
self.out(&t?)?;
}
}
InputFormat::StreamBase64 => {
let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
for t in crate::$m::Type::read_xdr_base64_iter(r#type, &mut l) {
let mut l = crate::Limited::new(f, crate::Limits::none());
for t in crate::Type::read_xdr_base64_iter(r#type, &mut l) {
self.out(&t?)?;
}
}
InputFormat::StreamFramed => {
let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
for t in crate::$m::Type::read_xdr_framed_iter(r#type, &mut l) {
let mut l = crate::Limited::new(f, crate::Limits::none());
for t in crate::Type::read_xdr_framed_iter(r#type, &mut l) {
self.out(&t?)?;
}
}
Expand All @@ -125,21 +125,16 @@ impl Cmd {
/// ## Errors
///
/// If the command is configured with state that is invalid.
pub fn run(&self, channel: &Channel) -> Result<(), Error> {
let result = match channel {
Channel::Curr => self.run_curr(),
Channel::Next => self.run_next(),
};

pub fn run(&self) -> Result<(), Error> {
let result = self.run_inner();
match result {
Ok(()) => Ok(()),
Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e),
}
}

run_x!(run_curr, curr);
run_x!(run_next, next);
run_x!(run_inner);

fn out(&self, v: &(impl Serialize + Debug)) -> Result<(), Error> {
let text = match self.output_format {
Expand Down
Loading
Loading