#[cfg(not(feature = "std"))]
use alloc::{format, string::String};
use alloc::string::FromUtf16Error;
use crate::{path::*, Codepage, FSResult, FileSystem, Sfn, SFN_EXT_LEN, SFN_NAME_LEN};
use embedded_io::*;
pub(crate) fn string_from_lfn(utf16_src: &[u16]) -> Result<String, FromUtf16Error> {
let nul_range_end = utf16_src
.iter()
.position(|c| *c == 0x0000)
.unwrap_or(utf16_src.len());
String::from_utf16(&utf16_src[..nul_range_end])
}
pub(crate) fn as_sfn(string: &str, codepage: &Codepage) -> Option<Sfn> {
if !string.chars().all(|c| codepage.contains(c)) {
return None;
}
if string.len() > SFN_NAME_LEN + 1 + SFN_EXT_LEN {
return None;
}
if !string
.chars()
.filter(|c| c.is_alphabetic())
.all(|c| c.is_uppercase())
{
return None;
}
let (name, ext) = string.split_once('.').unwrap_or((string, ""));
if name.len() > SFN_NAME_LEN || ext.len() > SFN_EXT_LEN {
return None;
}
let (name, ext) = (format!("{name:<8}"), format!("{ext:<3}"));
Some(Sfn {
name: name.as_bytes().try_into().unwrap(),
ext: ext.as_bytes().try_into().unwrap(),
})
}
#[derive(Debug)]
struct SfnGenerator {
name: String,
ext: [u8; 3],
index: usize,
next_pow_of_ten: usize,
}
impl SfnGenerator {
fn new(string: &str, codepage: &Codepage) -> Self {
let (name, ext) = Self::_sfn_name_ext_from_string(string, codepage);
Self {
name,
ext: ext.as_bytes().try_into().unwrap(),
index: 1,
next_pow_of_ten: 10,
}
}
fn _sfn_name_ext_from_string(string: &str, codepage: &Codepage) -> (String, String) {
let ascii_string: String = string.chars().filter(|c| codepage.contains(*c)).collect();
let (mut name, mut ext) = ascii_string
.rsplit_once('.')
.map(|(n, e)| (n.replace(".", ""), String::from(e)))
.unwrap_or((ascii_string, String::new()));
(name, ext) = (
name.chars().take(SFN_NAME_LEN - 2).collect::<String>(),
ext.chars().take(SFN_EXT_LEN).collect::<String>(),
);
(name, ext) = (name.to_ascii_uppercase(), ext.to_ascii_uppercase());
(format!("{name:<6}"), format!("{ext:<3}"))
}
fn reduce_name_by_a_char(&mut self) -> bool {
if self.name.is_empty() {
return false;
}
self.name.truncate(self.name.len() - 1);
true
}
}
impl Iterator for SfnGenerator {
type Item = Sfn;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.next_pow_of_ten {
if !self.reduce_name_by_a_char() {
return None;
}
self.next_pow_of_ten *= 10;
}
let sfn = Sfn {
name: format!("{}~{}", self.name, self.index)
.as_bytes()
.try_into()
.unwrap(),
ext: self.ext,
};
self.index += 1;
Some(sfn)
}
}
pub(crate) fn gen_sfn<S, P>(
string: &str,
fs: &FileSystem<S>,
target_dir: P,
) -> FSResult<Sfn, S::Error>
where
S: Read + Write + Seek,
P: AsRef<Path>,
{
'outer: {
if let Some(sfn) = as_sfn(string, &fs.options.codepage) {
for entry in fs.process_current_dir() {
let entry = entry?;
if entry.sfn == sfn {
break 'outer;
}
}
return Ok(sfn);
}
}
let generator = SfnGenerator::new(string, &fs.options.codepage);
'outer: for sfn in generator {
for entry in fs.read_dir(&target_dir)? {
let entry = entry?;
if entry.sfn.0 == sfn {
continue 'outer;
}
}
return Ok(sfn);
}
unreachable!(concat!(
"the FAT32 file limit per directory is 2^16 (~65 hundred) files, and this generator ",
"can theoretically generate 10^9 - 1 (1 billion minus one) unique short filenames"
))
}