Skip to content
This repository was archived by the owner on Sep 24, 2025. It is now read-only.

Commit f02fc5c

Browse files
authored
fix: enable pair serialization to inputs.json and outputs.json during execution (#538)
1 parent 3f8f214 commit f02fc5c

File tree

5 files changed

+168
-99
lines changed

5 files changed

+168
-99
lines changed

wdl-engine/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
#### Added
1111

1212
* Added `cpu_limit_behavior` and `memory_limit_behavior` options to task execution configuration ([#543](https://github.com/stjude-rust-labs/wdl/pull/543))
13+
* Serialize `Pair` as `Object` for execution-level `inputs.json` and `outputs.json` ([#538](https://github.com/stjude-rust-labs/wdl/pull/538)).
1314

1415
#### Changed
1516

wdl-engine/src/inputs.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use anyhow::Result;
1111
use anyhow::bail;
1212
use indexmap::IndexMap;
1313
use serde::Serialize;
14+
use serde::ser::SerializeMap;
1415
use serde_json::Value as JsonValue;
1516
use serde_yaml_ng::Value as YamlValue;
1617
use wdl_analysis::Document;
@@ -326,7 +327,12 @@ impl Serialize for TaskInputs {
326327
S: serde::Serializer,
327328
{
328329
// Only serialize the input values
329-
self.inputs.serialize(serializer)
330+
let mut map = serializer.serialize_map(Some(self.inputs.len()))?;
331+
for (k, v) in &self.inputs {
332+
let serialized_value = crate::ValueSerializer::new(v, true);
333+
map.serialize_entry(k, &serialized_value)?;
334+
}
335+
map.end()
330336
}
331337
}
332338

@@ -659,7 +665,12 @@ impl Serialize for WorkflowInputs {
659665
{
660666
// Note: for serializing, only serialize the direct inputs, not the nested
661667
// inputs
662-
self.inputs.serialize(serializer)
668+
let mut map = serializer.serialize_map(Some(self.inputs.len()))?;
669+
for (k, v) in &self.inputs {
670+
let serialized_value = crate::ValueSerializer::new(v, true);
671+
map.serialize_entry(k, &serialized_value)?;
672+
}
673+
map.end()
663674
}
664675
}
665676

wdl-engine/src/outputs.rs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,12 @@ impl Serialize for Outputs {
6565
{
6666
use serde::ser::SerializeMap;
6767

68-
/// Helper `Serialize` implementation for serializing element values.
69-
struct Serialize<'a> {
70-
/// The value being serialized.
71-
value: &'a Value,
72-
}
73-
74-
impl serde::Serialize for Serialize<'_> {
75-
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
76-
where
77-
S: serde::Serializer,
78-
{
79-
self.value.serialize(serializer)
80-
}
81-
}
82-
8368
let mut s = serializer.serialize_map(Some(self.values.len()))?;
8469
for (k, v) in &self.values {
70+
let v = crate::ValueSerializer::new(v, true);
8571
match &self.name {
86-
Some(prefix) => {
87-
s.serialize_entry(&format!("{prefix}.{k}"), &Serialize { value: v })?
88-
}
89-
None => s.serialize_entry(k, &Serialize { value: v })?,
72+
Some(prefix) => s.serialize_entry(&format!("{prefix}.{k}"), &v)?,
73+
None => s.serialize_entry(k, &v)?,
9074
}
9175
}
9276

wdl-engine/src/stdlib/write_json.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ fn write_json(context: CallContext<'_>) -> Result<Value, Diagnostic> {
4747
// Serialize the value
4848
let mut writer = BufWriter::new(file.as_file_mut());
4949
let mut serializer = serde_json::Serializer::pretty(&mut writer);
50-
context.arguments[0]
51-
.value
50+
crate::ValueSerializer::new(&context.arguments[0].value, false)
5251
.serialize(&mut serializer)
5352
.map_err(|e| {
5453
function_call_failed(

wdl-engine/src/value.rs

Lines changed: 150 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -697,24 +697,6 @@ impl From<CallValue> for Value {
697697
}
698698
}
699699

700-
impl serde::Serialize for Value {
701-
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
702-
where
703-
S: serde::Serializer,
704-
{
705-
use serde::ser::Error;
706-
707-
match self {
708-
Self::None => serializer.serialize_none(),
709-
Self::Primitive(v) => v.serialize(serializer),
710-
Self::Compound(v) => v.serialize(serializer),
711-
Self::Task(_) | Self::Hints(_) | Self::Input(_) | Self::Output(_) | Self::Call(_) => {
712-
Err(S::Error::custom("value cannot be serialized"))
713-
}
714-
}
715-
}
716-
}
717-
718700
impl<'de> serde::Deserialize<'de> for Value {
719701
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
720702
where
@@ -2612,63 +2594,6 @@ impl From<Struct> for CompoundValue {
26122594
}
26132595
}
26142596

2615-
impl serde::Serialize for CompoundValue {
2616-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2617-
where
2618-
S: serde::Serializer,
2619-
{
2620-
use serde::ser::Error;
2621-
2622-
match self {
2623-
Self::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
2624-
Self::Array(v) => {
2625-
let mut s = serializer.serialize_seq(Some(v.len()))?;
2626-
for v in v.as_slice() {
2627-
s.serialize_element(v)?;
2628-
}
2629-
2630-
s.end()
2631-
}
2632-
Self::Map(v) => {
2633-
if !v
2634-
.ty()
2635-
.as_map()
2636-
.expect("type should be a map")
2637-
.key_type()
2638-
.is_coercible_to(&PrimitiveType::String.into())
2639-
{
2640-
return Err(S::Error::custom(
2641-
"only maps with `String` key types may be serialized",
2642-
));
2643-
}
2644-
2645-
let mut s = serializer.serialize_map(Some(v.len()))?;
2646-
for (k, v) in v.iter() {
2647-
s.serialize_entry(k, v)?;
2648-
}
2649-
2650-
s.end()
2651-
}
2652-
Self::Object(object) => {
2653-
let mut s = serializer.serialize_map(Some(object.len()))?;
2654-
for (k, v) in object.iter() {
2655-
s.serialize_entry(k, v)?;
2656-
}
2657-
2658-
s.end()
2659-
}
2660-
Self::Struct(Struct { members, .. }) => {
2661-
let mut s = serializer.serialize_map(Some(members.len()))?;
2662-
for (k, v) in members.iter() {
2663-
s.serialize_entry(k, v)?;
2664-
}
2665-
2666-
s.end()
2667-
}
2668-
}
2669-
}
2670-
}
2671-
26722597
/// Immutable data for task values.
26732598
#[derive(Debug)]
26742599
struct TaskData {
@@ -3069,6 +2994,125 @@ impl fmt::Display for CallValue {
30692994
}
30702995
}
30712996

2997+
/// Serializes a value with optional serialization of pairs.
2998+
pub struct ValueSerializer<'a> {
2999+
/// The value to serialize.
3000+
value: &'a Value,
3001+
/// Whether pairs should be serialized as a map with `left` and `right`
3002+
/// keys.
3003+
allow_pairs: bool,
3004+
}
3005+
3006+
impl<'a> ValueSerializer<'a> {
3007+
/// Constructs a new `ValueSerializer`.
3008+
pub fn new(value: &'a Value, allow_pairs: bool) -> Self {
3009+
Self { value, allow_pairs }
3010+
}
3011+
}
3012+
3013+
impl serde::Serialize for ValueSerializer<'_> {
3014+
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3015+
where
3016+
S: serde::Serializer,
3017+
{
3018+
use serde::ser::Error;
3019+
3020+
match &self.value {
3021+
Value::None => serializer.serialize_none(),
3022+
Value::Primitive(v) => v.serialize(serializer),
3023+
Value::Compound(v) => {
3024+
CompoundValueSerializer::new(v, self.allow_pairs).serialize(serializer)
3025+
}
3026+
Value::Task(_)
3027+
| Value::Hints(_)
3028+
| Value::Input(_)
3029+
| Value::Output(_)
3030+
| Value::Call(_) => Err(S::Error::custom("value cannot be serialized")),
3031+
}
3032+
}
3033+
}
3034+
3035+
/// Serializes a `CompoundValue` with optional serialization of pairs.
3036+
pub struct CompoundValueSerializer<'a> {
3037+
/// The compound value to serialize.
3038+
value: &'a CompoundValue,
3039+
/// Whether pairs should be serialized as a map with `left` and `right`
3040+
/// keys.
3041+
allow_pairs: bool,
3042+
}
3043+
3044+
impl<'a> CompoundValueSerializer<'a> {
3045+
/// Constructs a new `CompoundValueSerializer`.
3046+
pub fn new(value: &'a CompoundValue, allow_pairs: bool) -> Self {
3047+
Self { value, allow_pairs }
3048+
}
3049+
}
3050+
3051+
impl serde::Serialize for CompoundValueSerializer<'_> {
3052+
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3053+
where
3054+
S: serde::Serializer,
3055+
{
3056+
use serde::ser::Error;
3057+
3058+
match &self.value {
3059+
CompoundValue::Pair(pair) if self.allow_pairs => {
3060+
let mut state = serializer.serialize_map(Some(2))?;
3061+
let left = ValueSerializer::new(pair.left(), self.allow_pairs);
3062+
let right = ValueSerializer::new(pair.right(), self.allow_pairs);
3063+
state.serialize_entry("left", &left)?;
3064+
state.serialize_entry("right", &right)?;
3065+
state.end()
3066+
}
3067+
CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3068+
CompoundValue::Array(v) => {
3069+
let mut s = serializer.serialize_seq(Some(v.len()))?;
3070+
for v in v.as_slice() {
3071+
s.serialize_element(&ValueSerializer::new(v, self.allow_pairs))?;
3072+
}
3073+
3074+
s.end()
3075+
}
3076+
CompoundValue::Map(v) => {
3077+
if !v
3078+
.ty()
3079+
.as_map()
3080+
.expect("type should be a map")
3081+
.key_type()
3082+
.is_coercible_to(&PrimitiveType::String.into())
3083+
{
3084+
return Err(S::Error::custom(
3085+
"only maps with `String` key types may be serialized",
3086+
));
3087+
}
3088+
3089+
let mut s = serializer.serialize_map(Some(v.len()))?;
3090+
for (k, v) in v.iter() {
3091+
s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3092+
}
3093+
3094+
s.end()
3095+
}
3096+
CompoundValue::Object(object) => {
3097+
let mut s = serializer.serialize_map(Some(object.len()))?;
3098+
for (k, v) in object.iter() {
3099+
s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3100+
}
3101+
3102+
s.end()
3103+
}
3104+
CompoundValue::Struct(Struct { members, .. }) => {
3105+
let mut s = serializer.serialize_map(Some(members.len()))?;
3106+
for (k, v) in members.iter() {
3107+
s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3108+
}
3109+
3110+
s.end()
3111+
}
3112+
}
3113+
}
3114+
}
3115+
30723116
#[cfg(test)]
30733117
mod test {
30743118
use approx::assert_relative_eq;
@@ -3450,7 +3494,7 @@ Caused by:
34503494

34513495
let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
34523496
let value: Value = Pair::new(ty, left, right)
3453-
.expect("should create map value")
3497+
.expect("should create pair value")
34543498
.into();
34553499

34563500
// Pair[File, String] -> Pair[String, File]
@@ -3549,4 +3593,34 @@ Caused by:
35493593
r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
35503594
);
35513595
}
3596+
3597+
#[test]
3598+
fn pair_serialization() {
3599+
let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
3600+
let pair: Value = Pair::new(
3601+
pair_ty,
3602+
PrimitiveValue::new_file("foo"),
3603+
PrimitiveValue::new_string("bar"),
3604+
)
3605+
.expect("should create pair value")
3606+
.into();
3607+
// Serialize pair with `left` and `right` keys
3608+
let value_serializer = ValueSerializer::new(&pair, true);
3609+
let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3610+
assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
3611+
3612+
// Serialize pair without `left` and `right` keys (should fail)
3613+
let value_serializer = ValueSerializer::new(&pair, false);
3614+
assert!(serde_json::to_string(&value_serializer).is_err());
3615+
3616+
let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
3617+
let array: Value = Array::new(array_ty, [pair])
3618+
.expect("should create array value")
3619+
.into();
3620+
3621+
// Serialize array of pairs with `left` and `right` keys
3622+
let value_serializer = ValueSerializer::new(&array, true);
3623+
let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3624+
assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
3625+
}
35523626
}

0 commit comments

Comments
 (0)