Skip to content

Migrate models to validated deserialization via serde(try_from) #704

Description

@isPANN

Problem

Most model structs (~55) use #[derive(Deserialize)] which bypasses constructor validation (assert!/panic! in new()). When users load invalid JSON via pred solve/pred evaluate/pred reduce/pred inspect, the deserialization path (serde_json::from_value in declare_variants! factory) skips all checks, leading to:

  • Runtime panics with unhelpful messages (e.g., index out of bounds in SpinGlass when fields.len() != num_spins)
  • Silent invalid state (e.g., ExactCoverBy3Sets with universe_size % 3 != 0 — silently unsolvable)

pred create is safe (constructs via new()). Only the JSON loading path is affected.

Already protected (~8 models)

Models with manual impl Deserialize, serde(from), or serde(try_from):

  • SequencingWithinIntervals, SequencingToMinimizeMaximumCumulativeCost, RectilinearPictureCompression, PartiallyOrderedKnapsack, SumOfSquaresPartition, MultipleChoiceBranching, StrongConnectivityAugmentation, BalancedCompleteBipartiteSubgraph

Proposed fix: #[serde(try_from)]

Replace #[derive(Deserialize)] with #[serde(try_from = "RawStruct")] + impl TryFrom:

#[derive(Serialize, Deserialize)]
#[serde(try_from = "FooRaw")]
pub struct Foo { ... }

#[derive(Deserialize)]
struct FooRaw { /* same fields */ }

impl TryFrom<FooRaw> for Foo {
    type Error = String;
    fn try_from(raw: FooRaw) -> Result<Self, String> {
        // reuse validation logic from new(), return Result instead of panic
    }
}

Advantages over manual impl Deserialize:

  • No hand-written Deserializer trait code
  • Validation errors become proper serde errors (clean CLI messages)
  • Shared validation function between new() (unwrap/panic) and TryFrom (propagate)

Scope

~55 models need migration. Can be done incrementally per-category (graph/, misc/, set/, algebraic/, formula/).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestlow prioritywait, wait, wait, do I really need this?

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions