okane_core/report/
commodity.rs

1//! Defines commodity and its related types.
2
3use std::{collections::HashMap, fmt::Display};
4
5use bumpalo::Bump;
6
7use crate::syntax::pretty_decimal::PrettyDecimal;
8
9use super::intern::{FromInterned, InternError, InternStore, InternedStr};
10
11/// `&str` for commodities, interned within the `'arena` bounded allocator lifetime.
12#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
13pub struct Commodity<'arena>(InternedStr<'arena>);
14
15impl<'arena> FromInterned<'arena> for Commodity<'arena> {
16    fn from_interned(v: InternedStr<'arena>) -> Self {
17        Self(v)
18    }
19
20    fn as_interned(&self) -> InternedStr<'arena> {
21        self.0
22    }
23}
24
25impl Display for Commodity<'_> {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        self.as_str().fmt(f)
28    }
29}
30
31impl<'arena> Commodity<'arena> {
32    /// Returns the `&str`.
33    pub fn as_str(&self) -> &'arena str {
34        self.0.as_str()
35    }
36}
37
38/// Owned [`Commodity`], which is just [`String`].
39/// Useful to store in the error.
40#[derive(Debug, PartialEq, Eq, Hash, Clone)]
41pub struct OwnedCommodity(String);
42
43impl OwnedCommodity {
44    /// Creates a new [`OwnedCommodity`] instance.
45    pub fn from_string(v: String) -> Self {
46        Self(v)
47    }
48
49    /// Returns the underlying [`&str`].
50    pub fn as_str(&self) -> &str {
51        self.0.as_str()
52    }
53
54    /// Returns the underlying [`String`].
55    pub fn into_string(self) -> String {
56        self.0
57    }
58}
59
60impl From<Commodity<'_>> for OwnedCommodity {
61    fn from(value: Commodity<'_>) -> Self {
62        Self(value.as_str().to_string())
63    }
64}
65
66impl Display for OwnedCommodity {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        self.0.fmt(f)
69    }
70}
71
72/// `Interner` for [`Commodity`].
73pub(super) struct CommodityStore<'arena> {
74    intern: InternStore<'arena, Commodity<'arena>>,
75    formatting: HashMap<Commodity<'arena>, PrettyDecimal>,
76}
77
78impl<'arena> CommodityStore<'arena> {
79    /// Creates a new instance.
80    pub fn new(arena: &'arena Bump) -> Self {
81        Self {
82            intern: InternStore::new(arena),
83            formatting: HashMap::new(),
84        }
85    }
86
87    /// Returns the Commodity with the given `value`,
88    /// potentially resolving the alias.
89    /// If not available, registers the given `value` as the canonical.
90    pub fn ensure(&mut self, value: &str) -> Commodity<'arena> {
91        self.intern.ensure(value)
92    }
93
94    /// Returns the Commodity with the given `value` if and only if it's already registered.
95    pub fn resolve(&self, value: &str) -> Option<Commodity<'arena>> {
96        self.intern.resolve(value)
97    }
98
99    /// Inserts given `value` as always canonical.
100    /// Returns the registered canonical, or error if given `value` is already registered as alias.
101    /// Facade for [InternStore::insert_canonical].
102    pub(super) fn insert_canonical(
103        &mut self,
104        value: &str,
105    ) -> Result<Commodity<'arena>, InternError> {
106        self.intern.insert_canonical(value)
107    }
108
109    /// Inserts given `value` as always alias of `canonical`.
110    /// Returns error if given `value` is already registered as canonical.
111    /// Facade for [InternStore::insert_alias].
112    pub(super) fn insert_alias(
113        &mut self,
114        value: &str,
115        canonical: Commodity<'arena>,
116    ) -> Result<(), InternError> {
117        self.intern.insert_alias(value, canonical)
118    }
119
120    #[inline]
121    pub(super) fn get_decimal_point(&self, commodity: Commodity<'arena>) -> Option<u32> {
122        self.formatting.get(&commodity).map(|x| x.value.scale())
123    }
124
125    #[inline]
126    pub(super) fn set_format(&mut self, commodity: Commodity<'arena>, format: PrettyDecimal) {
127        self.formatting.insert(commodity, format);
128    }
129}