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)
Has anyone found a clean workaround for this? Or is enum support planned in future releases of the crate?
Thanks in advance!
1 Like
Hirano
August 23, 2025, 11:01am
2
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(),
}
}
1 Like
I ran into exactly the same issue.
The logic of our application is quite complex, and I’d really prefer not to give up using Enums.
The solution suggested by @Hirano , while it seems to work, would require a lot of extra code, which would make project maintenance more difficult.
Besides, I still hope that the issue will be fixed soon, so we’ll be able to migrate to the new version without any problems.
1 Like
Hirano
September 27, 2025, 10:49am
4
I introduced traits to make the code more reusable.
However, I hope that such code will no longer be necessary.
#[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> {
to_enum(&self.purchase_type)
}
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PurchaseType {
OneTimePurchase,
Subscription,
Both,
}
trait FromStrCustom: Sized {
fn from_str_custom(s: &str) -> Option<Self>;
}
impl FromStrCustom for PurchaseType {
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,
}
}
}
fn to_enum<T: FromStrCustom>(val: &Option<String>) -> Option<T> {
val.as_ref().and_then(|s| T::from_str_custom(s))
}
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(),
}
}
I’ve ended up using the serde implementation instead.
I cannot get this to new crate to work properly with enums.