resb/
bundle.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! The `bundle` module provides data structures for working directly with the
//! contents of a resource bundle.
//!
//! WARNING: This module is not suitable for use at runtime due to its reliance
//! on `std` and `alloc` and therefore not intended for general deserialization
//! of resource bundles. Rather, it is intended to be used in development-time
//! tools for working with bundles.

extern crate alloc;
use alloc::{borrow::Cow, collections::BTreeMap};

use crate::MASK_28_BIT;

/// A tree-like collection of data [`Resource`]s primarily intended for storing
/// locale and other internationalization data for [ICU] (International
/// Components for Unicode).
///
/// [ICU]: https://icu.unicode.org/
#[derive(Debug)]
pub struct ResourceBundle<'a> {
    name: Cow<'a, str>,
    root: Resource<'a>,

    /// Whether fallback is enabled for this resource bundle.
    ///
    /// A resource bundle storing locale data may omit some data in order to
    /// reduce duplication, allowing fallback to more general locales which
    /// use the same values.
    pub is_locale_fallback_enabled: bool,
}

impl<'a> ResourceBundle<'a> {
    /// Makes a new resource bundle with the specified resource at its root.
    pub fn new(name: Cow<'a, str>, root: Resource<'a>, is_locale_fallback_enabled: bool) -> Self {
        Self {
            name,
            root,
            is_locale_fallback_enabled,
        }
    }

    /// Gets the name of the resource bundle.
    ///
    /// This name is used as the "key" of the root resource in a text format
    /// bundle, but is not used in building binary resource bundles.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Gets the root resource in the resource tree.
    pub fn root(&self) -> &Resource {
        &self.root
    }
}

/// A data resource within a [`ResourceBundle`].
#[derive(Debug)]
#[non_exhaustive]
pub enum Resource<'a> {
    /// A well-formed UTF-8 string.
    String(Cow<'a, str>),

    /// A heterogeneous list of resources, ordered by insertion.
    Array(Vec<Resource<'a>>),

    /// A set of key-resource pairs, sorted lexically by key.
    Table(Table<'a>),

    /// A slice of arbitrary binary data.
    Binary(Cow<'a, [u8]>),

    /// A 28-bit integer.
    ///
    /// May be interpreted as either signed or unsigned depending on consumer
    /// expectations. See [`Int28`] for further details.
    Integer(Int28),

    /// A list of 32-bit integers, ordered by insertion.
    IntVector(Vec<u32>),
}

/// A table of [`Resource`]s indexed by a string-based [`Key`].
// Ordering of keys in a table is significant, so `HashMap` isn't appropriate
// here.
pub type Table<'a> = BTreeMap<Key<'a>, Resource<'a>>;

/// A key for a [`Resource`] within a [`Table`].
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Key<'a>(Cow<'a, str>);

impl Key<'_> {
    /// Converts the string representing the key into a slice of UTF-8 bytes.
    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }
}

impl<'a> From<&'a str> for Key<'a> {
    fn from(value: &'a str) -> Self {
        Self(Cow::from(value))
    }
}

impl From<String> for Key<'_> {
    fn from(value: String) -> Self {
        Self(Cow::from(value))
    }
}

impl<'a> From<Key<'a>> for String {
    fn from(value: Key<'a>) -> Self {
        value.0.into_owned()
    }
}

/// A 28-bit integer of undetermined signedness.
///
/// [`Resource`]s may include 28-bit integers whose signedness is determined at
/// runtime by consumers. Because these integers are stored in a 32-bit value,
/// negative values in signed integers require special handling, provided by
/// this newtype wrapper.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Int28(u32);

impl From<Int28> for i32 {
    fn from(value: Int28) -> Self {
        ((value.0 as i32) << 4) >> 4
    }
}

impl From<Int28> for u32 {
    fn from(value: Int28) -> Self {
        value.0
    }
}

impl From<i32> for Int28 {
    fn from(value: i32) -> Self {
        Self::from(value as u32)
    }
}

impl From<u32> for Int28 {
    fn from(value: u32) -> Self {
        Self(value & MASK_28_BIT)
    }
}