fixedstr 0.4.6

strings of constant maximum size that can be copied and stack allocated using const generics
Documentation
//! Library for fixed-sized strings using const generics.  Rust will
//! probably provide something equivalent and more full-featured but
//! I can't wait...

// Fixed, :Copy strings of constant maximum size.  Size of each fstr is
// N or less.  The actual length of the string (<= N) is stored in a
// field inside the struct for quick access.

#[derive(Copy,Clone,Debug,Eq,PartialEq,Hash)]
pub struct fstr<const N:usize>
{
  chrs : [char;N],
  len : usize  // length will be <=N
}//fstr
impl<const N:usize> fstr<N>
{
  pub fn new(s:&str) -> fstr<N>
   {
      let mut chars = ['\0'; N];
      let mut i = 0;
      for c in s.chars()
      {
         if i<N { chars[i] = c; i+=1; } else {break;}
      }
      fstr {
         chrs: chars,
         len: i,
      }
   }//new

   /// returns the length of the string, which will be less than or
   /// equal to the maximum size.  This operation runs in O(1) time.
   pub fn len(&self)->usize { self.len }

   pub fn to_string(&self) -> String
   {
     self.chrs[0..self.len].iter().collect()
   }

   /// this will allow non-mutable access to the chars underneath
   pub fn get<'t>(&'t self) -> &'t [char;N] {&self.chrs}

   /// changes a char to c if i is less than the length of the string.
   /// returns true if change was successful.
   pub fn set(&mut self,i:usize, c:char) -> bool
   {
      if i<self.len {self.chrs[i]=c; true} else {false}
   }
   /// adds chars to end of current string up to maximum size N of fstr<N>,
   /// returns true if all chars in s were added, false if some were ignored
   pub fn push(&mut self,s:&str) -> bool
   {
      let mut i = self.len;
      for c in s.chars()
      {
         if i<N {self.chrs[i] = c; i+=1;} else {self.len=N; return false;}
      }
      self.len = i;
      true
   }

   /// returns the nth char of the fstr
   pub fn nth(&self,n:usize) -> Option<char>
   {
      if n<self.len {Some(self.chrs[n])} else {None}
   }
}//impl fstr<N>

impl<const N:usize> std::convert::From<String> for fstr<N>
{
  fn from(s:String) -> fstr<N>
  {
     fstr::<N>::new(&s[..])
  }
}

impl<const M:usize> fstr<M>
{
  pub fn resize<const N:usize>(&self) -> fstr<N>
  {
     let length = if (self.len<N) {self.len} else {N};
     let mut chars = ['\0';N];
     for i in 0..length {chars[i] = self.chrs[i];}
     fstr {
       chrs: chars,
       len: length,
     }
  }//resize
}

impl<const N:usize> std::convert::From<&str> for fstr<N>
{
  fn from(s:&str) -> fstr<N>
  {
     fstr::<N>::new(s)
  }
}

pub struct fstriter<const N:usize>
{
   fs : fstr<N>,
   i : usize,
}
impl<const N:usize> Iterator for fstriter<N>
{
   type Item = char;
   fn next(&mut self) -> Option<char>
   {
      if self.i<self.fs.len {self.i+=1; Some(self.fs.chrs[self.i-1])} else {None}
   }
}
impl<const N:usize> IntoIterator for fstr<N>
{
  type Item = char;
  type IntoIter = fstriter<N>;
  fn into_iter(self) -> fstriter<N>
  {
     fstriter {
       fs : self,
       i : 0,
     }
  }
}//IntoIterator

impl<const N:usize> std::fmt::Display for fstr<N>
{
  fn fmt(&self,f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result
  {
     write!(f,"{}",self.to_string())
  }
}

impl<const N:usize> PartialEq<&str> for fstr<N>
{
  fn eq(&self, other:&&str) -> bool
  {
     self==other   // see below
  }//eq
}
impl<const N:usize> PartialEq<&str> for &fstr<N>
{
  fn eq(&self, other:&&str) -> bool
  {
     if other.len()!=self.len {return false;}
     let mut i = 0;
     for c in other.chars()
     {
        if c!=self.chrs[i] {return false;}
        i +=1;
     }
     return true;
  }//eq
}
impl<'t, const N:usize> PartialEq<fstr<N>> for &'t str
{
  fn eq(&self, other:&fstr<N>) -> bool
  { other==self }
}
impl<'t, const N:usize> PartialEq<&fstr<N>> for &'t str
{
  fn eq(&self, other:&&fstr<N>) -> bool
  { other==self }
}


///Convert fstr to slice
impl<IndexType,const N:usize> std::ops::Index<IndexType> for fstr<N>
  where IndexType:std::slice::SliceIndex<[char]>,
{
  type Output = IndexType::Output;
  fn index(&self, index:IndexType)-> &Self::Output
  {
     &self.chrs[index]
  }
}//impl Index
// couldn't get it to work properly, [char] is not same as &str
// because there's no allocated string!

impl<const N:usize> fstr<N>
{
  /// returns a copy of the portion of the string, string could be truncated
  /// if indices are out of range. Similar to slice [start..end]
  pub fn substr(&self,start:usize, end:usize) -> fstr<N>
  {
    let mut chars = ['\0';N];
    if start>=self.len { return fstr{chrs:chars, len:0}; }
    let mut i = start;
    while i<end && i<self.len
    {
       chars[i-start] = self.chrs[i];
       i += 1;
    }
    fstr { chrs: chars, len:i }
  }//substr
}


pub type str8 = fstr<8>; // a type for small strings
pub type str16 = fstr<16>; 
pub type str32 = fstr<32>; 
pub type str64 = fstr<64>; 
pub type str128 = fstr<128>;