gs1/epc/
sgtin.rs

1//! Serialised Global Trade Item Number
2//!
3//! This is a combination of a GTIN and a serial number which allows an item to be uniquely
4//! identified.
5use crate::epc::{EPCValue, EPC};
6use crate::error::{ParseError, Result};
7use crate::util::{extract_indicator, read_string, uri_encode, zero_pad};
8use crate::{ApplicationIdentifier, GS1, GTIN};
9use bitreader::BitReader;
10
11/// 96-bit Serialised Global Trade Item Number
12///
13/// This comprises a GTIN, a filter value (which is used by RFID readers), and a numeric serial
14/// number.
15#[derive(PartialEq, Debug)]
16pub struct SGTIN96 {
17    /// Filter value to allow RFID readers to select the type of tag to read.
18    pub filter: u8,
19    /// Global Trade Item Number
20    pub gtin: GTIN,
21    /// Item serial number
22    pub serial: u64,
23}
24
25impl EPC for SGTIN96 {
26    // GS1 EPC TDS section 6.3.1
27    fn to_uri(&self) -> String {
28        format!(
29            "urn:epc:id:sgtin:{}.{}{}.{}",
30            zero_pad(self.gtin.company.to_string(), self.gtin.company_digits),
31            self.gtin.indicator,
32            zero_pad(self.gtin.item.to_string(), 12 - self.gtin.company_digits),
33            self.serial
34        )
35    }
36
37    fn to_tag_uri(&self) -> String {
38        format!(
39            "urn:epc:tag:sgtin-96:{}.{}.{}{}.{}",
40            self.filter,
41            zero_pad(self.gtin.company.to_string(), self.gtin.company_digits),
42            self.gtin.indicator,
43            zero_pad(self.gtin.item.to_string(), 12 - self.gtin.company_digits),
44            self.serial
45        )
46    }
47
48    fn get_value(&self) -> EPCValue {
49        EPCValue::SGTIN96(self)
50    }
51}
52
53impl GS1 for SGTIN96 {
54    fn to_gs1(&self) -> String {
55        let gtin_gs1 = self.gtin.to_gs1();
56        format!(
57            "{} ({:0>2}) {}",
58            gtin_gs1,
59            ApplicationIdentifier::SerialNumber as u16,
60            self.serial
61        )
62    }
63}
64
65/// 198-bit Serialised Global Trade Item Number
66///
67/// This comprises a GTIN, a filter value (which is used by RFID readers), and an
68/// alphanumeric serial number which is encoded using 7-bit ASCII.
69#[derive(PartialEq, Debug)]
70pub struct SGTIN198 {
71    /// Filter value to allow RFID readers to select tags to read
72    pub filter: u8,
73    /// Global Trade Item Number
74    pub gtin: GTIN,
75    /// Alphanumeric serial number
76    pub serial: String,
77}
78
79impl EPC for SGTIN198 {
80    // GS1 EPC TDS section 6.3.1
81    fn to_uri(&self) -> String {
82        format!(
83            "urn:epc:id:sgtin:{}.{}{}.{}",
84            zero_pad(self.gtin.company.to_string(), self.gtin.company_digits),
85            self.gtin.indicator,
86            zero_pad(self.gtin.item.to_string(), 12 - self.gtin.company_digits),
87            uri_encode(self.serial.to_string())
88        )
89    }
90
91    fn to_tag_uri(&self) -> String {
92        format!(
93            "urn:epc:tag:sgtin-198:{}.{}.{}{}.{}",
94            self.filter,
95            zero_pad(self.gtin.company.to_string(), self.gtin.company_digits),
96            self.gtin.indicator,
97            zero_pad(self.gtin.item.to_string(), 12 - self.gtin.company_digits),
98            uri_encode(self.serial.to_string())
99        )
100    }
101
102    fn get_value(&self) -> EPCValue {
103        EPCValue::SGTIN198(self)
104    }
105}
106
107impl GS1 for SGTIN198 {
108    fn to_gs1(&self) -> String {
109        let gtin_gs1 = self.gtin.to_gs1();
110        format!(
111            "{} ({:0>2}) {}",
112            gtin_gs1,
113            ApplicationIdentifier::SerialNumber as u16,
114            self.serial
115        )
116    }
117}
118
119// Calculate the number of digits in the decimal representation of a SGTIN
120// company code from the partition ID.
121// GS1 EPC TDS Table 14-2
122fn company_digits(partition: u8) -> usize {
123    12 - partition as usize
124}
125
126fn item_digits(partition: u8) -> usize {
127    13 - company_digits(partition)
128}
129
130// GS1 EPC TDS Table 14-2
131fn partition_bits(partition: u8) -> Result<(u8, u8)> {
132    Ok(match partition {
133        0 => (40, 4),
134        1 => (37, 7),
135        2 => (34, 10),
136        3 => (30, 14),
137        4 => (27, 17),
138        5 => (24, 20),
139        6 => (20, 24),
140        _ => {
141            return Err(Box::new(ParseError()));
142        }
143    })
144}
145
146// GS1 EPC TDC Section 14.5.1
147pub(super) fn decode_sgtin96(data: &[u8]) -> Result<Box<dyn EPC>> {
148    let mut reader = BitReader::new(data);
149
150    let filter = reader.read_u8(3)?;
151    let partition = reader.read_u8(3)?;
152    let (company_bits, item_bits) = partition_bits(partition)?;
153    let company = reader.read_u64(company_bits)?;
154    let item = reader.read_u64(item_bits)?;
155    let (item, indicator) = extract_indicator(item, item_digits(partition))?;
156    let serial = reader.read_u64(38)?;
157
158    Ok(Box::new(SGTIN96 {
159        filter,
160        gtin: GTIN {
161            company,
162            company_digits: company_digits(partition),
163            item,
164            indicator,
165        },
166        serial,
167    }))
168}
169
170// GS1 EPC TDC Section 14.5.1.2
171pub(super) fn decode_sgtin198(data: &[u8]) -> Result<Box<dyn EPC>> {
172    let mut reader = BitReader::new(data);
173
174    let filter = reader.read_u8(3)?;
175    let partition = reader.read_u8(3)?;
176    let (company_bits, item_bits) = partition_bits(partition)?;
177    let company = reader.read_u64(company_bits)?;
178    let item = reader.read_u64(item_bits)?;
179    let (item, indicator) = extract_indicator(item, item_digits(partition))?;
180    let serial = read_string(reader, 140)?;
181
182    Ok(Box::new(SGTIN198 {
183        filter,
184        gtin: GTIN {
185            company,
186            company_digits: company_digits(partition),
187            item,
188            indicator,
189        },
190        serial,
191    }))
192}