gs1/epc/
sscc.rs

1//! Serial Shipping Container Code
2use crate::checksum::gs1_checksum;
3use crate::epc::{EPCValue, EPC};
4use crate::error::{ParseError, Result};
5use crate::util::{extract_indicator, zero_pad};
6use crate::{ApplicationIdentifier, GS1};
7use bitreader::BitReader;
8
9/// 96-bit Serial Shipping Container Code
10#[derive(PartialEq, Debug)]
11pub struct SSCC96 {
12    /// Filter value to allow RFID readers to select the type of tag to read.
13    pub filter: u8,
14    pub partition: u8,
15    pub indicator: u8,
16    pub company: u64,
17    pub serial: u64,
18}
19
20impl EPC for SSCC96 {
21    // GS1 EPC TDS section 6.3.1
22    fn to_uri(&self) -> String {
23        format!(
24            "urn:epc:id:sscc:{}.{}{}",
25            zero_pad(self.company.to_string(), company_digits(self.partition)),
26            self.indicator,
27            zero_pad(self.serial.to_string(), item_digits(self.partition) - 1)
28        )
29    }
30
31    fn to_tag_uri(&self) -> String {
32        format!(
33            "urn:epc:tag:sscc-96:{}.{}.{}{}",
34            self.filter,
35            zero_pad(self.company.to_string(), company_digits(self.partition)),
36            self.indicator,
37            zero_pad(self.serial.to_string(), item_digits(self.partition) - 1)
38        )
39    }
40
41    fn get_value(&self) -> EPCValue {
42        EPCValue::SSCC96(self)
43    }
44}
45
46impl GS1 for SSCC96 {
47    fn to_gs1(&self) -> String {
48        let element_string = format!(
49            "{}{}{}",
50            self.indicator,
51            zero_pad(self.company.to_string(), company_digits(self.partition)),
52            zero_pad(self.serial.to_string(), item_digits(self.partition) - 1)
53        );
54        format!(
55            "({:0>2}) {}{}",
56            ApplicationIdentifier::SSCC as u16,
57            element_string,
58            gs1_checksum(&element_string)
59        )
60    }
61}
62
63// Calculate the number of digits in the decimal representation of a SGTIN
64// company code from the partition ID.
65// GS1 EPC TDS Table 14-5
66fn company_digits(partition: u8) -> usize {
67    12 - partition as usize
68}
69
70fn item_digits(partition: u8) -> usize {
71    17 - company_digits(partition)
72}
73
74// GS1 EPC TDS Table 14-5
75fn partition_bits(partition: u8) -> Result<(u8, u8)> {
76    Ok(match partition {
77        0 => (40, 18),
78        1 => (37, 21),
79        2 => (34, 24),
80        3 => (30, 28),
81        4 => (27, 31),
82        5 => (24, 34),
83        6 => (20, 48),
84        _ => {
85            return Err(Box::new(ParseError()));
86        }
87    })
88}
89
90// GS1 EPC TDC Section 14.5.2
91pub(super) fn decode_sscc96(data: &[u8]) -> Result<Box<dyn EPC>> {
92    let mut reader = BitReader::new(data);
93
94    let filter = reader.read_u8(3)?;
95    let partition = reader.read_u8(3)?;
96    let (company_bits, serial_bits) = partition_bits(partition)?;
97    let company = reader.read_u64(company_bits)?;
98    let serial = reader.read_u64(serial_bits)?;
99    let (serial, indicator) = extract_indicator(serial, item_digits(partition))?;
100
101    Ok(Box::new(SSCC96 {
102        filter,
103        partition,
104        indicator,
105        company,
106        serial,
107    }))
108}