Skip to main content

financial_types/
lib.rs

1#![cfg_attr(not(any(feature = "std", test)), no_std)]
2
3//! # Financial Types
4//!
5//! Core financial type definitions for trading systems.
6//!
7//! This crate provides fundamental enums used across financial applications:
8//! - [`UnderlyingAssetType`] — Classification of asset classes (stocks, crypto, forex, etc.)
9//! - [`Action`] — Trading actions (buy, sell)
10//! - [`Side`] — Position directional exposure (long, short)
11//! - [`OptionStyle`] — Option contract style (call, put)
12//!
13//! All enums use `#[repr(u8)]` for compact memory layout and are designed for
14//! high-performance financial systems.
15//!
16//! ## Features
17//!
18//! - Full `serde` serialization/deserialization support
19//! - Optional `utoipa` support for OpenAPI schema generation (enable the `utoipa` feature)
20//! - `#[repr(u8)]` on all enums for deterministic layout
21//! - `#[must_use]` on pure helper methods
22//! - `FromStr`, `TryFrom<&str>`, and `TryFrom<u8>` on every public enum,
23//!   returning [`ParseEnumError`] on failure. String parsing is
24//!   case-insensitive and trims whitespace.
25//!
26//! ## Wire format
27//!
28//! `serde` JSON encoding is part of the public contract. The string
29//! used for every variant is fixed and SemVer-tracked: renaming any
30//! variant string is a breaking change.
31//!
32//! | Enum                  | Variant     | JSON          |
33//! |-----------------------|-------------|---------------|
34//! | `UnderlyingAssetType` | `Crypto`    | `"Crypto"`    |
35//! | `UnderlyingAssetType` | `Stock`     | `"Stock"`     |
36//! | `UnderlyingAssetType` | `Forex`     | `"Forex"`     |
37//! | `UnderlyingAssetType` | `Commodity` | `"Commodity"` |
38//! | `UnderlyingAssetType` | `Bond`      | `"Bond"`      |
39//! | `UnderlyingAssetType` | `Other`     | `"Other"`     |
40//! | `UnderlyingAssetType` | `Future`    | `"Future"`    |
41//! | `UnderlyingAssetType` | `Forward`   | `"Forward"`   |
42//! | `Action`              | `Buy`       | `"Buy"`       |
43//! | `Action`              | `Sell`      | `"Sell"`      |
44//! | `Action`              | `Other`     | `"Other"`     |
45//! | `Side`                | `Long`      | `"Long"`      |
46//! | `Side`                | `Short`     | `"Short"`     |
47//! | `OptionStyle`         | `Call`      | `"Call"`      |
48//! | `OptionStyle`         | `Put`       | `"Put"`       |
49//!
50//! ## `no_std`
51//!
52//! The crate compiles in `no_std` environments. Disable default features:
53//!
54//! ```toml
55//! [dependencies]
56//! financial_types = { version = "0.1", default-features = false }
57//! ```
58//!
59//! The `alloc` crate is always required (used by `ParseEnumError`).
60//! Re-enable the `std` feature to opt into `std::error::Error`-style
61//! integration that pulls in `serde/std`.
62//!
63//! ## Usage
64//!
65//! ```rust
66//! use financial_types::{Action, Side, OptionStyle, UnderlyingAssetType};
67//!
68//! let action = Action::Buy;
69//! let side = Side::Long;
70//! let style = OptionStyle::Call;
71//! let asset = UnderlyingAssetType::Stock;
72//!
73//! assert_eq!(format!("{action}"), "Buy");
74//! assert_eq!(format!("{side}"), "Long");
75//! assert_eq!(format!("{style}"), "Call");
76//! assert_eq!(format!("{asset}"), "Stock");
77//!
78//! assert!(style.is_call());
79//! assert!(side.is_long());
80//! assert!(action.is_buy());
81//! ```
82
83extern crate alloc;
84
85pub mod prelude;
86
87use alloc::string::{String, ToString};
88use core::fmt;
89use core::str::FromStr;
90use serde::{Deserialize, Serialize};
91
92/// Error returned when a string or `u8` cannot be converted into one of the
93/// public financial enums defined by this crate.
94///
95/// This is the error type produced by `FromStr`, `TryFrom<&str>`, and
96/// `TryFrom<u8>` implementations on [`UnderlyingAssetType`], [`Action`],
97/// [`Side`], and [`OptionStyle`].
98///
99/// # Examples
100///
101/// ```rust
102/// use financial_types::{Side, ParseEnumError};
103/// use std::str::FromStr;
104///
105/// let err = Side::from_str("sideways").unwrap_err();
106/// assert_eq!(err.kind(), "Side");
107/// ```
108#[derive(Debug, Clone, PartialEq, Eq)]
109#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
110pub struct ParseEnumError {
111    /// Name of the enum that failed to parse (e.g. `"Side"`).
112    kind: &'static str,
113    /// Human-readable description of the invalid input.
114    input: String,
115}
116
117impl ParseEnumError {
118    /// Creates a new [`ParseEnumError`].
119    #[must_use]
120    #[inline]
121    pub fn new(kind: &'static str, input: impl Into<String>) -> Self {
122        Self {
123            kind,
124            input: input.into(),
125        }
126    }
127
128    /// Returns the name of the enum that failed to parse.
129    #[must_use]
130    #[inline]
131    pub const fn kind(&self) -> &'static str {
132        self.kind
133    }
134
135    /// Returns the original (stringified) input that was rejected.
136    #[must_use]
137    #[inline]
138    pub fn input(&self) -> &str {
139        &self.input
140    }
141}
142
143impl fmt::Display for ParseEnumError {
144    #[inline]
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        write!(f, "invalid {} value: {:?}", self.kind, self.input)
147    }
148}
149
150impl core::error::Error for ParseEnumError {}
151
152/// Classification of the underlying asset for a financial instrument.
153///
154/// Represents the broad asset class to which an instrument belongs.
155/// Used for routing, risk bucketing, and display purposes.
156///
157/// # Stability
158///
159/// This enum is `#[non_exhaustive]`. New asset classes may be added in
160/// minor releases without a major version bump. Downstream `match`
161/// expressions must include a wildcard arm (`_ =>`).
162///
163/// # Examples
164///
165/// ```rust
166/// use financial_types::UnderlyingAssetType;
167///
168/// let asset = UnderlyingAssetType::default();
169/// assert_eq!(asset, UnderlyingAssetType::Stock);
170/// assert_eq!(format!("{asset}"), "Stock");
171/// ```
172#[derive(
173    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
174)]
175#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
176#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
177#[repr(u8)]
178#[non_exhaustive]
179pub enum UnderlyingAssetType {
180    /// Cryptocurrency assets (e.g., BTC, ETH).
181    Crypto = 0,
182    /// Stock/equity assets (e.g., AAPL, GOOGL).
183    #[default]
184    Stock = 1,
185    /// Foreign exchange currency pairs (e.g., EUR/USD).
186    Forex = 2,
187    /// Commodity assets (e.g., Gold, Oil).
188    Commodity = 3,
189    /// Bond/fixed income securities.
190    Bond = 4,
191    /// Other asset types not covered by the above categories.
192    Other = 5,
193    /// Exchange-traded futures contract (e.g., ES, CL, ZB).
194    ///
195    /// Used to mark options whose underlying is a futures price `F`
196    /// rather than a spot price `S` (see Black-76 model).
197    Future = 6,
198    /// OTC / bilateral forward contract on any underlying.
199    ///
200    /// Used to mark options whose underlying is a forward price `F`
201    /// rather than a spot price `S` (see Black-76 model).
202    Forward = 7,
203}
204
205impl UnderlyingAssetType {
206    /// Returns `true` if this is a [`Stock`](Self::Stock) variant.
207    #[must_use]
208    #[inline]
209    pub const fn is_stock(&self) -> bool {
210        matches!(self, Self::Stock)
211    }
212
213    /// Returns `true` if this is a [`Crypto`](Self::Crypto) variant.
214    #[must_use]
215    #[inline]
216    pub const fn is_crypto(&self) -> bool {
217        matches!(self, Self::Crypto)
218    }
219
220    /// Returns `true` if this is a [`Forex`](Self::Forex) variant.
221    #[must_use]
222    #[inline]
223    pub const fn is_forex(&self) -> bool {
224        matches!(self, Self::Forex)
225    }
226
227    /// Returns `true` if this is a [`Commodity`](Self::Commodity) variant.
228    #[must_use]
229    #[inline]
230    pub const fn is_commodity(&self) -> bool {
231        matches!(self, Self::Commodity)
232    }
233
234    /// Returns `true` if this is a [`Bond`](Self::Bond) variant.
235    #[must_use]
236    #[inline]
237    pub const fn is_bond(&self) -> bool {
238        matches!(self, Self::Bond)
239    }
240
241    /// Returns `true` if this is a [`Future`](Self::Future) variant.
242    #[must_use]
243    #[inline]
244    pub const fn is_future(&self) -> bool {
245        matches!(self, Self::Future)
246    }
247
248    /// Returns `true` if this is a [`Forward`](Self::Forward) variant.
249    #[must_use]
250    #[inline]
251    pub const fn is_forward(&self) -> bool {
252        matches!(self, Self::Forward)
253    }
254
255    /// Returns the canonical string representation of this variant.
256    ///
257    /// Matches the [`fmt::Display`] output exactly and is allocation-free.
258    #[must_use]
259    #[inline]
260    pub const fn as_str(&self) -> &'static str {
261        match self {
262            Self::Crypto => "Crypto",
263            Self::Stock => "Stock",
264            Self::Forex => "Forex",
265            Self::Commodity => "Commodity",
266            Self::Bond => "Bond",
267            Self::Other => "Other",
268            Self::Future => "Future",
269            Self::Forward => "Forward",
270        }
271    }
272
273    /// All variants in discriminant order.
274    ///
275    /// Useful for iteration, UI dropdowns, exhaustive validation, and
276    /// schema generation. Order matches the `#[repr(u8)]` values.
277    pub const ALL: &'static [Self] = &[
278        Self::Crypto,
279        Self::Stock,
280        Self::Forex,
281        Self::Commodity,
282        Self::Bond,
283        Self::Other,
284        Self::Future,
285        Self::Forward,
286    ];
287}
288
289impl fmt::Display for UnderlyingAssetType {
290    #[inline]
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        f.write_str(self.as_str())
293    }
294}
295
296impl FromStr for UnderlyingAssetType {
297    type Err = ParseEnumError;
298
299    #[inline]
300    fn from_str(s: &str) -> Result<Self, Self::Err> {
301        match s.trim().to_ascii_lowercase().as_str() {
302            "crypto" => Ok(Self::Crypto),
303            "stock" => Ok(Self::Stock),
304            "forex" => Ok(Self::Forex),
305            "commodity" => Ok(Self::Commodity),
306            "bond" => Ok(Self::Bond),
307            "other" => Ok(Self::Other),
308            "future" => Ok(Self::Future),
309            "forward" => Ok(Self::Forward),
310            _ => Err(ParseEnumError::new("UnderlyingAssetType", s)),
311        }
312    }
313}
314
315impl TryFrom<&str> for UnderlyingAssetType {
316    type Error = ParseEnumError;
317
318    #[inline]
319    fn try_from(value: &str) -> Result<Self, Self::Error> {
320        Self::from_str(value)
321    }
322}
323
324impl TryFrom<u8> for UnderlyingAssetType {
325    type Error = ParseEnumError;
326
327    #[inline]
328    fn try_from(value: u8) -> Result<Self, Self::Error> {
329        match value {
330            0 => Ok(Self::Crypto),
331            1 => Ok(Self::Stock),
332            2 => Ok(Self::Forex),
333            3 => Ok(Self::Commodity),
334            4 => Ok(Self::Bond),
335            5 => Ok(Self::Other),
336            6 => Ok(Self::Future),
337            7 => Ok(Self::Forward),
338            other => Err(ParseEnumError::new(
339                "UnderlyingAssetType",
340                other.to_string(),
341            )),
342        }
343    }
344}
345
346/// Represents trading actions in a financial context.
347///
348/// Defines the fundamental trade operations that can be performed in a
349/// trading system. These actions represent the direction of a trade
350/// transaction.
351///
352/// # Stability
353///
354/// This enum is `#[non_exhaustive]`. New trading actions may be added in
355/// minor releases without a major version bump. Downstream `match`
356/// expressions must include a wildcard arm (`_ =>`).
357///
358/// # Examples
359///
360/// ```rust
361/// use financial_types::Action;
362///
363/// let action = Action::Buy;
364/// assert!(action.is_buy());
365/// assert_eq!(format!("{action}"), "Buy");
366/// ```
367#[derive(
368    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
369)]
370#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
371#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
372#[repr(u8)]
373#[non_exhaustive]
374pub enum Action {
375    /// Represents a purchase transaction, where assets are acquired.
376    #[default]
377    Buy = 0,
378    /// Represents a selling transaction, where assets are disposed of.
379    Sell = 1,
380    /// Action is not applicable to this type of transaction.
381    Other = 2,
382}
383
384impl Action {
385    /// Returns `true` if this is a [`Buy`](Self::Buy) action.
386    #[must_use]
387    #[inline]
388    pub const fn is_buy(&self) -> bool {
389        matches!(self, Self::Buy)
390    }
391
392    /// Returns `true` if this is a [`Sell`](Self::Sell) action.
393    #[must_use]
394    #[inline]
395    pub const fn is_sell(&self) -> bool {
396        matches!(self, Self::Sell)
397    }
398
399    /// Returns the canonical string representation of this variant.
400    ///
401    /// Matches the [`fmt::Display`] output exactly and is allocation-free.
402    #[must_use]
403    #[inline]
404    pub const fn as_str(&self) -> &'static str {
405        match self {
406            Self::Buy => "Buy",
407            Self::Sell => "Sell",
408            Self::Other => "Other",
409        }
410    }
411
412    /// All variants in discriminant order.
413    pub const ALL: &'static [Self] = &[Self::Buy, Self::Sell, Self::Other];
414
415    /// Returns the opposite trading action.
416    ///
417    /// - `Buy` → `Sell`
418    /// - `Sell` → `Buy`
419    /// - `Other` → `Other` (no meaningful inverse)
420    #[must_use]
421    #[inline]
422    pub const fn opposite(&self) -> Self {
423        match self {
424            Self::Buy => Self::Sell,
425            Self::Sell => Self::Buy,
426            Self::Other => Self::Other,
427        }
428    }
429}
430
431impl fmt::Display for Action {
432    #[inline]
433    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434        f.write_str(self.as_str())
435    }
436}
437
438impl FromStr for Action {
439    type Err = ParseEnumError;
440
441    #[inline]
442    fn from_str(s: &str) -> Result<Self, Self::Err> {
443        match s.trim().to_ascii_lowercase().as_str() {
444            "buy" => Ok(Self::Buy),
445            "sell" => Ok(Self::Sell),
446            "other" => Ok(Self::Other),
447            _ => Err(ParseEnumError::new("Action", s)),
448        }
449    }
450}
451
452impl TryFrom<&str> for Action {
453    type Error = ParseEnumError;
454
455    #[inline]
456    fn try_from(value: &str) -> Result<Self, Self::Error> {
457        Self::from_str(value)
458    }
459}
460
461impl TryFrom<u8> for Action {
462    type Error = ParseEnumError;
463
464    #[inline]
465    fn try_from(value: u8) -> Result<Self, Self::Error> {
466        match value {
467            0 => Ok(Self::Buy),
468            1 => Ok(Self::Sell),
469            2 => Ok(Self::Other),
470            other => Err(ParseEnumError::new("Action", other.to_string())),
471        }
472    }
473}
474
475/// Defines the directional exposure of a financial position.
476///
477/// Indicates whether a trader expects to profit from rising prices (Long)
478/// or falling prices (Short). This is a fundamental concept in trading that
479/// determines how profits and losses are calculated and affects risk
480/// management considerations.
481///
482/// # Stability
483///
484/// This enum is intentionally **exhaustive**. Position directionality is
485/// a closed two-state concept (long vs. short); no future variants are
486/// planned. Downstream code may rely on exhaustive `match`.
487///
488/// # Examples
489///
490/// ```rust
491/// use financial_types::Side;
492///
493/// let side = Side::Long;
494/// assert!(side.is_long());
495/// assert!(!side.is_short());
496/// assert_eq!(format!("{side}"), "Long");
497/// ```
498#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
499#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
500#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
501#[repr(u8)]
502pub enum Side {
503    /// Profits when the underlying asset's price increases.
504    /// Long positions involve buying an asset with the expectation of selling
505    /// at a higher price.
506    #[default]
507    Long = 0,
508    /// Profits when the underlying asset's price decreases.
509    /// Short positions involve selling an asset (often borrowed) with the
510    /// expectation of buying it back at a lower price.
511    Short = 1,
512}
513
514impl Side {
515    /// Returns `true` if this is a [`Long`](Self::Long) position.
516    #[must_use]
517    #[inline]
518    pub const fn is_long(&self) -> bool {
519        matches!(self, Self::Long)
520    }
521
522    /// Returns `true` if this is a [`Short`](Self::Short) position.
523    #[must_use]
524    #[inline]
525    pub const fn is_short(&self) -> bool {
526        matches!(self, Self::Short)
527    }
528
529    /// Returns the opposite side.
530    ///
531    /// - `Long` → `Short`
532    /// - `Short` → `Long`
533    #[must_use]
534    #[inline]
535    pub const fn opposite(&self) -> Self {
536        match self {
537            Self::Long => Self::Short,
538            Self::Short => Self::Long,
539        }
540    }
541
542    /// Returns the canonical string representation of this variant.
543    ///
544    /// Matches the [`fmt::Display`] output exactly and is allocation-free.
545    #[must_use]
546    #[inline]
547    pub const fn as_str(&self) -> &'static str {
548        match self {
549            Self::Long => "Long",
550            Self::Short => "Short",
551        }
552    }
553
554    /// All variants in discriminant order.
555    pub const ALL: &'static [Self] = &[Self::Long, Self::Short];
556}
557
558impl fmt::Display for Side {
559    #[inline]
560    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561        f.write_str(self.as_str())
562    }
563}
564
565impl fmt::Debug for Side {
566    #[inline]
567    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
568        match self {
569            Self::Long => write!(f, "Side::Long"),
570            Self::Short => write!(f, "Side::Short"),
571        }
572    }
573}
574
575impl FromStr for Side {
576    type Err = ParseEnumError;
577
578    #[inline]
579    fn from_str(s: &str) -> Result<Self, Self::Err> {
580        match s.trim().to_ascii_lowercase().as_str() {
581            "long" => Ok(Self::Long),
582            "short" => Ok(Self::Short),
583            _ => Err(ParseEnumError::new("Side", s)),
584        }
585    }
586}
587
588impl TryFrom<&str> for Side {
589    type Error = ParseEnumError;
590
591    #[inline]
592    fn try_from(value: &str) -> Result<Self, Self::Error> {
593        Self::from_str(value)
594    }
595}
596
597impl TryFrom<u8> for Side {
598    type Error = ParseEnumError;
599
600    #[inline]
601    fn try_from(value: u8) -> Result<Self, Self::Error> {
602        match value {
603            0 => Ok(Self::Long),
604            1 => Ok(Self::Short),
605            other => Err(ParseEnumError::new("Side", other.to_string())),
606        }
607    }
608}
609
610/// Specifies the style of an option contract.
611///
612/// Defines the fundamental classification of options contracts based on their
613/// payoff direction. The style determines whether the holder has the right to
614/// buy (Call) or sell (Put) the underlying asset.
615///
616/// This is a critical attribute for options contracts as it directly affects
617/// valuation, pricing models, and exercise strategies.
618///
619/// # Stability
620///
621/// This enum is intentionally **exhaustive**. Option payoff direction is
622/// a closed two-state concept (call vs. put); no future variants are
623/// planned. Downstream code may rely on exhaustive `match`.
624///
625/// # Examples
626///
627/// ```rust
628/// use financial_types::OptionStyle;
629///
630/// let style = OptionStyle::Call;
631/// assert!(style.is_call());
632/// assert!(!style.is_put());
633/// assert_eq!(format!("{style}"), "Call");
634/// assert!(OptionStyle::Call < OptionStyle::Put);
635/// ```
636#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
637#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
638#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
639#[repr(u8)]
640pub enum OptionStyle {
641    /// A call option gives the holder the right (but not obligation) to **buy**
642    /// the underlying asset at the strike price before or at expiration.
643    /// Call options typically increase in value when the underlying asset price rises.
644    #[default]
645    Call = 0,
646    /// A put option gives the holder the right (but not obligation) to **sell**
647    /// the underlying asset at the strike price before or at expiration.
648    /// Put options typically increase in value when the underlying asset price falls.
649    Put = 1,
650}
651
652impl OptionStyle {
653    /// Returns `true` if this is a [`Call`](Self::Call) option.
654    #[must_use]
655    #[inline]
656    pub const fn is_call(&self) -> bool {
657        matches!(self, Self::Call)
658    }
659
660    /// Returns `true` if this is a [`Put`](Self::Put) option.
661    #[must_use]
662    #[inline]
663    pub const fn is_put(&self) -> bool {
664        matches!(self, Self::Put)
665    }
666
667    /// Returns the opposite option style.
668    ///
669    /// - `Call` → `Put`
670    /// - `Put` → `Call`
671    #[must_use]
672    #[inline]
673    pub const fn opposite(&self) -> Self {
674        match self {
675            Self::Call => Self::Put,
676            Self::Put => Self::Call,
677        }
678    }
679
680    /// Returns the canonical string representation of this variant.
681    ///
682    /// Matches the [`fmt::Display`] output exactly and is allocation-free.
683    #[must_use]
684    #[inline]
685    pub const fn as_str(&self) -> &'static str {
686        match self {
687            Self::Call => "Call",
688            Self::Put => "Put",
689        }
690    }
691
692    /// All variants in discriminant order.
693    pub const ALL: &'static [Self] = &[Self::Call, Self::Put];
694}
695
696impl fmt::Display for OptionStyle {
697    #[inline]
698    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699        f.write_str(self.as_str())
700    }
701}
702
703impl fmt::Debug for OptionStyle {
704    #[inline]
705    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706        match self {
707            Self::Call => write!(f, "OptionStyle::Call"),
708            Self::Put => write!(f, "OptionStyle::Put"),
709        }
710    }
711}
712
713impl FromStr for OptionStyle {
714    type Err = ParseEnumError;
715
716    #[inline]
717    fn from_str(s: &str) -> Result<Self, Self::Err> {
718        match s.trim().to_ascii_lowercase().as_str() {
719            "call" => Ok(Self::Call),
720            "put" => Ok(Self::Put),
721            _ => Err(ParseEnumError::new("OptionStyle", s)),
722        }
723    }
724}
725
726impl TryFrom<&str> for OptionStyle {
727    type Error = ParseEnumError;
728
729    #[inline]
730    fn try_from(value: &str) -> Result<Self, Self::Error> {
731        Self::from_str(value)
732    }
733}
734
735impl TryFrom<u8> for OptionStyle {
736    type Error = ParseEnumError;
737
738    #[inline]
739    fn try_from(value: u8) -> Result<Self, Self::Error> {
740        match value {
741            0 => Ok(Self::Call),
742            1 => Ok(Self::Put),
743            other => Err(ParseEnumError::new("OptionStyle", other.to_string())),
744        }
745    }
746}
747
748#[cfg(feature = "proptest")]
749mod proptest_support {
750    use super::{Action, OptionStyle, Side, UnderlyingAssetType};
751    use proptest::prelude::*;
752
753    impl Arbitrary for UnderlyingAssetType {
754        type Parameters = ();
755        type Strategy = BoxedStrategy<Self>;
756
757        fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
758            prop_oneof![
759                Just(Self::Crypto),
760                Just(Self::Stock),
761                Just(Self::Forex),
762                Just(Self::Commodity),
763                Just(Self::Bond),
764                Just(Self::Other),
765                Just(Self::Future),
766                Just(Self::Forward),
767            ]
768            .boxed()
769        }
770    }
771
772    impl Arbitrary for Action {
773        type Parameters = ();
774        type Strategy = BoxedStrategy<Self>;
775
776        fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
777            prop_oneof![Just(Self::Buy), Just(Self::Sell), Just(Self::Other)].boxed()
778        }
779    }
780
781    impl Arbitrary for Side {
782        type Parameters = ();
783        type Strategy = BoxedStrategy<Self>;
784
785        fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
786            prop_oneof![Just(Self::Long), Just(Self::Short)].boxed()
787        }
788    }
789
790    impl Arbitrary for OptionStyle {
791        type Parameters = ();
792        type Strategy = BoxedStrategy<Self>;
793
794        fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
795            prop_oneof![Just(Self::Call), Just(Self::Put)].boxed()
796        }
797    }
798}