Skip to content

Commit f10c308

Browse files
committed
Fix SubmissionFeedbackKind serialization
1 parent d40bb07 commit f10c308

File tree

3 files changed

+87
-24
lines changed

3 files changed

+87
-24
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ tmc-langs-util = { path = "crates/tmc-langs-util" }
3939
tmc-mooc-client = { path = "crates/tmc-mooc-client" }
4040
tmc-server-mock = { path = "crates/helpers/tmc-server-mock" }
4141
tmc-testmycode-client = { path = "crates/tmc-testmycode-client" }
42-
ts-rs = { git = "https://github.com/Heliozoa/ts-rs.git", rev = "07712bf04007472aeeb065091261b3b64c019381" }
42+
ts-rs = { git = "https://github.com/Heliozoa/ts-rs.git", rev = "043c49c461543367d702e4db503f793f1ba801c2" }
4343

4444
# [patch.'https://github.com/Heliozoa/ts-rs.git']
4545
# ts-rs = { path = "../ts-rs/ts-rs" }

crates/tmc-testmycode-client/src/response.rs

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ pub struct SubmissionFeedbackQuestion {
437437
pub kind: SubmissionFeedbackKind,
438438
}
439439

440-
#[derive(Debug, PartialEq, Eq, JsonSchema)]
440+
#[derive(Debug, Clone, Copy, PartialEq, Eq, JsonSchema)]
441441
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
442442
pub enum SubmissionFeedbackKind {
443443
Text,
@@ -449,7 +449,22 @@ impl<'de> Deserialize<'de> for SubmissionFeedbackKind {
449449
where
450450
D: Deserializer<'de>,
451451
{
452-
deserializer.deserialize_string(SubmissionFeedbackKindVisitor {})
452+
let wrapper = SubmissionFeedbackKindWrapper::deserialize(deserializer)?;
453+
let kind = match wrapper {
454+
SubmissionFeedbackKindWrapper::String(string) => match string {
455+
SubmissionFeedbackKindString::Text => Self::Text,
456+
SubmissionFeedbackKindString::IntRange { lower, upper } => {
457+
Self::IntRange { lower, upper }
458+
}
459+
},
460+
SubmissionFeedbackKindWrapper::Derived(derived) => match derived {
461+
SubmissionFeedbackKindDerived::Text => Self::Text,
462+
SubmissionFeedbackKindDerived::IntRange { lower, upper } => {
463+
Self::IntRange { lower, upper }
464+
}
465+
},
466+
};
467+
Ok(kind)
453468
}
454469
}
455470

@@ -458,19 +473,53 @@ impl Serialize for SubmissionFeedbackKind {
458473
where
459474
S: Serializer,
460475
{
461-
let s = match self {
462-
Self::Text => "text".to_string(),
463-
Self::IntRange { lower, upper } => format!("intrange[{lower}..{upper}]"),
476+
let derived = match self {
477+
Self::Text => SubmissionFeedbackKindDerived::Text,
478+
Self::IntRange { lower, upper } => SubmissionFeedbackKindDerived::IntRange {
479+
lower: *lower,
480+
upper: *upper,
481+
},
464482
};
465-
serializer.serialize_str(&s)
483+
derived.serialize(serializer)
484+
}
485+
}
486+
487+
// wraps the two stringly typed and rusty versions of the kind
488+
#[derive(Debug, Clone, Copy, Deserialize)]
489+
#[serde(untagged)]
490+
enum SubmissionFeedbackKindWrapper {
491+
Derived(SubmissionFeedbackKindDerived),
492+
String(SubmissionFeedbackKindString),
493+
}
494+
495+
// uses derived serde impls
496+
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
497+
enum SubmissionFeedbackKindDerived {
498+
Text,
499+
IntRange { lower: u32, upper: u32 },
500+
}
501+
502+
// the stringly typed "text" or "intrange" that comes from the server
503+
#[derive(Debug, Clone, Copy)]
504+
enum SubmissionFeedbackKindString {
505+
Text,
506+
IntRange { lower: u32, upper: u32 },
507+
}
508+
509+
impl<'de> Deserialize<'de> for SubmissionFeedbackKindString {
510+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
511+
where
512+
D: Deserializer<'de>,
513+
{
514+
deserializer.deserialize_string(SubmissionFeedbackKindStringVisitor {})
466515
}
467516
}
468517

469-
struct SubmissionFeedbackKindVisitor {}
518+
struct SubmissionFeedbackKindStringVisitor {}
470519

471520
// parses "text" into Text, and "intrange[x..y]" into IntRange {lower: x, upper: y}
472-
impl<'de> Visitor<'de> for SubmissionFeedbackKindVisitor {
473-
type Value = SubmissionFeedbackKind;
521+
impl<'de> Visitor<'de> for SubmissionFeedbackKindStringVisitor {
522+
type Value = SubmissionFeedbackKindString;
474523

475524
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
476525
formatter.write_str("\"text\" or \"intrange[x..y]\"")
@@ -485,7 +534,7 @@ impl<'de> Visitor<'de> for SubmissionFeedbackKindVisitor {
485534
Lazy::new(|| Regex::new(r#"intrange\[(\d+)\.\.(\d+)\]"#).unwrap());
486535

487536
if value == "text" {
488-
Ok(SubmissionFeedbackKind::Text)
537+
Ok(SubmissionFeedbackKindString::Text)
489538
} else if let Some(captures) = RANGE.captures(value) {
490539
let lower = &captures[1];
491540
let lower = u32::from_str(lower).map_err(|e| {
@@ -495,7 +544,7 @@ impl<'de> Visitor<'de> for SubmissionFeedbackKindVisitor {
495544
let upper = u32::from_str(upper).map_err(|e| {
496545
E::custom(format!("error parsing intrange upper bound {upper}: {e}"))
497546
})?;
498-
Ok(SubmissionFeedbackKind::IntRange { lower, upper })
547+
Ok(SubmissionFeedbackKindString::IntRange { lower, upper })
499548
} else {
500549
Err(E::custom("expected \"text\" or \"intrange[x..y]\""))
501550
}
@@ -601,7 +650,7 @@ mod test {
601650
}
602651

603652
#[test]
604-
fn feedback_kind_de() {
653+
fn feedback_kind_de_server() {
605654
init();
606655

607656
let text = serde_json::json!("text");
@@ -619,17 +668,31 @@ mod test {
619668
}
620669
}
621670

671+
#[test]
672+
fn feedback_kind_de_rust() {
673+
init();
674+
675+
let original = SubmissionFeedbackKind::Text;
676+
let json = serde_json::to_string(&original).unwrap();
677+
let deserialized: SubmissionFeedbackKind = deserialize::json_from_str(&json).unwrap();
678+
assert_eq!(deserialized, original);
679+
680+
let original = SubmissionFeedbackKind::IntRange { lower: 1, upper: 5 };
681+
let json = serde_json::to_string(&original).unwrap();
682+
let deserialized: SubmissionFeedbackKind = deserialize::json_from_str(&json).unwrap();
683+
assert_eq!(deserialized, original);
684+
}
685+
622686
#[test]
623687
fn feedback_kind_se() {
624688
init();
625-
use serde_json::Value;
626689

627-
let text = SubmissionFeedbackKind::Text;
628-
let text = serde_json::to_value(&text).unwrap();
629-
assert_eq!(text, Value::String("text".to_string()));
690+
let original = SubmissionFeedbackKind::Text;
691+
let json = serde_json::to_string(&original).unwrap();
692+
assert_eq!(json, r#""Text""#);
630693

631-
let range = SubmissionFeedbackKind::IntRange { lower: 1, upper: 5 };
632-
let range = serde_json::to_value(&range).unwrap();
633-
assert_eq!(range, Value::String("intrange[1..5]".to_string()));
694+
let original = SubmissionFeedbackKind::IntRange { lower: 1, upper: 5 };
695+
let json = serde_json::to_string(&original).unwrap();
696+
assert_eq!(json, r#"{"IntRange":{"lower":1,"upper":5}}"#);
634697
}
635698
}

0 commit comments

Comments
 (0)