Skip to main content

shopify_sdk/rest/resources/v2025_10/
currency.rs

1//! Currency resource implementation.
2//!
3//! This module provides the [`Currency`] resource for retrieving the currencies
4//! enabled on a shop.
5//!
6//! # Read-Only Resource
7//!
8//! Currencies implement [`ReadOnlyResource`](crate::rest::ReadOnlyResource) - they
9//! can only be listed, not created, updated, or deleted through the API.
10//! Currency settings are managed through the Shopify admin.
11//!
12//! # No ID Field
13//!
14//! Unlike most resources, Currency does not have a numeric ID field.
15//! Currencies are identified by their currency code (e.g., "USD", "CAD").
16//!
17//! # Example
18//!
19//! ```rust,ignore
20//! use shopify_sdk::rest::{RestResource, ResourceResponse};
21//! use shopify_sdk::rest::resources::v2025_10::Currency;
22//!
23//! // List all enabled currencies
24//! let currencies = Currency::all(&client, None).await?;
25//! for currency in currencies.iter() {
26//!     println!("{} - rate updated: {:?}",
27//!         currency.currency.as_deref().unwrap_or(""),
28//!         currency.rate_updated_at);
29//! }
30//! ```
31
32use serde::{Deserialize, Serialize};
33
34use crate::rest::{ReadOnlyResource, ResourceOperation, ResourcePath, RestResource};
35use crate::HttpMethod;
36
37/// A currency enabled on a Shopify store.
38///
39/// Currencies represent the different currencies a store can accept.
40/// They are read-only through the API.
41///
42/// # Read-Only Resource
43///
44/// This resource implements [`ReadOnlyResource`] - only GET operations are
45/// available. Currency settings are managed through the Shopify admin.
46///
47/// # No ID Field
48///
49/// This resource does not have a numeric `id` field. Currencies are
50/// identified by their currency code.
51///
52/// # Fields
53///
54/// All fields are read-only:
55/// - `currency` - The three-letter ISO 4217 currency code
56/// - `rate_updated_at` - When the exchange rate was last updated
57/// - `enabled` - Whether the currency is enabled
58#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
59pub struct Currency {
60    /// The three-letter ISO 4217 currency code (e.g., "USD", "CAD", "EUR").
61    #[serde(skip_serializing)]
62    pub currency: Option<String>,
63
64    /// When the exchange rate was last updated.
65    #[serde(skip_serializing)]
66    pub rate_updated_at: Option<String>,
67
68    /// Whether this currency is enabled on the shop.
69    #[serde(skip_serializing)]
70    pub enabled: Option<bool>,
71}
72
73impl RestResource for Currency {
74    type Id = String;
75    type FindParams = ();
76    type AllParams = ();
77    type CountParams = ();
78
79    const NAME: &'static str = "Currency";
80    const PLURAL: &'static str = "currencies";
81
82    /// Paths for the Currency resource.
83    ///
84    /// Only list operation - no Find, Count, Create, Update, or Delete.
85    const PATHS: &'static [ResourcePath] = &[
86        ResourcePath::new(HttpMethod::Get, ResourceOperation::All, &[], "currencies"),
87        // Note: No Find by ID - currencies identified by code
88        // Note: No Count endpoint
89        // Note: No Create, Update, or Delete - read-only resource
90    ];
91
92    fn get_id(&self) -> Option<Self::Id> {
93        self.currency.clone()
94    }
95}
96
97impl ReadOnlyResource for Currency {}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::rest::{get_path, ReadOnlyResource, ResourceOperation, RestResource};
103
104    #[test]
105    fn test_currency_implements_read_only_resource() {
106        fn assert_read_only<T: ReadOnlyResource>() {}
107        assert_read_only::<Currency>();
108    }
109
110    #[test]
111    fn test_currency_deserialization() {
112        let json = r#"{
113            "currency": "CAD",
114            "rate_updated_at": "2024-01-15T10:30:00-05:00",
115            "enabled": true
116        }"#;
117
118        let currency: Currency = serde_json::from_str(json).unwrap();
119
120        assert_eq!(currency.currency, Some("CAD".to_string()));
121        assert_eq!(
122            currency.rate_updated_at,
123            Some("2024-01-15T10:30:00-05:00".to_string())
124        );
125        assert_eq!(currency.enabled, Some(true));
126    }
127
128    #[test]
129    fn test_currency_list_only_paths() {
130        // All (list)
131        let all_path = get_path(Currency::PATHS, ResourceOperation::All, &[]);
132        assert!(all_path.is_some());
133        assert_eq!(all_path.unwrap().template, "currencies");
134
135        // No Find path
136        let find_path = get_path(Currency::PATHS, ResourceOperation::Find, &["id"]);
137        assert!(find_path.is_none());
138
139        // No Count path
140        let count_path = get_path(Currency::PATHS, ResourceOperation::Count, &[]);
141        assert!(count_path.is_none());
142
143        // No Create path
144        let create_path = get_path(Currency::PATHS, ResourceOperation::Create, &[]);
145        assert!(create_path.is_none());
146
147        // No Update path
148        let update_path = get_path(Currency::PATHS, ResourceOperation::Update, &["id"]);
149        assert!(update_path.is_none());
150
151        // No Delete path
152        let delete_path = get_path(Currency::PATHS, ResourceOperation::Delete, &["id"]);
153        assert!(delete_path.is_none());
154    }
155
156    #[test]
157    fn test_currency_constants() {
158        assert_eq!(Currency::NAME, "Currency");
159        assert_eq!(Currency::PLURAL, "currencies");
160    }
161
162    #[test]
163    fn test_currency_get_id_returns_code() {
164        let currency_with_code = Currency {
165            currency: Some("USD".to_string()),
166            rate_updated_at: None,
167            enabled: Some(true),
168        };
169        assert_eq!(currency_with_code.get_id(), Some("USD".to_string()));
170
171        let currency_without_code = Currency::default();
172        assert_eq!(currency_without_code.get_id(), None);
173    }
174
175    #[test]
176    fn test_currency_all_fields_are_read_only() {
177        // All fields should be skipped during serialization
178        let currency = Currency {
179            currency: Some("EUR".to_string()),
180            rate_updated_at: Some("2024-01-15T10:30:00-05:00".to_string()),
181            enabled: Some(true),
182        };
183
184        let json = serde_json::to_value(&currency).unwrap();
185        // All fields should be omitted (empty object)
186        assert_eq!(json, serde_json::json!({}));
187    }
188}