use std::collections::HashMap;
const GSM_BASIC_CHARSET: &str = "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\x1bÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑÜ`¿abcdefghijklmnopqrstuvwxyzäöñüà";
pub fn gsm_7bit_encode(text: &str) -> Result<Vec<u8>, String> {
let mut encoded_text = Vec::new();
let mut gsm_extended_charset = HashMap::new();
gsm_extended_charset.insert('^', 20);
gsm_extended_charset.insert('{', 40);
gsm_extended_charset.insert('}', 41);
gsm_extended_charset.insert('\\', 47);
gsm_extended_charset.insert('[', 60);
gsm_extended_charset.insert('~', 61);
gsm_extended_charset.insert(']', 62);
gsm_extended_charset.insert('|', 64);
gsm_extended_charset.insert('€', 101);
for char in text.chars() {
if let Some(index) = GSM_BASIC_CHARSET.chars().position(|c| c == char) {
encoded_text.push(index as u8);
} else if let Some(&code) = gsm_extended_charset.get(&char) {
encoded_text.push(0x1B);
encoded_text.push(code);
} else {
return Err(format!("Character '{}' not supported in GSM 03.38", char));
}
}
Ok(encoded_text)
}
pub fn encode_8bit(text: &str) -> Vec<u8> {
text.chars()
.map(|c| if (c as u32) <= 0xFF { c as u8 } else { b'?' })
.collect()
}
pub fn encode_16bit(text: &str) -> Vec<u8> {
text.encode_utf16().flat_map(|u| u.to_be_bytes()).collect()
}
pub fn gsm_7bit_decode(bytes: &[u8]) -> String {
let basic_chars: Vec<char> = GSM_BASIC_CHARSET.chars().collect();
let mut result = String::new();
let mut i = 0;
while i < bytes.len() {
let byte = bytes[i];
if byte == 0x1B {
if i + 1 < bytes.len() {
let next_byte = bytes[i + 1];
let decoded_char = match next_byte {
20 => '^',
40 => '{',
41 => '}',
47 => '\\',
60 => '[',
61 => '~',
62 => ']',
64 => '|',
101 => '€',
_ => '?', };
result.push(decoded_char);
i += 2; } else {
i += 1; }
} else {
if (byte as usize) < basic_chars.len() {
result.push(basic_chars[byte as usize]);
} else {
result.push('?');
}
i += 1;
}
}
result
}
pub fn decode_8bit(bytes: &[u8]) -> String {
bytes.iter().map(|&b| b as char).collect()
}
pub fn decode_16bit(bytes: &[u8]) -> String {
let u16_vec: Vec<u16> = bytes
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
.collect();
String::from_utf16_lossy(&u16_vec)
}
#[derive(Debug, PartialEq, Clone)]
pub enum MessageBody {
Text(String), Binary(Vec<u8>), }
#[derive(Debug, Clone, Copy)]
enum RawEncoding {
Gsm7Bit,
Latin1,
Ucs2,
Binary8Bit, }
fn detect_raw_encoding(dcs: u8) -> RawEncoding {
match dcs {
0x00 | 0x01 => RawEncoding::Gsm7Bit,
0x03 => RawEncoding::Latin1, 0x08 => RawEncoding::Ucs2,
0x02 | 0x04 => RawEncoding::Binary8Bit,
_ => {
let group = dcs >> 4;
match group {
0x00..=0x03 => {
match (dcs & 0x0C) >> 2 {
0x02 => RawEncoding::Ucs2,
0x01 => RawEncoding::Binary8Bit, _ => RawEncoding::Gsm7Bit,
}
}
0x0F => {
if (dcs & 0x04) != 0 {
RawEncoding::Binary8Bit } else {
RawEncoding::Gsm7Bit
}
}
_ => RawEncoding::Binary8Bit, }
}
}
}
pub fn process_body(body: &[u8], dcs: u8, udhi: bool) -> MessageBody {
let payload = if udhi && !body.is_empty() {
let udh_len = body[0] as usize;
if body.len() > udh_len + 1 {
&body[udh_len + 1..]
} else {
return MessageBody::Binary(body.to_vec());
}
} else {
body
};
match detect_raw_encoding(dcs) {
RawEncoding::Gsm7Bit => MessageBody::Text(gsm_7bit_decode(payload)),
RawEncoding::Latin1 => MessageBody::Text(decode_8bit(payload)),
RawEncoding::Ucs2 => MessageBody::Text(decode_16bit(payload)),
RawEncoding::Binary8Bit => MessageBody::Binary(payload.to_vec()),
}
}