<commit_before>// ! Spit string with a delimeter.

// xxx

pub( crate ) mod internal
{

  /* xxx : qqq : tab after git sync */

  // use woptions::*; /* xxx : use prelude */
  use crate::string::parse::OpType;

  ///
  /// Either delimeter or delimeted with the slice on its string.
  ///

  #[allow(dead_code)]
  #[ derive( Debug ) ]
  pub struct Split< 'a >
  {
    string : &'a str,
    typ : SplitType,
  }

  impl< 'a > From< Split< 'a > > for String
  {
    fn from( src : Split ) -> Self
    {
      src.string.into()
    }
  }

  ///
  /// Either delimeter or delimeted
  ///

  #[ derive( Debug ) ]
  pub enum SplitType
  {
    /// Substring of the original string with text inbetween delimeters.
    Delimeted,
    /// Delimeter.
    Delimeter,
  }

  ///
  /// Find first match in the string.
  ///

  pub trait Searcher
  {
    /// Find positions of delimeter.
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >;
  }

  impl Searcher for &str
  {
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >
    {
      src.find( self ).map( | start | ( start, start + self.len() ) )
    }
  }

  impl Searcher for String
  {
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >
    {
      src.find( self ).map( | start | ( start, start + self.len() ) )
    }
  }

  impl Searcher for Vec<&str>
  {
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >
    {
      let mut r = vec![];
      for pat in self
      {
        match src.find( pat )
        {
          Some( x ) => r.push( ( x, x + pat.len() ) ),
          None => (),
        }
      }

      if r.is_empty()
      {
        return None;
      }

      r.into_iter().reduce( | accum, item |
      {
        if accum.0 > item.0
        {
          item
        }
        else
        {
          if accum.1 > item.1
          {
            item
          }
          else
          {
            accum
          }
        }
      })
    }
  }

  ///
  /// Split iterator.
  ///

  #[ derive( Debug ) ]
  pub struct SplitFastIterator< 'a, D >
  where
    D : Searcher
  {
    iterable : &'a str,
    counter : i32,
    delimeter : D,
    preserving_empty : bool,
    preserving_delimeters : bool,
    stop_empty : bool,
  }

  //

  impl< 'a, D : Searcher + Clone > SplitFastIterator< 'a, D >
  {
    #[ allow( dead_code ) ]
    fn new( o : impl SplitOptionsAdapter< 'a, D > ) -> Self
    {
      Self
      {
        iterable : o.src(),
        delimeter : o.delimeter(),
        counter : 0,
        preserving_empty : o.preserving_empty(),
        preserving_delimeters : o.preserving_delimeters(),
        stop_empty : false,
      }
    }
  }

  //

  impl< 'a, D > Iterator for SplitFastIterator< 'a, D >
  where
    D : Searcher
  {
    type Item = Split< 'a >;

    fn next( &mut self ) -> Option< Self::Item >
    {
      self.counter += 1;

      if self.counter % 2 == 1
      {
        let positions = self.delimeter.pos( self.iterable );
        if let Some( ( mut start, end ) ) = positions
        {
          if self.iterable == "" && start == end
          {
            if self.stop_empty
            {
              return None;
            }
            else
            {
              self.counter -= 1;
              self.stop_empty = true;
              return Some( Split { string : "", typ : SplitType::Delimeted } );
            }
          }
          let mut next = &self.iterable[ ..start ];
          if start == end
          {
            if self.counter >= 3
            {
              next = &self.iterable[ ..start + 1 ];
              start += 1;
            }
          }

          self.iterable = &self.iterable[ start.. ];

          Some( Split { string : next, typ : SplitType::Delimeted } )
        }
        else
        {
          return self.next_end_split();
        }
      }
      else
      {
        if self.delimeter.pos( self.iterable ).is_none()
        {
          self.iterable = "";
          return None;
        }

        let ( start, end ) = self.delimeter.pos( self.iterable ).unwrap();
        let string = &self.iterable[ start..end ];
        self.iterable = &self.iterable[ end.. ];

        if self.preserving_delimeters
        {
          return Some( Split { string, typ : SplitType::Delimeter } );
        }
        else
        {
          return self.next_odd_split();
        }
      }
    }
  }

  impl< 'a, D > SplitFastIterator< 'a, D >
  where
    D : Searcher
  {
    fn next_end_split( &mut self ) -> Option< Split< 'a > >
    {
      if self.iterable == ""
      {
        return None;
      }
      else
      {
        let r = Split { string : self.iterable, typ : SplitType::Delimeted };
        self.iterable = "";
        return Some( r );
      }
    }

    fn next_odd_split( &mut self ) -> Option< Split< 'a > >
    {
      match self.delimeter.pos( self.iterable )
      {
        Some( ( start, mut end ) ) =>
        {
          let mut string = &self.iterable[ ..start ];

          if start == end
          {
            string = &self.iterable[ ..start + 1 ];
            end += 1;
          }
          self.iterable = &self.iterable[ end.. ];
          return Some( Split { string, typ : SplitType::Delimeted } );
        },
        None =>
        {
          self.next_end_split()
        },
      }
    }
  }

  ///
  /// Split iterator.
  ///

  #[ derive( Debug ) ]
  pub struct SplitIterator< 'a >
  {
    iterator : SplitFastIterator< 'a, Vec< &'a str > >,
    counter : usize,
    stripping : bool,
  }

  //

  impl< 'a > SplitIterator< 'a >
  {
    fn new( o : impl SplitOptionsAdapter< 'a, Vec< &'a str > > ) -> Self
    {
      let iterator;
      if !o.stripping() && !o.quoting() /* && !onDelimeter */
      {
        iterator = SplitFastIterator
        {
          iterable : o.src(),
          delimeter : o.delimeter(),
          counter : 0,
          preserving_empty : o.preserving_empty(),
          preserving_delimeters : o.preserving_delimeters(),
          stop_empty : false,
        };
      }
      else
      {
        let mut delimeter;
        if o.quoting()
        {
          delimeter = vec![ "\"", "`", "'" ];
          delimeter.extend( vec![ "\"", "`", "'" ] );
          delimeter.extend( o.delimeter() );
        }
        else
        {
          delimeter = o.delimeter();
        }

        iterator = SplitFastIterator
        {
          iterable : o.src(),
          delimeter,
          counter : 0,
          preserving_empty : o.preserving_empty(),
          preserving_delimeters : o.preserving_delimeters(),
          // preserving_empty : true,
          // preserving_delimeters : true,
          stop_empty : false,
        };
      }

      Self
      {
        iterator,
        counter : 0,
        stripping : o.stripping(),
      }
    }
  }

  impl< 'a > Iterator for SplitIterator< 'a >
  {
    type Item = Split< 'a >;

    fn next( &mut self ) -> Option< Self::Item >
    {
      self.counter += 1;

      if let Some( mut split ) = self.iterator.next()
      {
        if self.stripping
        {
          split.string = split.string.trim();
          if !self.iterator.preserving_empty && split.string.is_empty() && self.counter % 2 == 0
          {
            self.counter += 1;
            return self.iterator.next();
          }
        }
        Some( split )
      }
      else
      {
        None
      }
    }
  }

  ///
  /// Options of function split.
  ///

  #[ derive( Debug ) ]
  pub struct SplitOptions< 'a, D >
  where
    D : Searcher + Default + Clone,
  {
    src : &'a str,
    delimeter : D,
    preserving_empty : bool,
    preserving_delimeters : bool,
    stripping : bool,
    quoting : bool,
  }

  impl< 'a > SplitOptions< 'a, Vec< &'a str > >
  {
    /// Produces SplitIterator.
    pub fn split( self ) -> SplitIterator< 'a >
    where
      Self : Sized,
    {
      SplitIterator::new( self )
    }
  }

  impl< 'a, D > SplitOptions< 'a, D >
  where
    D : Searcher + Default + Clone
  {
    /// Produces SplitFastIterator.
    pub fn split_fast( self ) -> SplitFastIterator< 'a, D >
    where
      Self : Sized,
    {
      SplitFastIterator::new( self )
    }
  }

  ///
  /// Adapter for Split Options.
  ///

  pub trait SplitOptionsAdapter< 'a, D >
  where
    D : Clone
  {
    /// A string to split.
    fn src( &self ) -> &'a str;
    /// A delimeter to split string.
    fn delimeter( &self ) -> D;
    /// Preserving or dropping empty splits.
    fn preserving_empty( &self ) -> bool;
    /// Preserving or dropping delimeters.
    fn preserving_delimeters( &self ) -> bool;
    /// Stripping.
    fn stripping( &self ) -> bool;
    /// Quoting.
    fn quoting( &self ) -> bool;
  }

  //

  impl< 'a, D : Searcher + Clone + Default > SplitOptionsAdapter< 'a, D > for SplitOptions< 'a, D >
  {
    fn src( &self ) -> &'a str
    {
      self.src
    }
    fn delimeter( &self ) -> D
    {
      self.delimeter.clone()
    }
    fn preserving_empty( &self ) -> bool
    {
      self.preserving_empty
    }
    fn preserving_delimeters( &self ) -> bool
    {
      self.preserving_delimeters
    }
    fn stripping( &self ) -> bool
    {
      self.stripping
    }
    fn quoting( &self ) -> bool
    {
      self.quoting
    }
  }

  //

  macro_rules! builder_impls_from
  {
    ( $name : ident, $( ( $field : ident, $type : ty ) ),* $( , )? ) =>
    {
      impl< 'a > $name< 'a >
      {
        $(
          pub fn $field( &mut self, value : $type ) -> &mut $name< 'a >
          {
            self.$field = value;
            self
          }
        )*

        pub fn form( &mut self ) -> SplitOptions< 'a, Vec< &'a str > >
        {
          SplitOptions
          {
            src : self.src,
            delimeter : self.delimeter.clone().vector().unwrap(),
            preserving_empty : self.preserving_empty,
            preserving_delimeters : self.preserving_delimeters,
            stripping : self.stripping,
            quoting : self.quoting,
          }
        }
      }
    }
  }

  ///
  /// Former for SplitOptions.
  ///

  #[ derive( Debug ) ]
  pub struct SplitOptionsFormer< 'a >
  {
    src : &'a str,
    delimeter : OpType< &'a str >,
    preserving_empty : bool,
    preserving_delimeters : bool,
    stripping : bool,
    quoting : bool,
  }
  builder_impls_from!
  (
    SplitOptionsFormer,
    ( src, &'a str ),
    ( preserving_empty, bool ),
    ( preserving_delimeters, bool ),
    ( stripping, bool ),
    ( quoting, bool ),
  );

  impl< 'a > SplitOptionsFormer< 'a >
  {
    pub fn new< D : Into< OpType< &'a str > > >( delimeter : D ) -> SplitOptionsFormer< 'a >
    {
      let op_vec : OpType<&'a str> = OpType::Vector( vec![] );
      Self
      {
        src : "",
        delimeter : op_vec.append( delimeter.into() ),
        preserving_empty : true,
        preserving_delimeters : true,
        stripping : true,
        quoting : false,
      }
    }

    pub fn delimeter< D : Into< OpType< &'a str > > >( &mut self, value : D ) -> &mut SplitOptionsFormer< 'a >
    {
      let op_vec : OpType<&'a str> = OpType::Vector( vec![] );
      let op : OpType<&'a str> = value.into();
      self.delimeter = op_vec.append( op );
      self
    }

    pub fn perform( &mut self ) -> SplitIterator< 'a >
    {
      let opts = self.form();
      opts.split()
    }
  }

  ///
  /// Function to split a string.
  ///
  /// It produces former. To convert former into options and run algorithm of splitting call `form()`.
  ///
  /// # Sample
  /// ```
  ///   let iter = wstring_tools::string::split()
  ///   .src( "abc def" )
  ///   .delimeter( " " )
  ///   .perform();
  /// ```

  pub fn split< 'a >() -> SplitOptionsFormer< 'a >
  {
    SplitOptionsFormer::new( < &str >::default() )
  }
}

/// Owned namespace of the module.
pub mod own
{
  use super::internal as i;

  pub use i::Split;
  pub use i::SplitType;
  pub use i::SplitFastIterator;
  pub use i::SplitOptions;
  pub use i::SplitOptionsAdapter;
  pub use i::split;
}

pub use own::*;

/// Exposed namespace of the module.
pub mod exposed
{
  use super::internal as i;

  pub use i::SplitOptionsAdapter;
  pub use i::split;
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  use super::internal as i;

  pub use i::SplitOptionsAdapter;
}
<commit_msg>Extend split iterator, add options `quoting`, `quoting_prefixes`, `quoting_postfixes`, add trivial realization of quoting<commit_after>// ! Spit string with a delimeter.

// xxx

pub( crate ) mod internal
{

  /* xxx : qqq : tab after git sync */

  // use woptions::*; /* xxx : use prelude */
  use crate::string::parse::OpType;

  ///
  /// Either delimeter or delimeted with the slice on its string.
  ///

  #[allow(dead_code)]
  #[ derive( Debug ) ]
  pub struct Split< 'a >
  {
    string : &'a str,
    typ : SplitType,
  }

  impl< 'a > From< Split< 'a > > for String
  {
    fn from( src : Split ) -> Self
    {
      src.string.into()
    }
  }

  ///
  /// Either delimeter or delimeted
  ///

  #[ derive( Debug ) ]
  pub enum SplitType
  {
    /// Substring of the original string with text inbetween delimeters.
    Delimeted,
    /// Delimeter.
    Delimeter,
  }

  ///
  /// Find first match in the string.
  ///

  pub trait Searcher
  {
    /// Find positions of delimeter.
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >;
  }

  impl Searcher for &str
  {
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >
    {
      src.find( self ).map( | start | ( start, start + self.len() ) )
    }
  }

  impl Searcher for String
  {
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >
    {
      src.find( self ).map( | start | ( start, start + self.len() ) )
    }
  }

  impl Searcher for Vec<&str>
  {
    fn pos( &self, src : &str ) -> Option< ( usize, usize ) >
    {
      let mut r = vec![];
      for pat in self
      {
        match src.find( pat )
        {
          Some( x ) => r.push( ( x, x + pat.len() ) ),
          None => (),
        }
      }

      if r.is_empty()
      {
        return None;
      }

      r.into_iter().reduce( | accum, item |
      {
        if accum.0 > item.0
        {
          item
        }
        else
        {
          if accum.1 > item.1
          {
            item
          }
          else
          {
            accum
          }
        }
      })
    }
  }

  ///
  /// Split iterator.
  ///

  #[ derive( Debug ) ]
  pub struct SplitFastIterator< 'a, D >
  where
    D : Searcher
  {
    iterable : &'a str,
    counter : i32,
    delimeter : D,
    preserving_empty : bool,
    preserving_delimeters : bool,
    stop_empty : bool,
  }

  //

  impl< 'a, D : Searcher + Clone > SplitFastIterator< 'a, D >
  {
    #[ allow( dead_code ) ]
    fn new( o : impl SplitOptionsAdapter< 'a, D > ) -> Self
    {
      Self
      {
        iterable : o.src(),
        delimeter : o.delimeter(),
        counter : 0,
        preserving_empty : o.preserving_empty(),
        preserving_delimeters : o.preserving_delimeters(),
        stop_empty : false,
      }
    }
  }

  //

  impl< 'a, D > Iterator for SplitFastIterator< 'a, D >
  where
    D : Searcher
  {
    type Item = Split< 'a >;

    fn next( &mut self ) -> Option< Self::Item >
    {
      self.counter += 1;

      if self.counter % 2 == 1
      {
        let positions = self.delimeter.pos( self.iterable );
        if let Some( ( mut start, end ) ) = positions
        {
          if self.iterable == "" && start == end
          {
            if self.stop_empty
            {
              return None;
            }
            else
            {
              self.counter -= 1;
              self.stop_empty = true;
              return Some( Split { string : "", typ : SplitType::Delimeted } );
            }
          }

          if start == 0 && end != 0
          {
            return self.next();
          }

          let mut next = &self.iterable[ ..start ];
          if start == end
          {
            if self.counter >= 3
            {
              next = &self.iterable[ ..start + 1 ];
              start += 1;
            }
          }

          self.iterable = &self.iterable[ start.. ];

          if !self.preserving_empty && next.is_empty()
          {
            return self.next();
          }

          Some( Split { string : next, typ : SplitType::Delimeted } )
        }
        else
        {
          if self.iterable == ""
          {
            return None;
          }
          else
          {
            let r = Split { string : self.iterable, typ : SplitType::Delimeted };
            self.iterable = "";
            return Some( r );
          }
        }
      }
      else
      {
        if self.delimeter.pos( self.iterable ).is_none()
        {
          self.iterable = "";
          return None;
        }

        let ( start, end ) = self.delimeter.pos( self.iterable ).unwrap();
        let string = &self.iterable[ start..end ];
        self.iterable = &self.iterable[ end.. ];

        if !self.preserving_empty && string.is_empty()
        {
          return self.next();
        }

        if self.preserving_delimeters
        {
          return Some( Split { string, typ : SplitType::Delimeter } );
        }
        else
        {
          return self.next();
          // return self.next_odd_split();
        }
      }
    }
  }

  ///
  /// Split iterator.
  ///

  #[ derive( Debug ) ]
  pub struct SplitIterator< 'a >
  {
    iterator : SplitFastIterator< 'a, Vec< &'a str > >,
    src : &'a str,
    stripping : bool,
    preserving_empty : bool,
    preserving_delimeters : bool,
    quoting : bool,
    quoting_prefixes : Vec< &'a str >,
    quoting_postfixes : Vec< &'a str >,
  }

  //

  impl< 'a > SplitIterator< 'a >
  {
    fn new( o : impl SplitOptionsAdapter< 'a, Vec< &'a str > > ) -> Self
    {
      let iterator;
      if !o.stripping() && !o.quoting() /* && !onDelimeter */
      {
        iterator = SplitFastIterator
        {
          iterable : o.src(),
          delimeter : o.delimeter(),
          counter : 0,
          preserving_empty : o.preserving_empty(),
          preserving_delimeters : o.preserving_delimeters(),
          stop_empty : false,
        };
      }
      else
      {
        let mut delimeter;
        if o.quoting()
        {
          delimeter = o.quoting_prefixes().clone();
          delimeter.extend( o.quoting_postfixes().clone() );
          delimeter.extend( o.delimeter() );
        }
        else
        {
          delimeter = o.delimeter();
        }

        iterator = SplitFastIterator
        {
          iterable : o.src(),
          delimeter,
          counter : 0,
          preserving_empty : true,
          preserving_delimeters : true,
          stop_empty : false,
        };
      }

      Self
      {
        iterator,
        src : o.src(),
        stripping : o.stripping(),
        preserving_empty : o.preserving_empty(),
        preserving_delimeters : o.preserving_delimeters(),
        quoting : o.quoting(),
        quoting_prefixes : o.quoting_prefixes().clone(),
        quoting_postfixes : o.quoting_postfixes().clone(),
      }
    }
  }

  impl< 'a > Iterator for SplitIterator< 'a >
  {
    type Item = Split< 'a >;

    fn next( &mut self ) -> Option< Self::Item >
    {
      if let Some( mut split ) = self.iterator.next()
      {
        if self.quoting
        {
          split = self.quoted_split( split.string );
        }

        if self.stripping
        {
          split.string = split.string.trim();
          if !self.preserving_empty && split.string.is_empty()
          {
            return self.next();
          }
        }
        else
        {
          if !self.quoting
          {
            return Some( split );
          }
        }

        if !self.preserving_delimeters
        {
          match self.iterator.delimeter.pos( split.string )
          {
            Some( ( s, e ) ) =>
            {
              if s == 0 && e == split.string.len()
              {
                return self.next();
              }
              else
              {
                return Some( split );
              }
            },
            None =>
            {
              return Some( split );
            },
          }
        }

        if !self.preserving_empty && split.string.is_empty()
        {
          return self.next();
        }

        Some( split )
      }
      else
      {
        None
      }
    }
  }

  impl< 'a > SplitIterator< 'a >
  {
    pub fn quoted_split( &mut self, split_str : &'a str ) -> Split< 'a >
    {
      match self.quoting_prefixes.iter().position( | &quote | quote == split_str )
      {
        Some( index ) =>
        {
          let postfix = self.quoting_postfixes[ index ];
          let pos = self.src.find( self.iterator.iterable ).unwrap();
          let start = pos - split_str.len();
          let end = self.iterator.iterable.find( postfix );

          if end.is_none()
          {
            self.iterator.iterable = "";
            return Split { string : &self.src[ start.. ], typ : SplitType::Delimeted };
          }
          else
          {
            let end = end.unwrap() + 1;
            while self.iterator.next().unwrap().string != postfix {}
            return Split { string : &self.src[ start..pos + end ], typ : SplitType::Delimeted };
          }
        },
        None => Split { string : split_str, typ : SplitType::Delimeted },
      }
    }
  }

  ///
  /// Options of function split.
  ///

  #[ derive( Debug ) ]
  pub struct SplitOptions< 'a, D >
  where
    D : Searcher + Default + Clone,
  {
    src : &'a str,
    delimeter : D,
    preserving_empty : bool,
    preserving_delimeters : bool,
    stripping : bool,
    quoting : bool,
    quoting_prefixes : Vec< &'a str >,
    quoting_postfixes : Vec< &'a str >,
  }

  impl< 'a > SplitOptions< 'a, Vec< &'a str > >
  {
    /// Produces SplitIterator.
    pub fn split( self ) -> SplitIterator< 'a >
    where
      Self : Sized,
    {
      SplitIterator::new( self )
    }
  }

  impl< 'a, D > SplitOptions< 'a, D >
  where
    D : Searcher + Default + Clone
  {
    /// Produces SplitFastIterator.
    pub fn split_fast( self ) -> SplitFastIterator< 'a, D >
    where
      Self : Sized,
    {
      SplitFastIterator::new( self )
    }
  }

  ///
  /// Adapter for Split Options.
  ///

  pub trait SplitOptionsAdapter< 'a, D >
  where
    D : Clone
  {
    /// A string to split.
    fn src( &self ) -> &'a str;
    /// A delimeter to split string.
    fn delimeter( &self ) -> D;
    /// Preserving or dropping empty splits.
    fn preserving_empty( &self ) -> bool;
    /// Preserving or dropping delimeters.
    fn preserving_delimeters( &self ) -> bool;
    /// Stripping.
    fn stripping( &self ) -> bool;
    /// Quoting.
    fn quoting( &self ) -> bool;
    /// Quoting prefixes.
    fn quoting_prefixes( &self ) -> &Vec< &'a str >;
    /// Quoting postfixes.
    fn quoting_postfixes( &self ) -> &Vec< &'a str >;
  }

  //

  impl< 'a, D : Searcher + Clone + Default > SplitOptionsAdapter< 'a, D > for SplitOptions< 'a, D >
  {
    fn src( &self ) -> &'a str
    {
      self.src
    }
    fn delimeter( &self ) -> D
    {
      self.delimeter.clone()
    }
    fn preserving_empty( &self ) -> bool
    {
      self.preserving_empty
    }
    fn preserving_delimeters( &self ) -> bool
    {
      self.preserving_delimeters
    }
    fn stripping( &self ) -> bool
    {
      self.stripping
    }
    fn quoting( &self ) -> bool
    {
      self.quoting
    }
    fn quoting_prefixes( &self ) -> &Vec< &'a str >
    {
      &self.quoting_prefixes
    }
    fn quoting_postfixes( &self ) -> &Vec< &'a str >
    {
      &self.quoting_postfixes
    }
  }

  //

  macro_rules! builder_impls_from
  {
    ( $name : ident, $( ( $field : ident, $type : ty ) ),* $( , )? ) =>
    {
      impl< 'a > $name< 'a >
      {
        $(
          pub fn $field( &mut self, value : $type ) -> &mut $name< 'a >
          {
            self.$field = value;
            self
          }
        )*

        pub fn form( &mut self ) -> SplitOptions< 'a, Vec< &'a str > >
        {
          if self.quoting
          {
            if self.quoting_prefixes.is_empty()
            {
              self.quoting_prefixes = vec![ "\"", "`", "'" ];
            }
            if self.quoting_postfixes.is_empty()
            {
              self.quoting_postfixes = vec![ "\"", "`", "'" ];
            }
          }
          SplitOptions
          {
            src : self.src,
            delimeter : self.delimeter.clone().vector().unwrap(),
            preserving_empty : self.preserving_empty,
            preserving_delimeters : self.preserving_delimeters,
            stripping : self.stripping,
            quoting : self.quoting,
            quoting_prefixes : self.quoting_prefixes.clone(),
            quoting_postfixes : self.quoting_postfixes.clone(),
          }
        }
      }
    }
  }

  ///
  /// Former for SplitOptions.
  ///

  #[ derive( Debug ) ]
  pub struct SplitOptionsFormer< 'a >
  {
    src : &'a str,
    delimeter : OpType< &'a str >,
    preserving_empty : bool,
    preserving_delimeters : bool,
    stripping : bool,
    quoting : bool,
    quoting_prefixes : Vec< &'a str >,
    quoting_postfixes : Vec< &'a str >,
  }
  builder_impls_from!
  (
    SplitOptionsFormer,
    ( src, &'a str ),
    ( preserving_empty, bool ),
    ( preserving_delimeters, bool ),
    ( stripping, bool ),
    ( quoting, bool ),
    ( quoting_prefixes, Vec< &'a str > ),
    ( quoting_postfixes, Vec< &'a str > ),
  );

  impl< 'a > SplitOptionsFormer< 'a >
  {
    pub fn new< D : Into< OpType< &'a str > > >( delimeter : D ) -> SplitOptionsFormer< 'a >
    {
      let op_vec : OpType<&'a str> = OpType::Vector( vec![] );
      Self
      {
        src : "",
        delimeter : op_vec.append( delimeter.into() ),
        preserving_empty : true,
        preserving_delimeters : true,
        stripping : true,
        quoting : true,
        quoting_prefixes : vec![],
        quoting_postfixes : vec![],
      }
    }

    pub fn delimeter< D : Into< OpType< &'a str > > >( &mut self, value : D ) -> &mut SplitOptionsFormer< 'a >
    {
      let op_vec : OpType<&'a str> = OpType::Vector( vec![] );
      let op : OpType<&'a str> = value.into();
      self.delimeter = op_vec.append( op );
      self
    }

    pub fn perform( &mut self ) -> SplitIterator< 'a >
    {
      let opts = self.form();
      opts.split()
    }
  }

  ///
  /// Function to split a string.
  ///
  /// It produces former. To convert former into options and run algorithm of splitting call `form()`.
  ///
  /// # Sample
  /// ```
  ///   let iter = wstring_tools::string::split()
  ///   .src( "abc def" )
  ///   .delimeter( " " )
  ///   .perform();
  /// ```

  pub fn split< 'a >() -> SplitOptionsFormer< 'a >
  {
    SplitOptionsFormer::new( < &str >::default() )
  }
}

/// Owned namespace of the module.
pub mod own
{
  use super::internal as i;

  pub use i::Split;
  pub use i::SplitType;
  pub use i::SplitFastIterator;
  pub use i::SplitOptions;
  pub use i::SplitOptionsAdapter;
  pub use i::split;
}

pub use own::*;

/// Exposed namespace of the module.
pub mod exposed
{
  use super::internal as i;

  pub use i::SplitOptionsAdapter;
  pub use i::split;
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  use super::internal as i;

  pub use i::SplitOptionsAdapter;
}
<|endoftext|>


<commit_before>#![feature(path, core, io, os, rustc_private)]

extern crate serialize;

use std::collections::HashMap;
use std::old_io::process::Command;
use std::old_io::{File, TempDir};
use std::old_io::fs;

fn main() {
    let (args, passthrough) = parse_arguments();

    // Find all the native shared libraries that exist in the target directory.
    let native_shared_libs = find_native_libs(&args);

    // getting the path from the ANDROID_HOME env
    let sdk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "ANDROID_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the ANDROID_HOME environment variable");

    // hardcoding ndk path
    let ndk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the NDK_HOME environment variable");

    // hardcoding ndk path
    let standalone_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_STANDALONE")
        .map(|(_, v)| Path::new(v)).unwrap_or(Path::new("/opt/ndk_standalone"));

    // creating the build directory that will contain all the necessary files to create teh apk
    let directory = build_directory(&sdk_path, args.output.filestem_str().unwrap(), &native_shared_libs);

    // Copy the additional native libs into the libs directory.
    for (name, path) in native_shared_libs.iter() {
        fs::copy(path, &directory.path().join("libs").join("armeabi").join(name)).unwrap();
    }

    // compiling android_native_app_glue.c
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
        .arg("-c")
        .arg("-o").arg(directory.path().join("android_native_app_glue.o"))
        .stdout(std::old_io::process::InheritFd(1)).stderr(std::old_io::process::InheritFd(2))
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling gcc to link to a shared object
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .args(passthrough.as_slice())
        .arg(directory.path().join("android_native_app_glue.o"))
        .arg("-o").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .arg("-shared")
        .arg("-Wl,-E")
        .stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2))//.cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling objdump to make sure that our object has `ANativeActivity_onCreate`
    // TODO: not working
    /*{
        let mut process =
            Command::new(standalone_path.join("bin").join("arm-linux-androideabi-objdump"))
            .arg("-x").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
            .stderr(std::old_io::process::InheritFd(2))
            .spawn().unwrap();

        // TODO: use UFCS instead
        fn by_ref<'a, T: Reader>(r: &'a mut T) -> std::old_io::RefReader<'a, T> { r.by_ref() };

        let stdout = process.stdout.as_mut().unwrap();
        let mut stdout = std::old_io::BufferedReader::new(by_ref(stdout));

        if stdout.lines().filter_map(|l| l.ok())
            .find(|line| line.as_slice().contains("ANativeActivity_onCreate")).is_none()
        {
            println!("Error: the output file doesn't contain ANativeActivity_onCreate");
            std::os::set_exit_status(1);
            return;
        }
    }*/

    // executing ant
    if Command::new("ant").arg("debug").stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2)).cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing ant debug");
        std::os::set_exit_status(1);
        return;
    }

    // copying apk file to the requested output
    fs::copy(&directory.path().join("bin").join("rust-android-debug.apk"),
        &args.output).unwrap();
}

struct Args {
    output: Path,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_passthrough = Vec::new();

    let args = std::os::args();
    let mut args = args.into_iter().skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument")
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match arg.as_slice() {
            "-o" => {
                result_output = Some(Path::new(args.next().expect("-o must be followed by the output name")));
            },
            _ => result_passthrough.push(arg)
        };
    }
}

fn find_native_libs(args: &Args) -> HashMap<String, Path> {
    let base_path = args.output.dir_path().join("native");
    let mut native_shared_libs: HashMap<String, Path> = HashMap::new();

    fs::walk_dir(&base_path).and_then(|dirs| {
        for dir in dirs {
            fs::readdir(&dir).and_then(|paths| {
                for path in paths.iter() {
                    match (path.filename_str(), path.extension_str()) {
                        (Some(filename), Some(ext)) => {
                            if filename.starts_with("lib") && ext == "so" {
                                native_shared_libs.insert(filename.to_string(), path.clone());
                            }
                        }
                        _ => {}
                    }
                }
                Ok(())
            }).ok();
        }
        Ok(())
    }).ok();

    native_shared_libs
}

fn build_directory(sdk_dir: &Path, crate_name: &str, libs: &HashMap<String, Path>) -> TempDir {
    use std::old_io::fs;

    let build_directory = TempDir::new("android-rs-glue-rust-to-apk")
        .ok().expect("Could not create temporary build directory");

    let activity_name = if libs.len() > 0 {
        let src_path = build_directory.path().join("src/rust/glutin");
        fs::mkdir_recursive(&src_path, std::old_io::USER_RWX).unwrap();

        File::create(&src_path.join("MainActivity.java")).unwrap()
            .write_str(java_src(libs).as_slice())
            .unwrap();

        "rust.glutin.MainActivity"
    } else {
        "android.app.NativeActivity"
    };

    File::create(&build_directory.path().join("AndroidManifest.xml")).unwrap()
        .write_str(build_manifest(crate_name, activity_name).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("build.xml")).unwrap()
        .write_str(build_build_xml().as_slice())
        .unwrap();

    File::create(&build_directory.path().join("local.properties")).unwrap()
        .write_str(build_local_properties(sdk_dir).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("project.properties")).unwrap()
        .write_str(build_project_properties().as_slice())
        .unwrap();

    {
        let libs_path = build_directory.path().join("libs").join("armeabi");
        fs::mkdir_recursive(&libs_path, std::old_io::USER_RWX).unwrap();
    }

    {
        // Make sure that 'src' directory is creates
        let src_path = build_directory.path().join("src");
        fs::mkdir_recursive(&src_path, std::old_io::USER_RWX).unwrap();
    }

    build_directory
}

fn java_src(libs: &HashMap<String, Path>) -> String {
    let mut libs_string = "".to_string();

    for (name, _) in libs.iter() {
        // Strip off the 'lib' prefix and ".so" suffix. This is safe since libs only get added
        // to the hash map if they start with lib.
        let line = format!("        System.loadLibrary(\"{}\");\n", name.slice(3, name.len()-3));
        libs_string.push_str(line.as_slice());
    }

    format!(r#"package rust.glutin;

public class MainActivity extends android.app.NativeActivity {{
    static {{
        {0}
    }}
}}"#, libs_string)
}

fn build_manifest(crate_name: &str, activity_name: &str) -> String {
    format!(r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <uses-feature android:glEsVersion="0x00020000" android:required="true"></uses-feature>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application android:label="{0}">
        <activity android:name="{1}"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name, activity_name)
}

fn build_build_xml() -> String {
    format!(r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#)
}

fn build_local_properties(sdk_dir: &Path) -> String {
    use std::os;
    format!(r"sdk.dir={}", os::make_absolute(sdk_dir).unwrap().display())
}

fn build_project_properties() -> String {
    format!(r"target=android-18")
}
<commit_msg>Search -L paths for native libraries.<commit_after>#![feature(path, core, io, os, rustc_private)]

extern crate serialize;

use std::collections::{HashMap, HashSet};
use std::old_io::process::Command;
use std::old_io::{File, TempDir};
use std::old_io::fs;

fn main() {
    let (args, passthrough) = parse_arguments();

    // Find all the native shared libraries that exist in the target directory.
    let native_shared_libs = find_native_libs(&args);

    // getting the path from the ANDROID_HOME env
    let sdk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "ANDROID_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the ANDROID_HOME environment variable");

    // hardcoding ndk path
    let ndk_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the NDK_HOME environment variable");

    // hardcoding ndk path
    let standalone_path = std::os::env().into_iter().find(|&(ref k, _)| k.as_slice() == "NDK_STANDALONE")
        .map(|(_, v)| Path::new(v)).unwrap_or(Path::new("/opt/ndk_standalone"));

    // creating the build directory that will contain all the necessary files to create teh apk
    let directory = build_directory(&sdk_path, args.output.filestem_str().unwrap(), &native_shared_libs);

    // Copy the additional native libs into the libs directory.
    for (name, path) in native_shared_libs.iter() {
        fs::copy(path, &directory.path().join("libs").join("armeabi").join(name)).unwrap();
    }

    // compiling android_native_app_glue.c
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
        .arg("-c")
        .arg("-o").arg(directory.path().join("android_native_app_glue.o"))
        .stdout(std::old_io::process::InheritFd(1)).stderr(std::old_io::process::InheritFd(2))
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling gcc to link to a shared object
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .args(passthrough.as_slice())
        .arg(directory.path().join("android_native_app_glue.o"))
        .arg("-o").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .arg("-shared")
        .arg("-Wl,-E")
        .stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2))//.cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling objdump to make sure that our object has `ANativeActivity_onCreate`
    // TODO: not working
    /*{
        let mut process =
            Command::new(standalone_path.join("bin").join("arm-linux-androideabi-objdump"))
            .arg("-x").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
            .stderr(std::old_io::process::InheritFd(2))
            .spawn().unwrap();

        // TODO: use UFCS instead
        fn by_ref<'a, T: Reader>(r: &'a mut T) -> std::old_io::RefReader<'a, T> { r.by_ref() };

        let stdout = process.stdout.as_mut().unwrap();
        let mut stdout = std::old_io::BufferedReader::new(by_ref(stdout));

        if stdout.lines().filter_map(|l| l.ok())
            .find(|line| line.as_slice().contains("ANativeActivity_onCreate")).is_none()
        {
            println!("Error: the output file doesn't contain ANativeActivity_onCreate");
            std::os::set_exit_status(1);
            return;
        }
    }*/

    // executing ant
    if Command::new("ant").arg("debug").stdout(std::old_io::process::InheritFd(1))
        .stderr(std::old_io::process::InheritFd(2)).cwd(directory.path())
        .status().unwrap() != std::old_io::process::ExitStatus(0)
    {
        println!("Error while executing ant debug");
        std::os::set_exit_status(1);
        return;
    }

    // copying apk file to the requested output
    fs::copy(&directory.path().join("bin").join("rust-android-debug.apk"),
        &args.output).unwrap();
}

struct Args {
    output: Path,
    library_path: Vec<Path>,
    shared_libraries: HashSet<String>,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_library_path = Vec::new();
    let mut result_shared_libraries = HashSet::new();
    let mut result_passthrough = Vec::new();

    let args = std::os::args();
    let mut args = args.into_iter().skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument"),
                    library_path: result_library_path,
                    shared_libraries: result_shared_libraries,
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match arg.as_slice() {
            "-o" => {
                result_output = Some(Path::new(args.next().expect("-o must be followed by the output name")));
            },
            "-L" => {
                let path = args.next().expect("-L must be followed by a path");
                result_library_path.push(Path::new(path.clone()));

                // Also pass these through.
                result_passthrough.push(arg);
                result_passthrough.push(path);
            },
            _ => {
                if arg.starts_with("-l") {
                    result_shared_libraries.insert(vec!["lib", &arg[2..], ".so"].concat());
                }
                result_passthrough.push(arg)
            }
        };
    }
}

fn find_native_libs(args: &Args) -> HashMap<String, Path> {
    let mut native_shared_libs: HashMap<String, Path> = HashMap::new();

    for dir in &args.library_path {
        fs::readdir(&dir).and_then(|paths| {
            for path in paths.iter() {
                match (path.filename_str(), path.extension_str()) {
                    (Some(filename), Some(ext)) => {
                        if filename.starts_with("lib") && ext == "so" &&
                                args.shared_libraries.contains(filename) {
                            native_shared_libs.insert(filename.to_string(), path.clone());
                        }
                    }
                    _ => {}
                }
            }
            Ok(())
        }).ok();
    }
    native_shared_libs
}

fn build_directory(sdk_dir: &Path, crate_name: &str, libs: &HashMap<String, Path>) -> TempDir {
    use std::old_io::fs;

    let build_directory = TempDir::new("android-rs-glue-rust-to-apk")
        .ok().expect("Could not create temporary build directory");

    let activity_name = if libs.len() > 0 {
        let src_path = build_directory.path().join("src/rust/glutin");
        fs::mkdir_recursive(&src_path, std::old_io::USER_RWX).unwrap();

        File::create(&src_path.join("MainActivity.java")).unwrap()
            .write_str(java_src(libs).as_slice())
            .unwrap();

        "rust.glutin.MainActivity"
    } else {
        "android.app.NativeActivity"
    };

    File::create(&build_directory.path().join("AndroidManifest.xml")).unwrap()
        .write_str(build_manifest(crate_name, activity_name).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("build.xml")).unwrap()
        .write_str(build_build_xml().as_slice())
        .unwrap();

    File::create(&build_directory.path().join("local.properties")).unwrap()
        .write_str(build_local_properties(sdk_dir).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("project.properties")).unwrap()
        .write_str(build_project_properties().as_slice())
        .unwrap();

    {
        let libs_path = build_directory.path().join("libs").join("armeabi");
        fs::mkdir_recursive(&libs_path, std::old_io::USER_RWX).unwrap();
    }

    {
        // Make sure that 'src' directory is creates
        let src_path = build_directory.path().join("src");
        fs::mkdir_recursive(&src_path, std::old_io::USER_RWX).unwrap();
    }

    build_directory
}

fn java_src(libs: &HashMap<String, Path>) -> String {
    let mut libs_string = "".to_string();

    for (name, _) in libs.iter() {
        // Strip off the 'lib' prefix and ".so" suffix. This is safe since libs only get added
        // to the hash map if they start with lib.
        let line = format!("        System.loadLibrary(\"{}\");\n", name.slice(3, name.len()-3));
        libs_string.push_str(line.as_slice());
    }

    format!(r#"package rust.glutin;

public class MainActivity extends android.app.NativeActivity {{
    static {{
        {0}
    }}
}}"#, libs_string)
}

fn build_manifest(crate_name: &str, activity_name: &str) -> String {
    format!(r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <uses-feature android:glEsVersion="0x00020000" android:required="true"></uses-feature>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application android:label="{0}">
        <activity android:name="{1}"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name, activity_name)
}

fn build_build_xml() -> String {
    format!(r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#)
}

fn build_local_properties(sdk_dir: &Path) -> String {
    use std::os;
    format!(r"sdk.dir={}", os::make_absolute(sdk_dir).unwrap().display())
}

fn build_project_properties() -> String {
    format!(r"target=android-18")
}
<|endoftext|>


<commit_before>#[deriving(Eq)]
struct RibonucleicAcid {
    nucleotides: ~str
}

impl RibonucleicAcid {
    pub fn new(nucleotides: ~str) -> RibonucleicAcid { RibonucleicAcid { nucleotides: nucleotides } }
}

impl ToStr for RibonucleicAcid {
    fn to_str(&self) -> ~str {
        self.nucleotides.to_str()
    }
}

struct DeoxyribonucleicAcid {
    nucleotides: ~str
}

impl DeoxyribonucleicAcid {
    pub fn new(nucleotides: ~str) -> DeoxyribonucleicAcid { DeoxyribonucleicAcid { nucleotides: nucleotides } }

    pub fn to_rna(&self) -> RibonucleicAcid {
        let rna_nucleotides = self.nucleotides.map_chars(|chr| if chr == 'T' { 'U' } else { chr } );

        RibonucleicAcid { nucleotides: rna_nucleotides }
    }
}

<commit_msg>rust rna-transcription - rust 0.8 no longer has map_chars, use replace instead<commit_after>#[deriving(Eq)]
struct RibonucleicAcid {
    nucleotides: ~str
}

impl RibonucleicAcid {
    pub fn new(nucleotides: ~str) -> RibonucleicAcid {
        RibonucleicAcid { nucleotides: nucleotides }
    }
}

impl ToStr for RibonucleicAcid {
    fn to_str(&self) -> ~str {
        self.nucleotides.to_str()
    }
}

struct DeoxyribonucleicAcid {
    nucleotides: ~str
}

impl DeoxyribonucleicAcid {
    pub fn new(nucleotides: ~str) -> DeoxyribonucleicAcid {
        DeoxyribonucleicAcid { nucleotides: nucleotides }
    }

    pub fn to_rna(&self) -> RibonucleicAcid {
        let rna_nucleotides = self.nucleotides.replace("T", "U");

        RibonucleicAcid { nucleotides: rna_nucleotides }
    }
}

<|endoftext|>


<commit_before>//! VM Runtime
use bigint::{H256, M256, U256, Gas, Address};
use super::commit::{AccountState, BlockhashState};
use super::errors::{RequireError, RuntimeError, CommitError, EvalOnChainError,
                    OnChainError, NotSupportedError};
use super::{Stack, Context, HeaderParams, Patch, PC, Memory, AccountCommitment, Log};

use self::check::{check_opcode, check_support, extra_check_opcode};
use self::run::run_opcode;
use self::cost::{gas_refund, gas_stipend, gas_cost, memory_cost, memory_gas};

mod cost;
mod run;
mod check;
mod util;
mod lifecycle;

/// A VM state without PC.
pub struct State<M> {
    /// Memory of this runtime.
    pub memory: M,
    /// Stack of this runtime.
    pub stack: Stack,

    /// Context.
    pub context: Context,
    /// Block header.
    pub block: HeaderParams,

    /// The current out value.
    pub out: Vec<u8>,

    /// The current memory cost. Note that this is different from
    /// memory gas.
    pub memory_cost: Gas,
    /// Used gas excluding memory gas.
    pub used_gas: Gas,
    /// Refunded gas.
    pub refunded_gas: Gas,

    /// The current account commitment states.
    pub account_state: AccountState,
    /// The current blockhash commitment states.
    pub blockhash_state: BlockhashState,
    /// Logs appended.
    pub logs: Vec<Log>,
    /// Removed accounts using the SUICIDE opcode.
    pub removed: Vec<Address>,

    /// Depth of this runtime.
    pub depth: usize,
}

impl<M> State<M> {
    /// Memory gas, part of total used gas.
    pub fn memory_gas(&self) -> Gas {
        memory_gas(self.memory_cost)
    }

    /// Available gas at this moment.
    pub fn available_gas(&self) -> Gas {
        self.context.gas_limit - self.memory_gas() - self.used_gas
    }

    /// Total used gas including the memory gas.
    pub fn total_used_gas(&self) -> Gas {
        self.memory_gas() + self.used_gas
    }
}

/// A VM state with PC.
pub struct Machine<M, P: Patch> {
    state: State<M>,
    pc: PC<P>,
    status: MachineStatus,
}

#[derive(Debug, Clone)]
/// Represents the current runtime status.
pub enum MachineStatus {
    /// This runtime is actively running or has just been started.
    Running,
    /// This runtime has exited successfully. Calling `step` on this
    /// runtime again would panic.
    ExitedOk,
    /// This runtime has exited with errors. Calling `step` on this
    /// runtime again would panic.
    ExitedErr(OnChainError),
    ExitedNotSupported(NotSupportedError),
    /// This runtime requires execution of a sub runtime, which is a
    /// ContractCreation instruction.
    InvokeCreate(Context),
    /// This runtime requires execution of a sub runtime, which is a
    /// MessageCall instruction.
    InvokeCall(Context, (U256, U256)),
}

#[derive(Debug, Clone)]
/// Used for `check` for additional checks related to the runtime.
pub enum ControlCheck {
    Jump(M256),
}

#[derive(Debug, Clone)]
/// Used for `step` for additional operations related to the runtime.
pub enum Control {
    Stop,
    Jump(M256),
    InvokeCreate(Context),
    InvokeCall(Context, (U256, U256)),
}

impl<M: Memory + Default, P: Patch> Machine<M, P> {
    /// Create a new runtime.
    pub fn new(context: Context, block: HeaderParams, depth: usize) -> Self {
        Self::with_states(context, block, depth,
                          AccountState::default(), BlockhashState::default())
    }

    /// Create a new runtime with the given states.
    pub fn with_states(context: Context, block: HeaderParams,
                       depth: usize, account_state: AccountState,
                       blockhash_state: BlockhashState) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context,
                block,

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state,
                blockhash_state,
                logs: Vec::new(),
                removed: Vec::new(),

                depth,
            },
        }
    }

    /// Derive this runtime to create a sub runtime. This will not
    /// modify the current runtime, and it will have a chance to
    /// review whether it wants to accept the result of this sub
    /// runtime afterwards.
    pub fn derive(&self, context: Context) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context: context,
                block: self.state.block.clone(),

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state: self.state.account_state.clone(),
                blockhash_state: self.state.blockhash_state.clone(),
                logs: self.state.logs.clone(),
                removed: self.state.removed.clone(),

                depth: self.state.depth + 1,
            },
        }
    }

    /// Commit a new account into this runtime.
    pub fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError> {
        self.state.account_state.commit(commitment)
    }

    /// Commit a new blockhash into this runtime.
    pub fn commit_blockhash(&mut self, number: U256, hash: H256) -> Result<(), CommitError> {
        self.state.blockhash_state.commit(number, hash)
    }

    /// Step a precompiled runtime. This function returns true if the
    /// runtime is indeed a precompiled address. Otherwise return
    /// false with state unchanged.
    pub fn step_precompiled(&mut self) -> bool {
        for precompiled in P::precompileds() {
            if self.state.context.address == precompiled.0 &&
                (precompiled.1.is_none() || precompiled.1.unwrap() == self.pc.code())
            {
                let data = &self.state.context.data;
                match precompiled.2.gas_and_step(data, self.state.context.gas_limit) {
                    Err(RuntimeError::OnChain(err)) => {
                        self.state.used_gas = self.state.context.gas_limit;
                        self.status = MachineStatus::ExitedErr(err);
                    },
                    Err(RuntimeError::NotSupported(err)) => {
                        self.status = MachineStatus::ExitedNotSupported(err);
                    },
                    Ok((gas, ret)) => {
                        assert!(gas <= self.state.context.gas_limit);
                        self.state.used_gas = gas;
                        self.state.out = ret;
                        self.status = MachineStatus::ExitedOk;
                    }
                }
                return true;
            }
        }
        return false;
    }

    /// Step an instruction in the PC. The eval result is refected by
    /// the runtime status, and it will only return an error if
    /// there're accounts or blockhashes to be committed to this
    /// runtime for it to run. In that case, the state of the current
    /// runtime will not be affected.
    pub fn step(&mut self) -> Result<(), RequireError> {
        match &self.status {
            &MachineStatus::Running => (),
            _ => panic!(),
        }

        if self.step_precompiled() {
            return Ok(());
        }

        if self.pc.is_end() {
            self.status = MachineStatus::ExitedOk;
            return Ok(());
        }

        let instruction = match self.pc.read() {
            Ok(val) => val,
            Err(RuntimeError::OnChain(err)) => {
                self.status = MachineStatus::ExitedErr(err);
                return Ok(())
            },
            Err(RuntimeError::NotSupported(err)) => {
                self.status = MachineStatus::ExitedNotSupported(err);
                return Ok(())
            },
        };

        match check_opcode(instruction, &self.state).and_then(|v| {
            match v {
                None => Ok(()),
                Some(ControlCheck::Jump(dest)) => {
                    if dest <= M256::from(usize::max_value()) && self.pc.is_valid(dest.as_usize()) {
                        Ok(())
                    } else {
                        Err(OnChainError::BadJumpDest.into())
                    }
                }
            }
        }) {
            Ok(()) => (),
            Err(EvalOnChainError::OnChain(error)) => {
                self.status = MachineStatus::ExitedErr(error);
                return Ok(());
            },
            Err(EvalOnChainError::Require(error)) => {
                return Err(error);
            },
        }

        let position = self.pc.position();
        let memory_cost = memory_cost(instruction, &self.state);
        let memory_gas = memory_gas(memory_cost);
        let gas_cost = gas_cost::<M, P>(instruction, &self.state);
        let gas_stipend = gas_stipend(instruction, &self.state);
        let gas_refund = gas_refund(instruction, &self.state);

        let all_gas_cost = memory_gas + self.state.used_gas + gas_cost;
        if self.state.context.gas_limit < all_gas_cost {
            self.status = MachineStatus::ExitedErr(OnChainError::EmptyGas);
            return Ok(());
        }

        match check_support(instruction, &self.state) {
            Ok(()) => (),
            Err(err) => {
                self.status = MachineStatus::ExitedNotSupported(err);
                return Ok(());
            },
        };

        let after_gas = self.state.context.gas_limit - all_gas_cost;

        match extra_check_opcode::<M, P>(instruction, &self.state, gas_stipend, after_gas) {
            Ok(()) => (),
            Err(err) => {
                self.status = MachineStatus::ExitedErr(err);
                return Ok(());
            },
        }

        let instruction = self.pc.read().unwrap();
        let result = run_opcode::<M, P>((instruction, position),
                                        &mut self.state, gas_stipend, after_gas);

        self.state.used_gas = self.state.used_gas + gas_cost - gas_stipend;
        self.state.memory_cost = memory_cost;
        self.state.refunded_gas = self.state.refunded_gas + gas_refund;

        match result {
            None => Ok(()),
            Some(Control::Jump(dest)) => {
                self.pc.jump(dest.as_usize()).unwrap();
                Ok(())
            },
            Some(Control::InvokeCall(context, (from, len))) => {
                self.status = MachineStatus::InvokeCall(context, (from, len));
                Ok(())
            },
            Some(Control::InvokeCreate(context)) => {
                self.status = MachineStatus::InvokeCreate(context);
                Ok(())
            },
            Some(Control::Stop) => {
                self.status = MachineStatus::ExitedOk;
                Ok(())
            },
        }
    }

    /// Get the runtime state.
    pub fn state(&self) -> &State<M> {
        &self.state
    }

    /// Get the runtime PC.
    pub fn pc(&self) -> &PC<P> {
        &self.pc
    }

    /// Get the current runtime status.
    pub fn status(&self) -> MachineStatus {
        self.status.clone()
    }
}
<commit_msg>First instruction check should be peek<commit_after>//! VM Runtime
use bigint::{H256, M256, U256, Gas, Address};
use super::commit::{AccountState, BlockhashState};
use super::errors::{RequireError, RuntimeError, CommitError, EvalOnChainError,
                    OnChainError, NotSupportedError};
use super::{Stack, Context, HeaderParams, Patch, PC, Memory, AccountCommitment, Log};

use self::check::{check_opcode, check_support, extra_check_opcode};
use self::run::run_opcode;
use self::cost::{gas_refund, gas_stipend, gas_cost, memory_cost, memory_gas};

mod cost;
mod run;
mod check;
mod util;
mod lifecycle;

/// A VM state without PC.
pub struct State<M> {
    /// Memory of this runtime.
    pub memory: M,
    /// Stack of this runtime.
    pub stack: Stack,

    /// Context.
    pub context: Context,
    /// Block header.
    pub block: HeaderParams,

    /// The current out value.
    pub out: Vec<u8>,

    /// The current memory cost. Note that this is different from
    /// memory gas.
    pub memory_cost: Gas,
    /// Used gas excluding memory gas.
    pub used_gas: Gas,
    /// Refunded gas.
    pub refunded_gas: Gas,

    /// The current account commitment states.
    pub account_state: AccountState,
    /// The current blockhash commitment states.
    pub blockhash_state: BlockhashState,
    /// Logs appended.
    pub logs: Vec<Log>,
    /// Removed accounts using the SUICIDE opcode.
    pub removed: Vec<Address>,

    /// Depth of this runtime.
    pub depth: usize,
}

impl<M> State<M> {
    /// Memory gas, part of total used gas.
    pub fn memory_gas(&self) -> Gas {
        memory_gas(self.memory_cost)
    }

    /// Available gas at this moment.
    pub fn available_gas(&self) -> Gas {
        self.context.gas_limit - self.memory_gas() - self.used_gas
    }

    /// Total used gas including the memory gas.
    pub fn total_used_gas(&self) -> Gas {
        self.memory_gas() + self.used_gas
    }
}

/// A VM state with PC.
pub struct Machine<M, P: Patch> {
    state: State<M>,
    pc: PC<P>,
    status: MachineStatus,
}

#[derive(Debug, Clone)]
/// Represents the current runtime status.
pub enum MachineStatus {
    /// This runtime is actively running or has just been started.
    Running,
    /// This runtime has exited successfully. Calling `step` on this
    /// runtime again would panic.
    ExitedOk,
    /// This runtime has exited with errors. Calling `step` on this
    /// runtime again would panic.
    ExitedErr(OnChainError),
    ExitedNotSupported(NotSupportedError),
    /// This runtime requires execution of a sub runtime, which is a
    /// ContractCreation instruction.
    InvokeCreate(Context),
    /// This runtime requires execution of a sub runtime, which is a
    /// MessageCall instruction.
    InvokeCall(Context, (U256, U256)),
}

#[derive(Debug, Clone)]
/// Used for `check` for additional checks related to the runtime.
pub enum ControlCheck {
    Jump(M256),
}

#[derive(Debug, Clone)]
/// Used for `step` for additional operations related to the runtime.
pub enum Control {
    Stop,
    Jump(M256),
    InvokeCreate(Context),
    InvokeCall(Context, (U256, U256)),
}

impl<M: Memory + Default, P: Patch> Machine<M, P> {
    /// Create a new runtime.
    pub fn new(context: Context, block: HeaderParams, depth: usize) -> Self {
        Self::with_states(context, block, depth,
                          AccountState::default(), BlockhashState::default())
    }

    /// Create a new runtime with the given states.
    pub fn with_states(context: Context, block: HeaderParams,
                       depth: usize, account_state: AccountState,
                       blockhash_state: BlockhashState) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context,
                block,

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state,
                blockhash_state,
                logs: Vec::new(),
                removed: Vec::new(),

                depth,
            },
        }
    }

    /// Derive this runtime to create a sub runtime. This will not
    /// modify the current runtime, and it will have a chance to
    /// review whether it wants to accept the result of this sub
    /// runtime afterwards.
    pub fn derive(&self, context: Context) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context: context,
                block: self.state.block.clone(),

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state: self.state.account_state.clone(),
                blockhash_state: self.state.blockhash_state.clone(),
                logs: self.state.logs.clone(),
                removed: self.state.removed.clone(),

                depth: self.state.depth + 1,
            },
        }
    }

    /// Commit a new account into this runtime.
    pub fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError> {
        self.state.account_state.commit(commitment)
    }

    /// Commit a new blockhash into this runtime.
    pub fn commit_blockhash(&mut self, number: U256, hash: H256) -> Result<(), CommitError> {
        self.state.blockhash_state.commit(number, hash)
    }

    /// Step a precompiled runtime. This function returns true if the
    /// runtime is indeed a precompiled address. Otherwise return
    /// false with state unchanged.
    pub fn step_precompiled(&mut self) -> bool {
        for precompiled in P::precompileds() {
            if self.state.context.address == precompiled.0 &&
                (precompiled.1.is_none() || precompiled.1.unwrap() == self.pc.code())
            {
                let data = &self.state.context.data;
                match precompiled.2.gas_and_step(data, self.state.context.gas_limit) {
                    Err(RuntimeError::OnChain(err)) => {
                        self.state.used_gas = self.state.context.gas_limit;
                        self.status = MachineStatus::ExitedErr(err);
                    },
                    Err(RuntimeError::NotSupported(err)) => {
                        self.status = MachineStatus::ExitedNotSupported(err);
                    },
                    Ok((gas, ret)) => {
                        assert!(gas <= self.state.context.gas_limit);
                        self.state.used_gas = gas;
                        self.state.out = ret;
                        self.status = MachineStatus::ExitedOk;
                    }
                }
                return true;
            }
        }
        return false;
    }

    /// Step an instruction in the PC. The eval result is refected by
    /// the runtime status, and it will only return an error if
    /// there're accounts or blockhashes to be committed to this
    /// runtime for it to run. In that case, the state of the current
    /// runtime will not be affected.
    pub fn step(&mut self) -> Result<(), RequireError> {
        match &self.status {
            &MachineStatus::Running => (),
            _ => panic!(),
        }

        if self.step_precompiled() {
            return Ok(());
        }

        if self.pc.is_end() {
            self.status = MachineStatus::ExitedOk;
            return Ok(());
        }

        let instruction = match self.pc.peek() {
            Ok(val) => val,
            Err(RuntimeError::OnChain(err)) => {
                self.status = MachineStatus::ExitedErr(err);
                return Ok(())
            },
            Err(RuntimeError::NotSupported(err)) => {
                self.status = MachineStatus::ExitedNotSupported(err);
                return Ok(())
            },
        };

        match check_opcode(instruction, &self.state).and_then(|v| {
            match v {
                None => Ok(()),
                Some(ControlCheck::Jump(dest)) => {
                    if dest <= M256::from(usize::max_value()) && self.pc.is_valid(dest.as_usize()) {
                        Ok(())
                    } else {
                        Err(OnChainError::BadJumpDest.into())
                    }
                }
            }
        }) {
            Ok(()) => (),
            Err(EvalOnChainError::OnChain(error)) => {
                self.status = MachineStatus::ExitedErr(error);
                return Ok(());
            },
            Err(EvalOnChainError::Require(error)) => {
                return Err(error);
            },
        }

        let position = self.pc.position();
        let memory_cost = memory_cost(instruction, &self.state);
        let memory_gas = memory_gas(memory_cost);
        let gas_cost = gas_cost::<M, P>(instruction, &self.state);
        let gas_stipend = gas_stipend(instruction, &self.state);
        let gas_refund = gas_refund(instruction, &self.state);

        let all_gas_cost = memory_gas + self.state.used_gas + gas_cost;
        if self.state.context.gas_limit < all_gas_cost {
            self.status = MachineStatus::ExitedErr(OnChainError::EmptyGas);
            return Ok(());
        }

        match check_support(instruction, &self.state) {
            Ok(()) => (),
            Err(err) => {
                self.status = MachineStatus::ExitedNotSupported(err);
                return Ok(());
            },
        };

        let after_gas = self.state.context.gas_limit - all_gas_cost;

        match extra_check_opcode::<M, P>(instruction, &self.state, gas_stipend, after_gas) {
            Ok(()) => (),
            Err(err) => {
                self.status = MachineStatus::ExitedErr(err);
                return Ok(());
            },
        }

        let instruction = self.pc.read().unwrap();
        let result = run_opcode::<M, P>((instruction, position),
                                        &mut self.state, gas_stipend, after_gas);

        self.state.used_gas = self.state.used_gas + gas_cost - gas_stipend;
        self.state.memory_cost = memory_cost;
        self.state.refunded_gas = self.state.refunded_gas + gas_refund;

        match result {
            None => Ok(()),
            Some(Control::Jump(dest)) => {
                self.pc.jump(dest.as_usize()).unwrap();
                Ok(())
            },
            Some(Control::InvokeCall(context, (from, len))) => {
                self.status = MachineStatus::InvokeCall(context, (from, len));
                Ok(())
            },
            Some(Control::InvokeCreate(context)) => {
                self.status = MachineStatus::InvokeCreate(context);
                Ok(())
            },
            Some(Control::Stop) => {
                self.status = MachineStatus::ExitedOk;
                Ok(())
            },
        }
    }

    /// Get the runtime state.
    pub fn state(&self) -> &State<M> {
        &self.state
    }

    /// Get the runtime PC.
    pub fn pc(&self) -> &PC<P> {
        &self.pc
    }

    /// Get the current runtime status.
    pub fn status(&self) -> MachineStatus {
        self.status.clone()
    }
}
<|endoftext|>


<commit_before>// TODO: Possible optimization could avoid pushing characters on a buffer.
mod command;
mod input;
mod job;
mod token;

use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};

use anyhow::{anyhow, Result};
use lazy_static::lazy_static;
use regex::Regex;

use crate::exit_codes::ExitCode;

use self::command::execute_command;
use self::input::{basename, dirname, remove_extension};
pub use self::job::{batch, job};
use self::token::Token;

/// Execution mode of the command
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExecutionMode {
    /// Command is executed for each search result
    OneByOne,
    /// Command is run for a batch of results at once
    Batch,
}

/// Represents a template that is utilized to generate command strings.
///
/// The template is meant to be coupled with an input in order to generate a command. The
/// `generate_and_execute()` method will be used to generate a command and execute it.
#[derive(Debug, Clone, PartialEq)]
pub struct CommandTemplate {
    args: Vec<ArgumentTemplate>,
    mode: ExecutionMode,
}

impl CommandTemplate {
    pub fn new<I, S>(input: I) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        Self::build(input, ExecutionMode::OneByOne)
    }

    pub fn new_batch<I, S>(input: I) -> Result<CommandTemplate>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        let cmd = Self::build(input, ExecutionMode::Batch);
        if cmd.number_of_tokens() > 1 {
            return Err(anyhow!("Only one placeholder allowed for batch commands"));
        }
        if cmd.args[0].has_tokens() {
            return Err(anyhow!(
                "First argument of exec-batch is expected to be a fixed executable"
            ));
        }
        Ok(cmd)
    }

    fn build<I, S>(input: I, mode: ExecutionMode) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        lazy_static! {
            static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap();
        }

        let mut args = Vec::new();
        let mut has_placeholder = false;

        for arg in input {
            let arg = arg.as_ref();

            let mut tokens = Vec::new();
            let mut start = 0;

            for placeholder in PLACEHOLDER_PATTERN.find_iter(arg) {
                // Leading text before the placeholder.
                if placeholder.start() > start {
                    tokens.push(Token::Text(arg[start..placeholder.start()].to_owned()));
                }

                start = placeholder.end();

                match placeholder.as_str() {
                    "{}" => tokens.push(Token::Placeholder),
                    "{.}" => tokens.push(Token::NoExt),
                    "{/}" => tokens.push(Token::Basename),
                    "{//}" => tokens.push(Token::Parent),
                    "{/.}" => tokens.push(Token::BasenameNoExt),
                    _ => panic!("Unhandled placeholder"),
                }

                has_placeholder = true;
            }

            // Without a placeholder, the argument is just fixed text.
            if tokens.is_empty() {
                args.push(ArgumentTemplate::Text(arg.to_owned()));
                continue;
            }

            if start < arg.len() {
                // Trailing text after last placeholder.
                tokens.push(Token::Text(arg[start..].to_owned()));
            }

            args.push(ArgumentTemplate::Tokens(tokens));
        }

        // If a placeholder token was not supplied, append one at the end of the command.
        if !has_placeholder {
            args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
        }

        CommandTemplate { args, mode }
    }

    fn number_of_tokens(&self) -> usize {
        self.args.iter().filter(|arg| arg.has_tokens()).count()
    }

    fn prepare_path(input: &Path) -> String {
        input
            .strip_prefix(".")
            .unwrap_or(input)
            .to_string_lossy()
            .into_owned()
    }

    /// Generates and executes a command.
    ///
    /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
    /// build. Once all arguments have been processed, the command is executed.
    pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) -> ExitCode {
        let input = Self::prepare_path(input);

        let mut cmd = Command::new(self.args[0].generate(&input).as_ref());
        for arg in &self.args[1..] {
            cmd.arg(arg.generate(&input).as_ref());
        }

        execute_command(cmd, &out_perm)
    }

    pub fn in_batch_mode(&self) -> bool {
        self.mode == ExecutionMode::Batch
    }

    pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
    where
        I: Iterator<Item = PathBuf>,
    {
        let mut cmd = Command::new(self.args[0].generate("").as_ref());
        cmd.stdin(Stdio::inherit());
        cmd.stdout(Stdio::inherit());
        cmd.stderr(Stdio::inherit());

        let mut paths: Vec<String> = paths.map(|p| Self::prepare_path(&p)).collect();
        let mut has_path = false;

        for arg in &self.args[1..] {
            if arg.has_tokens() {
                paths.sort();

                // A single `Tokens` is expected
                // So we can directly consume the iterator once and for all
                for path in &mut paths {
                    cmd.arg(arg.generate(&path).as_ref());
                    has_path = true;
                }
            } else {
                cmd.arg(arg.generate("").as_ref());
            }
        }

        if has_path {
            execute_command(cmd, &Mutex::new(()))
        } else {
            ExitCode::Success
        }
    }
}

/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
    Tokens(Vec<Token>),
    Text(String),
}

impl ArgumentTemplate {
    pub fn has_tokens(&self) -> bool {
        match self {
            ArgumentTemplate::Tokens(_) => true,
            _ => false,
        }
    }

    pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> {
        use self::Token::*;

        match *self {
            ArgumentTemplate::Tokens(ref tokens) => {
                let mut s = String::new();
                for token in tokens {
                    match *token {
                        Basename => s += basename(path),
                        BasenameNoExt => s += remove_extension(basename(path)),
                        NoExt => s += remove_extension(path),
                        Parent => s += dirname(path),
                        Placeholder => s += path,
                        Text(ref string) => s += string,
                    }
                }
                Cow::Owned(s)
            }
            ArgumentTemplate::Text(ref text) => Cow::Borrowed(text),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn tokens_with_placeholder() {
        assert_eq!(
            CommandTemplate::new(&[&"echo", &"${SHELL}:"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Text("${SHELL}:".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Basename]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_parent() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{//}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Parent]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_multiple() {
        assert_eq!(
            CommandTemplate::new(&["cp", "{}", "{/.}.ext"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("cp".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                    ArgumentTemplate::Tokens(vec![
                        Token::BasenameNoExt,
                        Token::Text(".ext".into())
                    ]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_single_batch() {
        assert_eq!(
            CommandTemplate::new_batch(&["echo", "{.}"]).unwrap(),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::Batch,
            }
        );
    }

    #[test]
    fn tokens_multiple_batch() {
        assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"]).is_err());
    }
}
<commit_msg>Remove irrelevant TODO<commit_after>mod command;
mod input;
mod job;
mod token;

use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};

use anyhow::{anyhow, Result};
use lazy_static::lazy_static;
use regex::Regex;

use crate::exit_codes::ExitCode;

use self::command::execute_command;
use self::input::{basename, dirname, remove_extension};
pub use self::job::{batch, job};
use self::token::Token;

/// Execution mode of the command
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExecutionMode {
    /// Command is executed for each search result
    OneByOne,
    /// Command is run for a batch of results at once
    Batch,
}

/// Represents a template that is utilized to generate command strings.
///
/// The template is meant to be coupled with an input in order to generate a command. The
/// `generate_and_execute()` method will be used to generate a command and execute it.
#[derive(Debug, Clone, PartialEq)]
pub struct CommandTemplate {
    args: Vec<ArgumentTemplate>,
    mode: ExecutionMode,
}

impl CommandTemplate {
    pub fn new<I, S>(input: I) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        Self::build(input, ExecutionMode::OneByOne)
    }

    pub fn new_batch<I, S>(input: I) -> Result<CommandTemplate>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        let cmd = Self::build(input, ExecutionMode::Batch);
        if cmd.number_of_tokens() > 1 {
            return Err(anyhow!("Only one placeholder allowed for batch commands"));
        }
        if cmd.args[0].has_tokens() {
            return Err(anyhow!(
                "First argument of exec-batch is expected to be a fixed executable"
            ));
        }
        Ok(cmd)
    }

    fn build<I, S>(input: I, mode: ExecutionMode) -> CommandTemplate
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        lazy_static! {
            static ref PLACEHOLDER_PATTERN: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap();
        }

        let mut args = Vec::new();
        let mut has_placeholder = false;

        for arg in input {
            let arg = arg.as_ref();

            let mut tokens = Vec::new();
            let mut start = 0;

            for placeholder in PLACEHOLDER_PATTERN.find_iter(arg) {
                // Leading text before the placeholder.
                if placeholder.start() > start {
                    tokens.push(Token::Text(arg[start..placeholder.start()].to_owned()));
                }

                start = placeholder.end();

                match placeholder.as_str() {
                    "{}" => tokens.push(Token::Placeholder),
                    "{.}" => tokens.push(Token::NoExt),
                    "{/}" => tokens.push(Token::Basename),
                    "{//}" => tokens.push(Token::Parent),
                    "{/.}" => tokens.push(Token::BasenameNoExt),
                    _ => panic!("Unhandled placeholder"),
                }

                has_placeholder = true;
            }

            // Without a placeholder, the argument is just fixed text.
            if tokens.is_empty() {
                args.push(ArgumentTemplate::Text(arg.to_owned()));
                continue;
            }

            if start < arg.len() {
                // Trailing text after last placeholder.
                tokens.push(Token::Text(arg[start..].to_owned()));
            }

            args.push(ArgumentTemplate::Tokens(tokens));
        }

        // If a placeholder token was not supplied, append one at the end of the command.
        if !has_placeholder {
            args.push(ArgumentTemplate::Tokens(vec![Token::Placeholder]));
        }

        CommandTemplate { args, mode }
    }

    fn number_of_tokens(&self) -> usize {
        self.args.iter().filter(|arg| arg.has_tokens()).count()
    }

    fn prepare_path(input: &Path) -> String {
        input
            .strip_prefix(".")
            .unwrap_or(input)
            .to_string_lossy()
            .into_owned()
    }

    /// Generates and executes a command.
    ///
    /// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
    /// build. Once all arguments have been processed, the command is executed.
    pub fn generate_and_execute(&self, input: &Path, out_perm: Arc<Mutex<()>>) -> ExitCode {
        let input = Self::prepare_path(input);

        let mut cmd = Command::new(self.args[0].generate(&input).as_ref());
        for arg in &self.args[1..] {
            cmd.arg(arg.generate(&input).as_ref());
        }

        execute_command(cmd, &out_perm)
    }

    pub fn in_batch_mode(&self) -> bool {
        self.mode == ExecutionMode::Batch
    }

    pub fn generate_and_execute_batch<I>(&self, paths: I) -> ExitCode
    where
        I: Iterator<Item = PathBuf>,
    {
        let mut cmd = Command::new(self.args[0].generate("").as_ref());
        cmd.stdin(Stdio::inherit());
        cmd.stdout(Stdio::inherit());
        cmd.stderr(Stdio::inherit());

        let mut paths: Vec<String> = paths.map(|p| Self::prepare_path(&p)).collect();
        let mut has_path = false;

        for arg in &self.args[1..] {
            if arg.has_tokens() {
                paths.sort();

                // A single `Tokens` is expected
                // So we can directly consume the iterator once and for all
                for path in &mut paths {
                    cmd.arg(arg.generate(&path).as_ref());
                    has_path = true;
                }
            } else {
                cmd.arg(arg.generate("").as_ref());
            }
        }

        if has_path {
            execute_command(cmd, &Mutex::new(()))
        } else {
            ExitCode::Success
        }
    }
}

/// Represents a template for a single command argument.
///
/// The argument is either a collection of `Token`s including at least one placeholder variant, or
/// a fixed text.
#[derive(Clone, Debug, PartialEq)]
enum ArgumentTemplate {
    Tokens(Vec<Token>),
    Text(String),
}

impl ArgumentTemplate {
    pub fn has_tokens(&self) -> bool {
        match self {
            ArgumentTemplate::Tokens(_) => true,
            _ => false,
        }
    }

    pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> {
        use self::Token::*;

        match *self {
            ArgumentTemplate::Tokens(ref tokens) => {
                let mut s = String::new();
                for token in tokens {
                    match *token {
                        Basename => s += basename(path),
                        BasenameNoExt => s += remove_extension(basename(path)),
                        NoExt => s += remove_extension(path),
                        Parent => s += dirname(path),
                        Placeholder => s += path,
                        Text(ref string) => s += string,
                    }
                }
                Cow::Owned(s)
            }
            ArgumentTemplate::Text(ref text) => Cow::Borrowed(text),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn tokens_with_placeholder() {
        assert_eq!(
            CommandTemplate::new(&[&"echo", &"${SHELL}:"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Text("${SHELL}:".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Basename]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_parent() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{//}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::Parent]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_with_basename_no_extension() {
        assert_eq!(
            CommandTemplate::new(&["echo", "{/.}"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::BasenameNoExt]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_multiple() {
        assert_eq!(
            CommandTemplate::new(&["cp", "{}", "{/.}.ext"]),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("cp".into()),
                    ArgumentTemplate::Tokens(vec![Token::Placeholder]),
                    ArgumentTemplate::Tokens(vec![
                        Token::BasenameNoExt,
                        Token::Text(".ext".into())
                    ]),
                ],
                mode: ExecutionMode::OneByOne,
            }
        );
    }

    #[test]
    fn tokens_single_batch() {
        assert_eq!(
            CommandTemplate::new_batch(&["echo", "{.}"]).unwrap(),
            CommandTemplate {
                args: vec![
                    ArgumentTemplate::Text("echo".into()),
                    ArgumentTemplate::Tokens(vec![Token::NoExt]),
                ],
                mode: ExecutionMode::Batch,
            }
        );
    }

    #[test]
    fn tokens_multiple_batch() {
        assert!(CommandTemplate::new_batch(&["echo", "{.}", "{}"]).is_err());
    }
}
<|endoftext|>


<commit_before>use std::mem;

use wasm_context::{
    FunctionKind,
    FnTy,
    Import,
    Export,
    Instruction,
    Context
};

pub fn process( ctx: &mut Context ) {
    let type_index = ctx.get_or_add_fn_type( FnTy { params: vec![], return_type: None } );
    let on_grow_function_index = ctx.add_function( FunctionKind::Import {
        type_index,
        export: Export::none(),
        import: Import {
            module: "env".to_owned(),
            field: "__web_on_grow".to_owned()
        },
        name: Some( "__web_on_grow".to_owned() )
    });

    ctx.patch_code( |instructions| {
        let should_process = instructions.iter().any( |instruction| {
            match instruction {
                &Instruction::GrowMemory( _ ) => true,
                _ => false
            }
        });

        if !should_process {
            return;
        }

        let mut new_instructions = Vec::with_capacity( instructions.len() );
        let mut old_instructions = Vec::new();
        mem::swap( instructions, &mut old_instructions );

        for instruction in old_instructions {
            let should_insert = match &instruction {
                &Instruction::GrowMemory( _ ) => true,
                &_ => false
            };

            new_instructions.push( instruction );
            if should_insert {
                new_instructions.push( Instruction::Call( on_grow_function_index ) );
            }
        }

        *instructions = new_instructions;
    });
}
<commit_msg>Do not hook memory growth twice<commit_after>use std::mem;

use wasm_context::{
    FunctionKind,
    FnTy,
    Import,
    Export,
    Instruction,
    Context
};

pub fn process( ctx: &mut Context ) {
    let already_done = ctx.functions.iter().any( |(_, function)| {
        match function {
            &FunctionKind::Import { import: Import { ref module, ref field }, .. } if module == "env" && field == "__web_on_grow" => {
                true
            },
            _ => false
        }
    });

    if already_done {
        return;
    }

    let type_index = ctx.get_or_add_fn_type( FnTy { params: vec![], return_type: None } );
    let on_grow_function_index = ctx.add_function( FunctionKind::Import {
        type_index,
        export: Export::none(),
        import: Import {
            module: "env".to_owned(),
            field: "__web_on_grow".to_owned()
        },
        name: Some( "__web_on_grow".to_owned() )
    });

    ctx.patch_code( |instructions| {
        let should_process = instructions.iter().any( |instruction| {
            match instruction {
                &Instruction::GrowMemory( _ ) => true,
                _ => false
            }
        });

        if !should_process {
            return;
        }

        let mut new_instructions = Vec::with_capacity( instructions.len() );
        let mut old_instructions = Vec::new();
        mem::swap( instructions, &mut old_instructions );

        for instruction in old_instructions {
            let should_insert = match &instruction {
                &Instruction::GrowMemory( _ ) => true,
                &_ => false
            };

            new_instructions.push( instruction );
            if should_insert {
                new_instructions.push( Instruction::Call( on_grow_function_index ) );
            }
        }

        *instructions = new_instructions;
    });
}
<|endoftext|>


<commit_before><commit_msg>Revert "Minor wheeling..."<commit_after><|endoftext|>