Rust shopify_function crate 1.1.1 – Enum Deserialization Limitation

I’m trying to migrate a discount function from shopify_function 0.8.x to 1.1.1.

In 0.8.x, I could use serde tagged enums for config without issues:

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", content = "value", rename_all = "lowercase")]
pub enum DiscountValue {
    Percentage(f64),
    Amount(Vec<MarketWiseAmount>),
}

But in 1.1.1, this fails when used in custom_scalar_overrides:

#[query("src/run.graphql",
  custom_scalar_overrides = {
    "Input.discount.configuration.jsonValue" => super::Configuration,
  }
)]

Error: “Enum types are not supported for deriving Deserialize

Enums are pretty important for modeling real-world discount configs (percentage vs. amount, min quantity vs. amount, etc.). Right now my only options seem to be:

  • Stay on 0.8.x
  • Do fragile JSON conversions with serde_json
  • Flatten everything to structs (lose type safety)

:backhand_index_pointing_right: Has anyone found a clean workaround for this? Or is enum support planned in future releases of the crate?

Thanks in advance!

1 Like

I am trying to implement enum using the following method. However, this code needs to be verified during the testing phase.

PS: I have tested the following code and confirmed that it works.

#[derive(Deserialize, Debug, Clone)]
#[shopify_function(rename_all = "camelCase")]
pub struct DiscountConfiguration {
  pub purchase_type: Option<String>
}

impl DiscountConfiguration {
  const DEFAULT_PURCHASE_TYPE: Option<String> = None;

  pub fn purchase_type_enum(&self) -> Option<PurchaseType> {
    self
      .purchase_type
      .as_ref()
      .and_then(|s| PurchaseType::from_str(s))
  }
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PurchaseType {
  OneTimePurchase,
  Subscription,
  Both,
}
impl PurchaseType {
  pub fn from_str_custom(s: &str) -> Option<Self> {
    match s {
      "ONE_TIME_PURCHASE" => Some(Self::OneTimePurchase),
      "SUBSCRIPTION" => Some(Self::Subscription),
      "BOTH" => Some(Self::Both),
      _ => None,
    }
  }
}

Next is an examples of usage.

fn is_purchase_type_line(&self, line: &Lines) -> bool {
    match self.configuration.purchase_type_enum() {
      None | Some(PurchaseType::Both) => true,
      Some(PurchaseType::OneTimePurchase) => line.selling_plan_allocation().is_none(),
      Some(PurchaseType::Subscription) => line.selling_plan_allocation().is_some(),
    }
  }