<commit_before>//! Handles instance splitting.
//!
//! Used to reason separately on each positive/negative clause.

use common::* ;

/// Prints the stats if asked. Does nothing in bench mode.
#[cfg(feature = "bench")]
fn print_stats(_: & 'static str, _: Profiler) {}
/// Prints the stats if asked. Does nothing in bench mode.
#[cfg( not(feature = "bench") )]
fn print_stats(name: & 'static str, profiler: Profiler) {
  if conf.stats {
    println!("") ;
    profiler.print( name, "", & [ "data" ] ) ;
    println!("") ;
  }
}

/// Splits the instance if asked to do so, and solves it.
///
/// Assumes the instance is **already pre-processed**.
pub fn work(
  real_instance: Arc<Instance>, _profiler: & Profiler
) -> Res< Option<DnfCandidates> > {
  // Encodes a DNF.
  let mut model = DnfCandidates::new() ;
  let mut neg_models = PrdHMap::new() ;

  let mut pos_splitter = Splitter::new(& real_instance, true, _profiler) ;

  while let Some(instance) = pos_splitter.next_instance() ? {
    let mut neg_splitter = Splitter::new(& instance, false, _profiler) ;

    while let Some(mut instance) = neg_splitter.next_instance() ? {
      debug_assert! { neg_models.is_empty() }

      if_verb! {
        if let Some((current, left)) = pos_splitter.info() {
          info! { "Splitting on positive clause {} of {}", current, left }
        }
        if let Some((current, left)) = neg_splitter.info() {
          info! { "Splitting on negative clause {} of {}", current, left }
        }
      }

      if let Some(candidates) = run_teacher(instance.clone(), _profiler) ? {
        let mut this_model = instance.model_of(candidates) ? ;
        if let Some(instance) = Arc::get_mut(& mut instance) {
          instance.simplify_pred_defs(& mut this_model) ?
        } else {
          bail!("could not obtain instance as mut...")
        }
        for (pred, tterms) in this_model {
          if ! real_instance.is_known(pred) {
            let conj = neg_models.entry(pred).or_insert_with(
              || vec![]
            ) ;
            if ! conj.iter().any( |tts| tts == & tterms ) {
              if let Some(true) = tterms.bool() {
                ()
              } else {
                conj.push(tterms)
              }
            }
          }
        }
      } else {
        return Ok(None)
      }

    }

    'update_model: for (pred, mut conj) in neg_models.drain() {
      let mut cnt = 0 ;
      while cnt < conj.len() {
        match conj[cnt].bool() {
          Some(false) => continue 'update_model,
          Some(true) => {
            conj.swap_remove(cnt) ;
          },
          None => cnt += 1,
        }
      }
      if ! conj.is_empty() {
        model.entry(pred).or_insert_with(
          || vec![]
        ).push(conj)
      }
    }

  }

  Ok( Some(model) )
}

/// Runs the teacher on an instance.
pub fn run_teacher(
  instance: Arc<Instance>, _profiler: & Profiler
) -> Res< Option<Candidates> > {
  let teacher_profiler = Profiler::new() ;
  profile! { |_profiler| tick "solving" }
  let solve_res = ::teacher::start_class(
    & instance, & teacher_profiler
  ) ;
  profile! { |_profiler| mark "solving" }
  print_stats("teacher", teacher_profiler) ;
  solve_res
}

/// Creates new instances by splitting positive/negative clauses.
pub struct Splitter<'a> {
  /// The instance we're working on.
  instance: Arc<Instance>,
  /// True if the splitter works on positive clauses.
  pos: bool,
  /// Clauses to look at separately. Indices refer to `self.instance`.
  ///
  /// `Right(once)` means that this splitting is inactive, and `next_instance`
  /// will return `self.instance` if `! once` and `None` otherwise.
  clauses: Either<Vec<ClsIdx>, bool>,
  /// Profiler.
  _profiler: & 'a Profiler,
}
impl<'a> Splitter<'a> {

  /// Constructor.
  pub fn new(
    instance: & Arc<Instance>, pos: bool,
    _profiler: & 'a Profiler
  ) -> Self {
    let clauses = if pos
    && conf.split_pos
    && instance.pos_clauses().len() > 1 {
      Either::Left(
        instance.pos_clauses().iter().map(|c| * c).collect()
      )
    } else if ! pos
    && conf.split_neg
    && instance.neg_clauses().len() > 1 {
      Either::Left(
        instance.neg_clauses().iter().map(|c| * c).collect()
      )
    } else {
      Either::Right(false)
    } ;
    let instance = instance.clone() ;
    // let model = Vec::new() ;
    Splitter {
      instance, // model,
      pos, clauses, _profiler
    }
  }

  /// Returns the number of clauses already treated and the total number of
  /// clauses to handle if active.
  pub fn info(& self) -> Option<(usize, usize)> {
    match self.clauses {
      Either::Left(ref clauses) => {
        let total = if self.pos {
          self.instance.pos_clauses().len()
        } else {
          self.instance.neg_clauses().len()
        } ;
        Some( (total - clauses.len(), total) )
      },
      Either::Right(_) => None,
    }
  }

  /// Returns the next instance to work on.
  pub fn next_instance(& mut self) -> Res< Option<Arc<Instance>> > {
    match self.clauses {
      Either::Left(ref mut clauses) => if let Some(clause) = clauses.pop() {
        Ok(
          Some(
            Arc::new(
              pre_proc(
                self.instance.as_ref(), (self.pos, clause), self._profiler
              ) ?
            )
          )
        )
      } else {
        Ok(None)
      },
      Either::Right(ref mut once) => if * once {
        Ok(None)
      } else {
        * once = true ;
        Ok( Some(self.instance.clone()) )
      }
    }
  }

}

/// Applies pre-processing to a modified instance.
///
/// Generates the instance obtained by removing all positive (if `pos`,
/// negative otherwise) clauses but `clause`. Preprocesses it and returns the
/// result.
fn pre_proc(
  instance: & Instance, (pos, clause): (bool, ClsIdx),
  profiler: & Profiler
) -> Res<Instance> {

  debug_assert! {
    ( ! pos || instance[clause].lhs_preds().is_empty() ) &&
    (   pos || instance[clause].rhs().is_none()        )
  }

  let mut instance = if pos {
    instance.clone_with_one_pos(clause)
  } else {
    instance.clone_with_one_neg(clause)
  } ;

  if conf.preproc.active {
    ::instance::preproc::work(& mut instance, profiler) ?
  }
  instance.finalize() ? ;

  Ok(instance)
}<commit_msg>minor<commit_after>//! Handles instance splitting.
//!
//! Used to reason separately on each positive/negative clause.

use common::* ;

/// Prints the stats if asked. Does nothing in bench mode.
#[cfg(feature = "bench")]
fn print_stats(_: & 'static str, _: Profiler) {}
/// Prints the stats if asked. Does nothing in bench mode.
#[cfg( not(feature = "bench") )]
fn print_stats(name: & 'static str, profiler: Profiler) {
  if conf.stats {
    println!("") ;
    profiler.print( name, "", & [ "data" ] ) ;
    println!("") ;
  }
}

/// Splits the instance if asked to do so, and solves it.
///
/// Assumes the instance is **already pre-processed**.
pub fn work(
  real_instance: Arc<Instance>, _profiler: & Profiler
) -> Res< Option<DnfCandidates> > {
  // Encodes a DNF.
  let mut model = DnfCandidates::new() ;
  let mut neg_models = PrdHMap::new() ;

  let mut pos_splitter = Splitter::new(& real_instance, true, _profiler) ;

  while let Some(instance) = pos_splitter.next_instance() ? {
    let mut neg_splitter = Splitter::new(& instance, false, _profiler) ;

    while let Some(mut instance) = neg_splitter.next_instance() ? {
      debug_assert! { neg_models.is_empty() }

      if_verb! {
        if let Some((current, left)) = pos_splitter.info() {
          info! { "Splitting on positive clause {} of {}", current, left }
        }
        if let Some((current, left)) = neg_splitter.info() {
          info! { "Splitting on negative clause {} of {}", current, left }
        }
      }

      if let Some(candidates) = run_teacher(instance.clone(), _profiler) ? {
        let mut this_model = instance.model_of(candidates) ? ;
        if let Some(instance) = Arc::get_mut(& mut instance) {
          instance.simplify_pred_defs(& mut this_model) ?
        }
        for (pred, tterms) in this_model {
          if ! real_instance.is_known(pred) {
            let conj = neg_models.entry(pred).or_insert_with(
              || vec![]
            ) ;
            if ! conj.iter().any( |tts| tts == & tterms ) {
              if let Some(true) = tterms.bool() {
                ()
              } else {
                conj.push(tterms)
              }
            }
          }
        }
      } else {
        return Ok(None)
      }

    }

    'update_model: for (pred, mut conj) in neg_models.drain() {
      let mut cnt = 0 ;
      while cnt < conj.len() {
        match conj[cnt].bool() {
          Some(false) => continue 'update_model,
          Some(true) => {
            conj.swap_remove(cnt) ;
          },
          None => cnt += 1,
        }
      }
      if ! conj.is_empty() {
        model.entry(pred).or_insert_with(
          || vec![]
        ).push(conj)
      }
    }

  }

  Ok( Some(model) )
}

/// Runs the teacher on an instance.
pub fn run_teacher(
  instance: Arc<Instance>, _profiler: & Profiler
) -> Res< Option<Candidates> > {
  let teacher_profiler = Profiler::new() ;
  profile! { |_profiler| tick "solving" }
  let solve_res = ::teacher::start_class(
    & instance, & teacher_profiler
  ) ;
  profile! { |_profiler| mark "solving" }
  print_stats("teacher", teacher_profiler) ;
  solve_res
}

/// Creates new instances by splitting positive/negative clauses.
pub struct Splitter<'a> {
  /// The instance we're working on.
  instance: Arc<Instance>,
  /// True if the splitter works on positive clauses.
  pos: bool,
  /// Clauses to look at separately. Indices refer to `self.instance`.
  ///
  /// `Right(once)` means that this splitting is inactive, and `next_instance`
  /// will return `self.instance` if `! once` and `None` otherwise.
  clauses: Either<Vec<ClsIdx>, bool>,
  /// Profiler.
  _profiler: & 'a Profiler,
}
impl<'a> Splitter<'a> {

  /// Constructor.
  pub fn new(
    instance: & Arc<Instance>, pos: bool,
    _profiler: & 'a Profiler
  ) -> Self {
    let clauses = if pos
    && conf.split_pos
    && instance.pos_clauses().len() > 1 {
      Either::Left(
        instance.pos_clauses().iter().map(|c| * c).collect()
      )
    } else if ! pos
    && conf.split_neg
    && instance.neg_clauses().len() > 1 {
      Either::Left(
        instance.neg_clauses().iter().map(|c| * c).collect()
      )
    } else {
      Either::Right(false)
    } ;
    let instance = instance.clone() ;
    // let model = Vec::new() ;
    Splitter {
      instance, // model,
      pos, clauses, _profiler
    }
  }

  /// Returns the number of clauses already treated and the total number of
  /// clauses to handle if active.
  pub fn info(& self) -> Option<(usize, usize)> {
    match self.clauses {
      Either::Left(ref clauses) => {
        let total = if self.pos {
          self.instance.pos_clauses().len()
        } else {
          self.instance.neg_clauses().len()
        } ;
        Some( (total - clauses.len(), total) )
      },
      Either::Right(_) => None,
    }
  }

  /// Returns the next instance to work on.
  pub fn next_instance(& mut self) -> Res< Option<Arc<Instance>> > {
    match self.clauses {
      Either::Left(ref mut clauses) => if let Some(clause) = clauses.pop() {
        Ok(
          Some(
            Arc::new(
              pre_proc(
                self.instance.as_ref(), (self.pos, clause), self._profiler
              ) ?
            )
          )
        )
      } else {
        Ok(None)
      },
      Either::Right(ref mut once) => if * once {
        Ok(None)
      } else {
        * once = true ;
        Ok( Some(self.instance.clone()) )
      }
    }
  }

}

/// Applies pre-processing to a modified instance.
///
/// Generates the instance obtained by removing all positive (if `pos`,
/// negative otherwise) clauses but `clause`. Preprocesses it and returns the
/// result.
fn pre_proc(
  instance: & Instance, (pos, clause): (bool, ClsIdx),
  profiler: & Profiler
) -> Res<Instance> {

  debug_assert! {
    ( ! pos || instance[clause].lhs_preds().is_empty() ) &&
    (   pos || instance[clause].rhs().is_none()        )
  }

  let mut instance = if pos {
    instance.clone_with_one_pos(clause)
  } else {
    instance.clone_with_one_neg(clause)
  } ;

  if conf.preproc.active {
    ::instance::preproc::work(& mut instance, profiler) ?
  }
  instance.finalize() ? ;

  Ok(instance)
}<|endoftext|>


<commit_before>use super::*;

fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

#[test]
pub fn channel_is_send() {
    assert_send::<Channel>();
}

#[test]
pub fn error_is_send() {
    assert_send::<Error>();
}

#[test]
pub fn options_is_send() {
    assert_send::<Options>();
}

#[test]
pub fn get_sock_is_send() {
    assert_send::<GetSock>();
}

#[test]
pub fn get_sock_iter_is_send() {
    assert_send::<GetSockIter>();
}

#[test]
pub fn a_result_is_send() {
    assert_send::<AResult>();
}

#[test]
pub fn a_results_are_send() {
    assert_send::<AResults>();
}

#[test]
pub fn a_results_iter_is_send() {
    assert_send::<AResultsIter>();
}

#[test]
pub fn aaaa_result_is_send() {
    assert_send::<AAAAResult>();
}

#[test]
pub fn aaaa_results_are_send() {
    assert_send::<AAAAResults>();
}

#[test]
pub fn aaaa_results_iter_is_send() {
    assert_send::<AAAAResultsIter>();
}

#[test]
pub fn cname_results_are_send() {
    assert_send::<CNameResults>();
}

#[test]
pub fn host_address_results_iter_is_send() {
    assert_send::<HostAddressResultsIter>();
}

#[test]
pub fn host_alias_results_iter_is_send() {
    assert_send::<HostAliasResultsIter>();
}

#[test]
pub fn host_results_are_send() {
    assert_send::<HostResults>();
}

#[test]
pub fn mx_result_is_send() {
    assert_send::<MXResult>();
}

#[test]
pub fn mx_results_are_send() {
    assert_send::<MXResults>();
}

#[test]
pub fn mx_results_iter_is_send() {
    assert_send::<MXResultsIter>();
}

#[test]
pub fn naptr_result_is_send() {
    assert_send::<NAPTRResult>();
}

#[test]
pub fn naptr_results_are_send() {
    assert_send::<NAPTRResults>();
}

#[test]
pub fn naptr_results_iter_is_send() {
    assert_send::<NAPTRResultsIter>();
}

#[test]
pub fn ns_results_are_send() {
    assert_send::<NSResults>();
}

#[test]
pub fn ptr_results_are_send() {
    assert_send::<PTRResults>();
}

#[test]
pub fn srv_result_is_send() {
    assert_send::<SRVResult>();
}

#[test]
pub fn srv_results_are_send() {
    assert_send::<SRVResults>();
}

#[test]
pub fn srv_results_iter_is_send() {
    assert_send::<SRVResultsIter>();
}

#[test]
pub fn txt_result_is_send() {
    assert_send::<TXTResult>();
}

#[test]
pub fn txt_results_are_send() {
    assert_send::<TXTResults>();
}

#[test]
pub fn txt_results_iter_is_send() {
    assert_send::<TXTResultsIter>();
}

#[test]
pub fn channel_is_sync() {
    assert_sync::<Channel>();
}

#[test]
pub fn error_is_sync() {
    assert_sync::<Error>();
}

#[test]
pub fn options_is_sync() {
    assert_sync::<Options>();
}

#[test]
pub fn get_sock_is_sync() {
    assert_sync::<GetSock>();
}

#[test]
pub fn get_sock_iter_is_sync() {
    assert_sync::<GetSockIter>();
}

#[test]
pub fn a_result_is_sync() {
    assert_sync::<AResult>();
}

#[test]
pub fn a_results_are_sync() {
    assert_sync::<AResults>();
}

#[test]
pub fn a_results_iter_is_sync() {
    assert_sync::<AResultsIter>();
}

#[test]
pub fn aaaa_result_is_sync() {
    assert_sync::<AAAAResult>();
}

#[test]
pub fn aaaa_results_are_sync() {
    assert_sync::<AAAAResults>();
}

#[test]
pub fn aaaa_results_iter_is_sync() {
    assert_sync::<AAAAResultsIter>();
}

#[test]
pub fn cname_results_are_sync() {
    assert_sync::<CNameResults>();
}

#[test]
pub fn host_address_results_iter_is_sync() {
    assert_sync::<HostAddressResultsIter>();
}

#[test]
pub fn host_alias_results_iter_is_sync() {
    assert_sync::<HostAliasResultsIter>();
}

#[test]
pub fn host_results_are_sync() {
    assert_sync::<HostResults>();
}

#[test]
pub fn mx_result_is_sync() {
    assert_sync::<MXResult>();
}

#[test]
pub fn mx_results_are_sync() {
    assert_sync::<MXResults>();
}

#[test]
pub fn mx_results_iter_is_sync() {
    assert_sync::<MXResultsIter>();
}

#[test]
pub fn naptr_results_are_sync() {
    assert_sync::<NAPTRResults>();
}

#[test]
pub fn naptr_result_is_sync() {
    assert_sync::<NAPTRResult>();
}

#[test]
pub fn naptr_results_iter_is_sync() {
    assert_sync::<NAPTRResultsIter>();
}

#[test]
pub fn ns_results_are_sync() {
    assert_sync::<NSResults>();
}

#[test]
pub fn ptr_results_are_sync() {
    assert_sync::<PTRResults>();
}

#[test]
pub fn srv_result_is_sync() {
    assert_sync::<SRVResult>();
}

#[test]
pub fn srv_results_are_sync() {
    assert_sync::<SRVResults>();
}

#[test]
pub fn srv_results_iter_is_sync() {
    assert_sync::<SRVResultsIter>();
}

#[test]
pub fn txt_result_is_sync() {
    assert_sync::<TXTResult>();
}

#[test]
pub fn txt_results_are_sync() {
    assert_sync::<TXTResults>();
}

#[test]
pub fn txt_results_iter_is_sync() {
    assert_sync::<TXTResultsIter>();
}
<commit_msg>unnecessary pub on tests<commit_after>use super::*;

fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

#[test]
fn channel_is_send() {
    assert_send::<Channel>();
}

#[test]
fn error_is_send() {
    assert_send::<Error>();
}

#[test]
fn options_is_send() {
    assert_send::<Options>();
}

#[test]
fn get_sock_is_send() {
    assert_send::<GetSock>();
}

#[test]
fn get_sock_iter_is_send() {
    assert_send::<GetSockIter>();
}

#[test]
fn a_result_is_send() {
    assert_send::<AResult>();
}

#[test]
fn a_results_are_send() {
    assert_send::<AResults>();
}

#[test]
fn a_results_iter_is_send() {
    assert_send::<AResultsIter>();
}

#[test]
fn aaaa_result_is_send() {
    assert_send::<AAAAResult>();
}

#[test]
fn aaaa_results_are_send() {
    assert_send::<AAAAResults>();
}

#[test]
fn aaaa_results_iter_is_send() {
    assert_send::<AAAAResultsIter>();
}

#[test]
fn cname_results_are_send() {
    assert_send::<CNameResults>();
}

#[test]
fn host_address_results_iter_is_send() {
    assert_send::<HostAddressResultsIter>();
}

#[test]
fn host_alias_results_iter_is_send() {
    assert_send::<HostAliasResultsIter>();
}

#[test]
fn host_results_are_send() {
    assert_send::<HostResults>();
}

#[test]
fn mx_result_is_send() {
    assert_send::<MXResult>();
}

#[test]
fn mx_results_are_send() {
    assert_send::<MXResults>();
}

#[test]
fn mx_results_iter_is_send() {
    assert_send::<MXResultsIter>();
}

#[test]
fn naptr_result_is_send() {
    assert_send::<NAPTRResult>();
}

#[test]
fn naptr_results_are_send() {
    assert_send::<NAPTRResults>();
}

#[test]
fn naptr_results_iter_is_send() {
    assert_send::<NAPTRResultsIter>();
}

#[test]
fn ns_results_are_send() {
    assert_send::<NSResults>();
}

#[test]
fn ptr_results_are_send() {
    assert_send::<PTRResults>();
}

#[test]
fn srv_result_is_send() {
    assert_send::<SRVResult>();
}

#[test]
fn srv_results_are_send() {
    assert_send::<SRVResults>();
}

#[test]
fn srv_results_iter_is_send() {
    assert_send::<SRVResultsIter>();
}

#[test]
fn txt_result_is_send() {
    assert_send::<TXTResult>();
}

#[test]
fn txt_results_are_send() {
    assert_send::<TXTResults>();
}

#[test]
fn txt_results_iter_is_send() {
    assert_send::<TXTResultsIter>();
}

#[test]
fn channel_is_sync() {
    assert_sync::<Channel>();
}

#[test]
fn error_is_sync() {
    assert_sync::<Error>();
}

#[test]
fn options_is_sync() {
    assert_sync::<Options>();
}

#[test]
fn get_sock_is_sync() {
    assert_sync::<GetSock>();
}

#[test]
fn get_sock_iter_is_sync() {
    assert_sync::<GetSockIter>();
}

#[test]
fn a_result_is_sync() {
    assert_sync::<AResult>();
}

#[test]
fn a_results_are_sync() {
    assert_sync::<AResults>();
}

#[test]
fn a_results_iter_is_sync() {
    assert_sync::<AResultsIter>();
}

#[test]
fn aaaa_result_is_sync() {
    assert_sync::<AAAAResult>();
}

#[test]
fn aaaa_results_are_sync() {
    assert_sync::<AAAAResults>();
}

#[test]
fn aaaa_results_iter_is_sync() {
    assert_sync::<AAAAResultsIter>();
}

#[test]
fn cname_results_are_sync() {
    assert_sync::<CNameResults>();
}

#[test]
fn host_address_results_iter_is_sync() {
    assert_sync::<HostAddressResultsIter>();
}

#[test]
fn host_alias_results_iter_is_sync() {
    assert_sync::<HostAliasResultsIter>();
}

#[test]
fn host_results_are_sync() {
    assert_sync::<HostResults>();
}

#[test]
fn mx_result_is_sync() {
    assert_sync::<MXResult>();
}

#[test]
fn mx_results_are_sync() {
    assert_sync::<MXResults>();
}

#[test]
fn mx_results_iter_is_sync() {
    assert_sync::<MXResultsIter>();
}

#[test]
fn naptr_results_are_sync() {
    assert_sync::<NAPTRResults>();
}

#[test]
fn naptr_result_is_sync() {
    assert_sync::<NAPTRResult>();
}

#[test]
fn naptr_results_iter_is_sync() {
    assert_sync::<NAPTRResultsIter>();
}

#[test]
fn ns_results_are_sync() {
    assert_sync::<NSResults>();
}

#[test]
fn ptr_results_are_sync() {
    assert_sync::<PTRResults>();
}

#[test]
fn srv_result_is_sync() {
    assert_sync::<SRVResult>();
}

#[test]
fn srv_results_are_sync() {
    assert_sync::<SRVResults>();
}

#[test]
fn srv_results_iter_is_sync() {
    assert_sync::<SRVResultsIter>();
}

#[test]
fn txt_result_is_sync() {
    assert_sync::<TXTResult>();
}

#[test]
fn txt_results_are_sync() {
    assert_sync::<TXTResults>();
}

#[test]
fn txt_results_iter_is_sync() {
    assert_sync::<TXTResultsIter>();
}
<|endoftext|>


<commit_before>#![feature(proc_macro)]
#![no_std]

extern crate cortex_m_rtfm as rtfm;
extern crate stm32f30x;

use core::u16;
use rtfm::{app, Threshold};

app! {
    device: stm32f30x,

    resources: {
        static ON: bool = false;
    },

    tasks: {
        TIM7: {
            path: toggle,
            resources: [ON, GPIOE],
        },
    },
}

fn init(p: init::Peripherals, _r: init::Resources) {
    let gpioe = p.GPIOE;
    let rcc = p.RCC;
    let tim7 = p.TIM7;

    rcc.ahbenr.modify(|_, w| w.iopeen().enabled());
    rcc.apb1enr.modify(|_, w| w.tim7en().enabled());

    gpioe.moder.modify(|_, w| w.moder9().output());

    let ratio = 8_000_000;
    let psc = ((ratio - 1) /(u16::MAX as u32)) as u16;
    tim7.psc.write(|w| w.psc().bits(psc));
    let arr = (ratio / ((psc + 1) as u32)) as u16;
    tim7.arr.write(|w| w.arr().bits(arr));
    tim7.cr1.write(|w| w.opm().continuous());

    tim7.dier.write(|w| w.uie().bit(true));

    tim7.cr1.modify(|_, w| w.cen().enabled());
}

fn idle() -> ! {
    loop {
        rtfm::wfi();
    }
}

fn toggle(_t: &mut Threshold, r: TIM7::Resources) {
    // Toggle our state
    **r.ON = !**r.ON;

    let gpioe = &**r.GPIOE;
    if **r.ON {
        gpioe.bsrr.write(|w| w.bs9().set());
    } else {
        gpioe.bsrr.write(|w| w.br9().reset());
    }
}

<commit_msg>Add RTFM based firmware for interrupt driven blinky<commit_after>#![feature(proc_macro)]
#![no_std]

extern crate cortex_m_rtfm as rtfm;
extern crate stm32f30x;
extern crate cortex_m_semihosting;

use core::u16;
use rtfm::{app, Threshold};

use core::fmt::Write;
use cortex_m_semihosting::hio;

app! {
    device: stm32f30x,

    resources: {
        static ON: bool = false;
        static COUNTER: u32 = 0;
    },

    tasks: {
        TIM7: {
            path: toggle,
            resources: [ON, COUNTER, GPIOA],
        },
    },
}

fn init(p: init::Peripherals, _r: init::Resources) {
    let gpioa = p.GPIOA;
    let rcc = p.RCC;
    let tim7 = p.TIM7;

    rcc.ahbenr.modify(|_, w| w.iopaen().enabled());
    rcc.apb1enr.modify(|_, w| w.tim7en().enabled());

    gpioa.moder.modify(|_, w| w.moder9().output());

    let ratio = 8_000_000;
    //let psc = ((ratio - 1) /(u16::MAX as u32)) as u16;
    let psc = u16::MAX - 100;
    tim7.psc.write(|w| w.psc().bits(psc));
    let arr = (ratio / ((psc + 1) as u32)) as u16;
    tim7.arr.write(|w| w.arr().bits(arr));
    tim7.cr1.write(|w| w.opm().continuous());

    tim7.dier.write(|w| w.uie().bit(true));

    tim7.cr1.modify(|_, w| w.cen().enabled());
}

fn idle() -> ! {
    loop {
        rtfm::wfi();
    }
}

fn toggle(_t: &mut Threshold, r: TIM7::Resources) {
    if (**r.COUNTER > 10000) {
        // Toggle our state
        **r.ON = !**r.ON;
        **r.COUNTER = 0;
        let gpioa = &**r.GPIOA;
        if **r.ON {
            gpioa.bsrr.write(|w| w.bs9().set());
        } else {
            gpioa.bsrr.write(|w| w.br9().reset());
        }
    } else {
        **r.COUNTER += 1;
    }
}

<|endoftext|>


<commit_before>//! Types used in this bot implementation.

use std::fmt;

use errors::CommandParseError;

/// A stateless command.
///
/// A command consists of a name and a list of parameters.
///
/// # Example
///
/// The string
///
///     /join channel1 channel2
///
/// ...will become...
///
///     Command {
///         name: "join",
///         params: ["channel1", "channel2"]
///     }
#[derive(Debug, Clone)]
pub struct Command {
    /// Command name, without a leading slash character.
    pub name: String,
    /// List of command parameters. May be empty.
    pub params: Vec<String>,
}

/// Parse a text message, return a command if possible
impl<'a> ::conv::TryFrom<&'a str> for Command {
    type Err = CommandParseError;
    fn try_from(text: &'a str) -> Result<Self, CommandParseError> {

        // Verify if this is actually a command
        if !text.starts_with("/") {
            return Err(CommandParseError::NoCommand);
        }

        // Split text into words iterator
        let mut words = text.split(' ');

        // Parse out name and params
        if let Some(command) = words.next() {

            // Strip leading slash
            let name: String = command[1..].into();

            // If name is an empty string, it's not valid
            if name.len() == 0 {
                return Err(CommandParseError::NoCommand);
            }

            // Parse parameters
            let params: Vec<String> = words.map(|s| s.into()).collect();

            // Return command
            Ok(Command {
                name: name.into(),
                params: params,
            })

        } else {
            Err(CommandParseError::NoCommand)
        }
    }
}

impl fmt::Display for Command {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "/{} {}", self.name, self.params.join(" "))
    }
}

#[cfg(test)]
mod tests {
    use super::Command;
    use conv::TryFrom;

    #[test]
    fn command_parse_str_ok() {
        let text_simple: String = "/help".into();
        let text_params = "/list all";

        let command_simple = Command::try_from(&*text_simple).unwrap();
        let command_params = Command::try_from(text_params).unwrap();

        assert_eq!(command_simple.name, "help");
        assert_eq!(command_simple.params, Vec::<String>::new());
        assert_eq!(command_params.name, "list");
        assert_eq!(command_params.params, vec!["all"]);
    }

    #[test]
    fn command_parse_string_err() {
        let texts = vec![
            "",
            "/",
            "no initial slash",
            " /preceding space",
            "/ params but no name",
        ];

        for &text in texts.iter() {
            assert!(Command::try_from(text).is_err());
        }
    }

}
<commit_msg>Add support for @ character in command names<commit_after>//! Types used in this bot implementation.

use std::fmt;

use errors::CommandParseError;

/// A stateless command.
///
/// A command consists of a name and a list of parameters.
///
/// # Example
///
/// The string
///
///     /join channel1 channel2
///
/// ...will become...
///
///     Command {
///         name: "join",
///         params: ["channel1", "channel2"]
///     }
#[derive(Debug, Clone)]
pub struct Command {
    /// Command name, without a leading slash character.
    pub name: String,
    /// List of command parameters. May be empty.
    pub params: Vec<String>,
}

/// Parse a text message, return a command if possible
impl<'a> ::conv::TryFrom<&'a str> for Command {
    type Err = CommandParseError;
    fn try_from(text: &'a str) -> Result<Self, CommandParseError> {

        // Verify if this is actually a command
        if !text.starts_with("/") {
            return Err(CommandParseError::NoCommand);
        }

        // Split text into words iterator
        let mut words = text.split(' ');

        // Parse out name and params
        if let Some(command) = words.next() {

            // Strip leading slash and bot name following the @ symbol
            let name: String = try!(command[1..].split('@').next().ok_or(CommandParseError::NoCommand)).into();

            // If name is an empty string, it's not valid
            if name.len() == 0 {
                return Err(CommandParseError::NoCommand);
            }

            // Parse parameters
            let params: Vec<String> = words.map(|s| s.into()).collect();

            // Return command
            Ok(Command {
                name: name.into(),
                params: params,
            })

        } else {
            Err(CommandParseError::NoCommand)
        }
    }
}

impl fmt::Display for Command {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "/{} {}", self.name, self.params.join(" "))
    }
}

#[cfg(test)]
mod tests {
    use super::Command;
    use conv::TryFrom;

    #[test]
    fn command_parse_str_ok() {
        let text_simple: String = "/help".into();
        let text_params = "/list all";
        let text_directed = "/list@TestBot all";

        let command_simple = Command::try_from(&*text_simple).unwrap();
        let command_params = Command::try_from(text_params).unwrap();
        let command_directed = Command::try_from(text_directed).unwrap();

        assert_eq!(command_simple.name, "help");
        assert_eq!(command_simple.params, Vec::<String>::new());
        assert_eq!(command_params.name, "list");
        assert_eq!(command_params.params, vec!["all"]);
        assert_eq!(command_directed.name, "list");
        assert_eq!(command_directed.params, vec!["all"]);
    }

    #[test]
    fn command_parse_string_err() {
        let texts = vec![
            "",
            "/",
            "no initial slash",
            " /preceding space",
            "/ params but no name",
            "/@BotName but no command name",
        ];

        for &text in texts.iter() {
            assert!(Command::try_from(text).is_err());
        }
    }

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the completion language feature
use crate::lsp::Position;
use common::{FileKey, Span};
use graphql_syntax::{parse, Document, GraphQLSource};
use interner::StringKey;
use log::info;

use relay_compiler::Programs;

use crate::lsp::{
    CompletionItem, CompletionParams, CompletionResponse, Connection, Message, ServerRequestId,
    ServerResponse, TextDocumentPositionParams, Url,
};
use schema::{Schema, Type, TypeWithFields};

use graphql_syntax::{
    ExecutableDefinition, LinkedField, List, OperationDefinition, OperationKind, Selection,
};

pub type GraphQLSourceCache = std::collections::HashMap<Url, Vec<GraphQLSource>>;

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum CompletionKind {
    FieldName,
    FragmentSpread,
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct CompletionRequest {
    /// The type of the completion request we're responding to
    kind: CompletionKind,
    /// A list of type metadata that we can use to resolve the leaf
    /// type the request is being made against
    type_path: Vec<TypePathItem>,
}

impl Default for CompletionRequest {
    fn default() -> Self {
        CompletionRequest {
            kind: CompletionKind::FieldName,
            type_path: vec![],
        }
    }
}

impl CompletionRequest {
    fn add_type(&mut self, type_path_item: TypePathItem) {
        self.type_path.push(type_path_item)
    }

    /// Returns the leaf type, which is the type that the completion request is being made against.
    fn resolve_leaf_type(self, schema: &Schema) -> Type {
        let mut type_path = self.type_path;
        type_path.reverse();
        let mut type_ = resolve_root_type(type_path.pop().expect("path must be non-empty"), schema);
        while let Some(path_item) = type_path.pop() {
            type_ = resolve_relative_type(type_, path_item, schema);
        }
        type_
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum TypePathItem {
    Operation(OperationKind),
    FragmentDefinition { type_name: StringKey },
    LinkedField { name: StringKey },
}

pub fn create_completion_request(document: Document, position_span: Span) -> CompletionRequest {
    info!("Building completion path for {:#?}", document);
    let mut completion_request = CompletionRequest::default();

    for definition in document.definitions {
        match &definition {
            ExecutableDefinition::Operation(operation) => {
                if operation.location.contains(position_span) {
                    // TODO don't unwrap here
                    let (_, kind) = operation.operation.clone().unwrap();
                    completion_request.add_type(TypePathItem::Operation(kind));

                    info!(
                        "Completion request is within operation: {:?}",
                        operation.name
                    );
                    let OperationDefinition { selections, .. } = operation;

                    if selections.span.contains(position_span) {
                        // TODO(brandondail) handle when the completion occurs at/within the start token
                        info!("Completion request is within a selection");
                        populate_completion_request_from_selection(
                            selections,
                            position_span,
                            &mut completion_request,
                        );
                    }
                }
                // Check if the position span is within this operation's span
            }
            ExecutableDefinition::Fragment(fragment) => {
                if fragment.location.contains(position_span) {
                    let type_name = fragment.type_condition.type_.value;
                    completion_request.add_type(TypePathItem::FragmentDefinition { type_name });
                    if fragment.selections.span.contains(position_span) {
                        populate_completion_request_from_selection(
                            &fragment.selections,
                            position_span,
                            &mut completion_request,
                        );
                    }
                }
            }
        }
    }

    completion_request
}

/// Resolves the root type of this completion path.
fn resolve_root_type(root_path_item: TypePathItem, schema: &Schema) -> Type {
    match root_path_item {
        TypePathItem::Operation(kind) => match kind {
            OperationKind::Query => schema.query_type().unwrap(),
            OperationKind::Mutation => schema.mutation_type().unwrap(),
            OperationKind::Subscription => schema.subscription_type().unwrap(),
        },
        TypePathItem::FragmentDefinition { type_name } => schema.get_type(type_name).unwrap(),
        TypePathItem::LinkedField { .. } => {
            // TODO(brandondail) fail silently and log here instead
            panic!("Completion paths must start with an operation or fragment")
        }
    }
}

fn resolve_relative_type(parent_type: Type, path_item: TypePathItem, schema: &Schema) -> Type {
    match path_item {
        TypePathItem::Operation(_) => {
            // TODO(brandondail) fail silently and log here instead
            panic!("Operations must only exist at the root of the completion path");
        }
        TypePathItem::FragmentDefinition { .. } => {
            // TODO(brandondail) fail silently and log here instead
            panic!("Fragments must only exist at the root of the completion path");
        }
        TypePathItem::LinkedField { name } => {
            let field_id = schema.named_field(parent_type, name).unwrap();
            let field = schema.field(field_id);
            info!("resolved type for {:?} : {:?}", field.name, field.type_);
            field.type_.inner()
        }
    }
}

fn resolve_completion_items_from_fields<T: TypeWithFields>(
    type_: &T,
    schema: &Schema,
) -> Vec<CompletionItem> {
    type_
        .fields()
        .iter()
        .map(|field_id| {
            let field = schema.field(*field_id);
            let name = field.name.to_string();
            CompletionItem::new_simple(name, String::from(""))
        })
        .collect()
}

/// Finds all the valid fragment names for a given type. Used to complete fragment spreads
fn get_valid_fragments_for_type(type_: Type, programs: &Programs<'_>) -> Vec<StringKey> {
    let mut valid_fragment_names = vec![];
    let fragment_map = programs.source.fragment_map();
    for (fragment_name, fragment) in fragment_map {
        if fragment.type_condition == type_ {
            valid_fragment_names.push(*fragment_name);
        }
    }
    info!("get_valid_fragments_for_type {:#?}", valid_fragment_names);
    valid_fragment_names
}

fn resolve_completion_items_for_fragment_spread(
    type_: Type,
    programs: &Programs<'_>,
) -> Vec<CompletionItem> {
    get_valid_fragments_for_type(type_, programs)
        .iter()
        .map(|fragment_name| {
            CompletionItem::new_simple(fragment_name.to_string(), String::from(""))
        })
        .collect()
}

pub fn completion_items_for_request(
    request: CompletionRequest,
    schema: &Schema,
    programs: Option<&Programs<'_>>,
) -> Option<Vec<CompletionItem>> {
    let kind = request.kind;
    let leaf_type = request.resolve_leaf_type(schema);
    info!("completion_items_for_request: {:?} - {:?}", leaf_type, kind);
    match kind {
        CompletionKind::FragmentSpread => {
            if let Some(programs) = programs {
                let items = resolve_completion_items_for_fragment_spread(leaf_type, programs);
                Some(items)
            } else {
                None
            }
        }
        CompletionKind::FieldName => match leaf_type {
            Type::Interface(interface_id) => {
                let interface = schema.interface(interface_id);
                let items = resolve_completion_items_from_fields(interface, schema);
                Some(items)
            }
            Type::Object(object_id) => {
                let object = schema.object(object_id);
                let items = resolve_completion_items_from_fields(object, schema);
                Some(items)
            }
            Type::Enum(_) | Type::InputObject(_) | Type::Scalar(_) | Type::Union(_) => None,
        },
    }
}

fn populate_completion_request_from_selection(
    selections: &List<Selection>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    for item in &selections.items {
        if item.span().contains(position_span) {
            match item {
                Selection::LinkedField(node) => {
                    completion_request.kind = CompletionKind::FieldName;
                    let LinkedField {
                        name, selections, ..
                    } = node;
                    completion_request.add_type(TypePathItem::LinkedField { name: name.value });
                    populate_completion_request_from_selection(
                        selections,
                        position_span,
                        completion_request,
                    );
                }
                Selection::FragmentSpread(_) => {
                    completion_request.kind = CompletionKind::FragmentSpread;
                }
                Selection::ScalarField(_node) => {}
                Selection::InlineFragment(_node) => {}
            }
        }
    }
}

/// Maps the LSP `Position` type back to a relative span, so we can find out which syntax node(s)
/// this completion request came from
pub fn position_to_span(position: Position, source: &GraphQLSource) -> Option<Span> {
    let mut index_of_last_line = 0;
    let mut line_index = source.line_index as u64;

    let mut chars = source.text.chars().enumerate().peekable();

    while let Some((index, chr)) = chars.next() {
        let is_newline = match chr {
            // Line terminators: https://www.ecma-international.org/ecma-262/#sec-line-terminators
            '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => match (chr, chars.peek()) {
                // <CLRF>
                ('\u{000D}', Some((_, '\u{000D}'))) => false,
                _ => true,
            },
            _ => false,
        };

        if is_newline {
            line_index += 1;
            index_of_last_line = index as u64;
        }

        if line_index == position.line {
            let start_offset = index_of_last_line + position.character;
            return Some(Span::new(start_offset as u32, 0));
        }
    }
    None
}

pub fn send_completion_response(
    items: Vec<CompletionItem>,
    request_id: ServerRequestId,
    connection: &Connection,
) {
    info!("send_completion_response: {:#?}", items);
    // If there are no items, don't send any response
    if items.is_empty() {
        return;
    }
    let completion_response = CompletionResponse::Array(items);
    let result = serde_json::to_value(&completion_response).unwrap();
    let response = ServerResponse {
        id: request_id,
        error: None,
        result: Some(result),
    };
    connection.sender.send(Message::Response(response)).unwrap();
}

/// Return a `CompletionPath` for this request, only if the completion request occurs
// within a GraphQL document. Otherwise return `None`
pub fn get_completion_request(
    params: CompletionParams,
    graphql_source_cache: &GraphQLSourceCache,
) -> Option<CompletionRequest> {
    let CompletionParams {
        text_document_position,
        ..
    } = params;
    let TextDocumentPositionParams {
        text_document,
        position,
    } = text_document_position;
    let url = text_document.uri;
    let graphql_sources = match graphql_source_cache.get(&url) {
        Some(sources) => sources,
        // If we have no sources for this file, do nothing
        None => return None,
    };

    info!(
        "Got completion request for file with sources: {:#?}",
        *graphql_sources
    );

    info!("position: {:?}", position);

    // We have GraphQL documents, now check if the completion request
    // falls within the range of one of these documents.
    let mut target_graphql_source: Option<&GraphQLSource> = None;
    for graphql_source in &*graphql_sources {
        let range = graphql_source.to_range();
        if position >= range.start && position <= range.end {
            target_graphql_source = Some(graphql_source);
            break;
        }
    }

    let graphql_source = match target_graphql_source {
        Some(source) => source,
        // Exit early if this completion request didn't fall within
        // the range of one of our GraphQL documents
        None => return None,
    };

    match parse(&graphql_source.text, FileKey::new(&url.to_string())) {
        Ok(document) => {
            // Now we need to take the `Position` and map that to an offset relative
            // to this GraphQL document, as the `Span`s in the document are relative.
            info!("Successfully parsed the definitions for a target GraphQL source");
            // Map the position to a zero-length span, relative to this GraphQL source.
            let position_span = match position_to_span(position, &graphql_source) {
                Some(span) => span,
                // Exit early if we can't map the position for some reason
                None => return None,
            };
            // Now we need to walk the Document, tracking our path along the way, until
            // we find the position within the document. Note that the GraphQLSource will
            // already be updated *with the characters that triggered the completion request*
            // since the change event fires before completion.
            info!("position_span: {:?}", position_span);
            let completion_request = create_completion_request(document, position_span);
            info!("Completion path: {:#?}", completion_request);
            Some(completion_request)
        }
        Err(err) => {
            info!("Failed to parse this target!");
            info!("{:?}", err);
            None
        }
    }
}
<commit_msg>add support for field completion within inline fragments<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the completion language feature
use crate::lsp::Position;
use common::{FileKey, Span};
use graphql_syntax::{parse, Document, GraphQLSource};
use interner::StringKey;
use log::info;

use relay_compiler::Programs;

use crate::lsp::{
    CompletionItem, CompletionParams, CompletionResponse, Connection, Message, ServerRequestId,
    ServerResponse, TextDocumentPositionParams, Url,
};
use schema::{Schema, Type, TypeWithFields};

use graphql_syntax::{
    ExecutableDefinition, InlineFragment, LinkedField, List, OperationDefinition, OperationKind,
    Selection,
};

pub type GraphQLSourceCache = std::collections::HashMap<Url, Vec<GraphQLSource>>;

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum CompletionKind {
    FieldName,
    FragmentSpread,
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct CompletionRequest {
    /// The type of the completion request we're responding to
    kind: CompletionKind,
    /// A list of type metadata that we can use to resolve the leaf
    /// type the request is being made against
    type_path: Vec<TypePathItem>,
}

impl Default for CompletionRequest {
    fn default() -> Self {
        CompletionRequest {
            kind: CompletionKind::FieldName,
            type_path: vec![],
        }
    }
}

impl CompletionRequest {
    fn add_type(&mut self, type_path_item: TypePathItem) {
        self.type_path.push(type_path_item)
    }

    /// Returns the leaf type, which is the type that the completion request is being made against.
    fn resolve_leaf_type(self, schema: &Schema) -> Type {
        let mut type_path = self.type_path;
        type_path.reverse();
        let mut type_ = resolve_root_type(type_path.pop().expect("path must be non-empty"), schema);
        while let Some(path_item) = type_path.pop() {
            type_ = resolve_relative_type(type_, path_item, schema);
        }
        type_
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum TypePathItem {
    Operation(OperationKind),
    FragmentDefinition { type_name: StringKey },
    InlineFragment { type_name: StringKey },
    LinkedField { name: StringKey },
}

pub fn create_completion_request(document: Document, position_span: Span) -> CompletionRequest {
    info!("Building completion path for {:#?}", document);
    let mut completion_request = CompletionRequest::default();

    for definition in document.definitions {
        match &definition {
            ExecutableDefinition::Operation(operation) => {
                if operation.location.contains(position_span) {
                    // TODO don't unwrap here
                    let (_, kind) = operation.operation.clone().unwrap();
                    completion_request.add_type(TypePathItem::Operation(kind));

                    info!(
                        "Completion request is within operation: {:?}",
                        operation.name
                    );
                    let OperationDefinition { selections, .. } = operation;

                    if selections.span.contains(position_span) {
                        // TODO(brandondail) handle when the completion occurs at/within the start token
                        info!("Completion request is within a selection");
                        populate_completion_request_from_selection(
                            selections,
                            position_span,
                            &mut completion_request,
                        );
                    }
                }
                // Check if the position span is within this operation's span
            }
            ExecutableDefinition::Fragment(fragment) => {
                if fragment.location.contains(position_span) {
                    let type_name = fragment.type_condition.type_.value;
                    completion_request.add_type(TypePathItem::FragmentDefinition { type_name });
                    if fragment.selections.span.contains(position_span) {
                        populate_completion_request_from_selection(
                            &fragment.selections,
                            position_span,
                            &mut completion_request,
                        );
                    }
                }
            }
        }
    }

    completion_request
}

/// Resolves the root type of this completion path.
fn resolve_root_type(root_path_item: TypePathItem, schema: &Schema) -> Type {
    match root_path_item {
        TypePathItem::Operation(kind) => match kind {
            OperationKind::Query => schema.query_type().unwrap(),
            OperationKind::Mutation => schema.mutation_type().unwrap(),
            OperationKind::Subscription => schema.subscription_type().unwrap(),
        },
        TypePathItem::FragmentDefinition { type_name } => schema.get_type(type_name).unwrap(),
        TypePathItem::LinkedField { .. } | TypePathItem::InlineFragment { .. } => {
            // TODO(brandondail) fail silently and log here instead
            panic!("Completion paths must start with an operation or fragment")
        }
    }
}

fn resolve_relative_type(parent_type: Type, path_item: TypePathItem, schema: &Schema) -> Type {
    match path_item {
        TypePathItem::Operation(_) => {
            // TODO(brandondail) fail silently and log here instead
            panic!("Operations must only exist at the root of the completion path");
        }
        TypePathItem::FragmentDefinition { .. } => {
            // TODO(brandondail) fail silently and log here instead
            panic!("Fragments must only exist at the root of the completion path");
        }
        TypePathItem::LinkedField { name } => {
            let field_id = schema.named_field(parent_type, name).unwrap();
            let field = schema.field(field_id);
            info!("resolved type for {:?} : {:?}", field.name, field.type_);
            field.type_.inner()
        }
        TypePathItem::InlineFragment { type_name } => schema.get_type(type_name).unwrap(),
    }
}

fn resolve_completion_items_from_fields<T: TypeWithFields>(
    type_: &T,
    schema: &Schema,
) -> Vec<CompletionItem> {
    type_
        .fields()
        .iter()
        .map(|field_id| {
            let field = schema.field(*field_id);
            let name = field.name.to_string();
            CompletionItem::new_simple(name, String::from(""))
        })
        .collect()
}

/// Finds all the valid fragment names for a given type. Used to complete fragment spreads
fn get_valid_fragments_for_type(type_: Type, programs: &Programs<'_>) -> Vec<StringKey> {
    let mut valid_fragment_names = vec![];
    let fragment_map = programs.source.fragment_map();
    for (fragment_name, fragment) in fragment_map {
        if fragment.type_condition == type_ {
            valid_fragment_names.push(*fragment_name);
        }
    }
    info!("get_valid_fragments_for_type {:#?}", valid_fragment_names);
    valid_fragment_names
}

fn resolve_completion_items_for_fragment_spread(
    type_: Type,
    programs: &Programs<'_>,
) -> Vec<CompletionItem> {
    get_valid_fragments_for_type(type_, programs)
        .iter()
        .map(|fragment_name| {
            CompletionItem::new_simple(fragment_name.to_string(), String::from(""))
        })
        .collect()
}

pub fn completion_items_for_request(
    request: CompletionRequest,
    schema: &Schema,
    programs: Option<&Programs<'_>>,
) -> Option<Vec<CompletionItem>> {
    let kind = request.kind;
    let leaf_type = request.resolve_leaf_type(schema);
    info!("completion_items_for_request: {:?} - {:?}", leaf_type, kind);
    match kind {
        CompletionKind::FragmentSpread => {
            if let Some(programs) = programs {
                let items = resolve_completion_items_for_fragment_spread(leaf_type, programs);
                Some(items)
            } else {
                None
            }
        }
        CompletionKind::FieldName => match leaf_type {
            Type::Interface(interface_id) => {
                let interface = schema.interface(interface_id);
                let items = resolve_completion_items_from_fields(interface, schema);
                Some(items)
            }
            Type::Object(object_id) => {
                let object = schema.object(object_id);
                let items = resolve_completion_items_from_fields(object, schema);
                Some(items)
            }
            Type::Enum(_) | Type::InputObject(_) | Type::Scalar(_) | Type::Union(_) => None,
        },
    }
}

fn populate_completion_request_from_selection(
    selections: &List<Selection>,
    position_span: Span,
    completion_request: &mut CompletionRequest,
) {
    for item in &selections.items {
        if item.span().contains(position_span) {
            match item {
                Selection::LinkedField(node) => {
                    completion_request.kind = CompletionKind::FieldName;
                    let LinkedField {
                        name, selections, ..
                    } = node;
                    completion_request.add_type(TypePathItem::LinkedField { name: name.value });
                    populate_completion_request_from_selection(
                        selections,
                        position_span,
                        completion_request,
                    );
                }
                Selection::FragmentSpread(_) => {
                    completion_request.kind = CompletionKind::FragmentSpread;
                }
                Selection::InlineFragment(node) => {
                    let InlineFragment {
                        selections,
                        type_condition,
                        ..
                    } = node;
                    if let Some(type_condition) = type_condition {
                        let type_name = type_condition.type_.value;
                        completion_request.add_type(TypePathItem::InlineFragment { type_name });
                        populate_completion_request_from_selection(
                            selections,
                            position_span,
                            completion_request,
                        )
                    }
                }
                Selection::ScalarField(_node) => {}
            }
        }
    }
}

/// Maps the LSP `Position` type back to a relative span, so we can find out which syntax node(s)
/// this completion request came from
pub fn position_to_span(position: Position, source: &GraphQLSource) -> Option<Span> {
    let mut index_of_last_line = 0;
    let mut line_index = source.line_index as u64;

    let mut chars = source.text.chars().enumerate().peekable();

    while let Some((index, chr)) = chars.next() {
        let is_newline = match chr {
            // Line terminators: https://www.ecma-international.org/ecma-262/#sec-line-terminators
            '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => match (chr, chars.peek()) {
                // <CLRF>
                ('\u{000D}', Some((_, '\u{000D}'))) => false,
                _ => true,
            },
            _ => false,
        };

        if is_newline {
            line_index += 1;
            index_of_last_line = index as u64;
        }

        if line_index == position.line {
            let start_offset = index_of_last_line + position.character;
            return Some(Span::new(start_offset as u32, 0));
        }
    }
    None
}

pub fn send_completion_response(
    items: Vec<CompletionItem>,
    request_id: ServerRequestId,
    connection: &Connection,
) {
    info!("send_completion_response: {:#?}", items);
    // If there are no items, don't send any response
    if items.is_empty() {
        return;
    }
    let completion_response = CompletionResponse::Array(items);
    let result = serde_json::to_value(&completion_response).unwrap();
    let response = ServerResponse {
        id: request_id,
        error: None,
        result: Some(result),
    };
    connection.sender.send(Message::Response(response)).unwrap();
}

/// Return a `CompletionPath` for this request, only if the completion request occurs
// within a GraphQL document. Otherwise return `None`
pub fn get_completion_request(
    params: CompletionParams,
    graphql_source_cache: &GraphQLSourceCache,
) -> Option<CompletionRequest> {
    let CompletionParams {
        text_document_position,
        ..
    } = params;
    let TextDocumentPositionParams {
        text_document,
        position,
    } = text_document_position;
    let url = text_document.uri;
    let graphql_sources = match graphql_source_cache.get(&url) {
        Some(sources) => sources,
        // If we have no sources for this file, do nothing
        None => return None,
    };

    info!(
        "Got completion request for file with sources: {:#?}",
        *graphql_sources
    );

    info!("position: {:?}", position);

    // We have GraphQL documents, now check if the completion request
    // falls within the range of one of these documents.
    let mut target_graphql_source: Option<&GraphQLSource> = None;
    for graphql_source in &*graphql_sources {
        let range = graphql_source.to_range();
        if position >= range.start && position <= range.end {
            target_graphql_source = Some(graphql_source);
            break;
        }
    }

    let graphql_source = match target_graphql_source {
        Some(source) => source,
        // Exit early if this completion request didn't fall within
        // the range of one of our GraphQL documents
        None => return None,
    };

    match parse(&graphql_source.text, FileKey::new(&url.to_string())) {
        Ok(document) => {
            // Now we need to take the `Position` and map that to an offset relative
            // to this GraphQL document, as the `Span`s in the document are relative.
            info!("Successfully parsed the definitions for a target GraphQL source");
            // Map the position to a zero-length span, relative to this GraphQL source.
            let position_span = match position_to_span(position, &graphql_source) {
                Some(span) => span,
                // Exit early if we can't map the position for some reason
                None => return None,
            };
            // Now we need to walk the Document, tracking our path along the way, until
            // we find the position within the document. Note that the GraphQLSource will
            // already be updated *with the characters that triggered the completion request*
            // since the change event fires before completion.
            info!("position_span: {:?}", position_span);
            let completion_request = create_completion_request(document, position_span);
            info!("Completion path: {:#?}", completion_request);
            Some(completion_request)
        }
        Err(err) => {
            info!("Failed to parse this target!");
            info!("{:?}", err);
            None
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Global style data

use context::StyleSystemOptions;
use gecko_bindings::bindings;
use gecko_bindings::bindings::{Gecko_RegisterProfilerThread, Gecko_UnregisterProfilerThread};
use gecko_bindings::bindings::Gecko_SetJemallocThreadLocalArena;
use num_cpus;
use rayon;
use shared_lock::SharedRwLock;
use std::cmp;
use std::env;
use std::ffi::CString;

/// Global style data
pub struct GlobalStyleData {
    /// Shared RWLock for CSSOM objects
    pub shared_lock: SharedRwLock,

    /// Global style system options determined by env vars.
    pub options: StyleSystemOptions,
}

/// Global thread pool
pub struct StyleThreadPool {
    /// How many threads parallel styling can use.
    pub num_threads: usize,

    /// The parallel styling thread pool.
    pub style_thread_pool: Option<rayon::ThreadPool>,
}

fn thread_name(index: usize) -> String {
    format!("StyleThread#{}", index)
}

fn thread_startup(index: usize) {
    unsafe {
        Gecko_SetJemallocThreadLocalArena(true);
    }
    let name = thread_name(index);
    let name = CString::new(name).unwrap();
    unsafe {
        // Gecko_RegisterProfilerThread copies the passed name here.
        Gecko_RegisterProfilerThread(name.as_ptr());
    }
}

fn thread_shutdown(_: usize) {
    unsafe {
        Gecko_UnregisterProfilerThread();
        Gecko_SetJemallocThreadLocalArena(false);
    }
}

lazy_static! {
    /// Global thread pool
    pub static ref STYLE_THREAD_POOL: StyleThreadPool = {
        let stylo_threads = env::var("STYLO_THREADS")
            .map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value"));
        let mut num_threads = match stylo_threads {
            Ok(num) => num,
            _ => cmp::max(num_cpus::get() * 3 / 4, 1),
        };

        // If num_threads is one, there's no point in creating a thread pool, so
        // force it to zero.
        //
        // We allow developers to force a one-thread pool for testing via a
        // special environmental variable.
        if num_threads == 1 {
            let force_pool = env::var("FORCE_STYLO_THREAD_POOL")
                .ok().map_or(false, |s| s.parse::<usize>().expect("invalid FORCE_STYLO_THREAD_POOL value") == 1);
            if !force_pool {
                num_threads = 0;
            }
        }

        let pool = if num_threads < 1 || unsafe { !bindings::Gecko_ShouldCreateStyleThreadPool() } {
            None
        } else {
            let configuration = rayon::Configuration::new()
                .num_threads(num_threads)
                // Enable a breadth-first rayon traversal. This causes the work
                // queue to be always FIFO, rather than FIFO for stealers and
                // FILO for the owner (which is what rayon does by default). This
                // ensures that we process all the elements at a given depth before
                // proceeding to the next depth, which is important for style sharing.
                .breadth_first()
                .thread_name(thread_name)
                .start_handler(thread_startup)
                .exit_handler(thread_shutdown);
                // Set thread stack size to 128KB.  See Gecko bug 1376883.
                //.stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024);
            let pool = rayon::ThreadPool::new(configuration).ok();
            pool
        };

        StyleThreadPool {
            num_threads: num_threads,
            style_thread_pool: pool,
        }
    };
    /// Global style data
    pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData {
        shared_lock: SharedRwLock::new(),
        options: StyleSystemOptions::default(),
    };
}
<commit_msg>Reenable tight stacks sizes.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Global style data

use context::StyleSystemOptions;
use gecko_bindings::bindings;
use gecko_bindings::bindings::{Gecko_RegisterProfilerThread, Gecko_UnregisterProfilerThread};
use gecko_bindings::bindings::Gecko_SetJemallocThreadLocalArena;
use num_cpus;
use parallel::STYLE_THREAD_STACK_SIZE_KB;
use rayon;
use shared_lock::SharedRwLock;
use std::cmp;
use std::env;
use std::ffi::CString;

/// Global style data
pub struct GlobalStyleData {
    /// Shared RWLock for CSSOM objects
    pub shared_lock: SharedRwLock,

    /// Global style system options determined by env vars.
    pub options: StyleSystemOptions,
}

/// Global thread pool
pub struct StyleThreadPool {
    /// How many threads parallel styling can use.
    pub num_threads: usize,

    /// The parallel styling thread pool.
    pub style_thread_pool: Option<rayon::ThreadPool>,
}

fn thread_name(index: usize) -> String {
    format!("StyleThread#{}", index)
}

fn thread_startup(index: usize) {
    unsafe {
        Gecko_SetJemallocThreadLocalArena(true);
    }
    let name = thread_name(index);
    let name = CString::new(name).unwrap();
    unsafe {
        // Gecko_RegisterProfilerThread copies the passed name here.
        Gecko_RegisterProfilerThread(name.as_ptr());
    }
}

fn thread_shutdown(_: usize) {
    unsafe {
        Gecko_UnregisterProfilerThread();
        Gecko_SetJemallocThreadLocalArena(false);
    }
}

lazy_static! {
    /// Global thread pool
    pub static ref STYLE_THREAD_POOL: StyleThreadPool = {
        let stylo_threads = env::var("STYLO_THREADS")
            .map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value"));
        let mut num_threads = match stylo_threads {
            Ok(num) => num,
            _ => cmp::max(num_cpus::get() * 3 / 4, 1),
        };

        // If num_threads is one, there's no point in creating a thread pool, so
        // force it to zero.
        //
        // We allow developers to force a one-thread pool for testing via a
        // special environmental variable.
        if num_threads == 1 {
            let force_pool = env::var("FORCE_STYLO_THREAD_POOL")
                .ok().map_or(false, |s| s.parse::<usize>().expect("invalid FORCE_STYLO_THREAD_POOL value") == 1);
            if !force_pool {
                num_threads = 0;
            }
        }

        let pool = if num_threads < 1 || unsafe { !bindings::Gecko_ShouldCreateStyleThreadPool() } {
            None
        } else {
            let configuration = rayon::Configuration::new()
                .num_threads(num_threads)
                // Enable a breadth-first rayon traversal. This causes the work
                // queue to be always FIFO, rather than FIFO for stealers and
                // FILO for the owner (which is what rayon does by default). This
                // ensures that we process all the elements at a given depth before
                // proceeding to the next depth, which is important for style sharing.
                .breadth_first()
                .thread_name(thread_name)
                .start_handler(thread_startup)
                .exit_handler(thread_shutdown)
                .stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024);
            let pool = rayon::ThreadPool::new(configuration).ok();
            pool
        };

        StyleThreadPool {
            num_threads: num_threads,
            style_thread_pool: pool,
        }
    };
    /// Global style data
    pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData {
        shared_lock: SharedRwLock::new(),
        options: StyleSystemOptions::default(),
    };
}
<|endoftext|>


<commit_before>use data::{Compression, NBT, NBTFile};
use errors::Result;

use std::io::Write;

use byteorder::{BigEndian, WriteBytesExt};

use flate2;
use flate2::write::{GzEncoder, ZlibEncoder};

macro_rules! compression_level {
    () => { flate2::Compression::default() };
}

/// Given an NBT file, write it as a binary NBT file to the writer
pub fn write_file<W: Write>(w: &mut W, file: &NBTFile) -> Result<()> {
    let map = match file.root {
        NBT::Compound(ref x) => x,
        _ => unreachable!(),
    };

    match file.compression {
        Compression::None => write_compound(w, &map, false)?,
        Compression::Gzip => {
            let mut w = GzEncoder::new(w, compression_level!());
            write_compound(&mut w, map, false)?;
            let _: &mut W = w.finish()?;
        },
        Compression::Zlib => {
            let mut w = ZlibEncoder::new(w, compression_level!());
            write_compound(&mut w, map, false)?;
            let _: &mut W = w.finish()?;
        },
    }

    Ok(())
}

fn write_tag<W: Write>(w: &mut W, tag: &NBT) -> Result<()> {
    match tag {
        &NBT::End => panic!("Unable to write End tag"),
        &NBT::Byte(x) => write_byte(w, x),
        &NBT::Short(x) => write_short(w, x),
        &NBT::Int(x) => write_int(w, x),
        &NBT::Long(x) => write_long(w, x),
        &NBT::Float(x) => write_float(w, x),
        &NBT::Double(x) => write_double(w, x),
        &NBT::ByteArray(ref x) => write_byte_array(w, x),
        &NBT::String(ref x) => write_string(w, x),
        &NBT::List(ref x) => write_list(w, x),
        &NBT::Compound(ref x) => write_compound(w, x, true),
        &NBT::IntArray(ref x) => write_int_array(w, x),
    }
}

fn write_byte<W: Write>(w: &mut W, val: i8) -> Result<()> {
    w.write_i8(val).map_err(|e| e.into())
}

fn write_short<W: Write>(w: &mut W, val: i16) -> Result<()> {
    w.write_i16::<BigEndian>(val).map_err(|e| e.into())
}

fn write_int<W: Write>(w: &mut W, val: i32) -> Result<()> {
    w.write_i32::<BigEndian>(val).map_err(|e| e.into())
}

fn write_long<W: Write>(w: &mut W, val: i64) -> Result<()> {
    w.write_i64::<BigEndian>(val).map_err(|e| e.into())
}

fn write_float<W: Write>(w: &mut W, val: f32) -> Result<()> {
    w.write_f32::<BigEndian>(val).map_err(|e| e.into())
}

fn write_double<W: Write>(w: &mut W, val: f64) -> Result<()> {
    w.write_f64::<BigEndian>(val).map_err(|e| e.into())
}

fn write_byte_array<W: Write>(w: &mut W, val: &Vec<i8>) -> Result<()> {
    write_int(w, val.len() as i32)?;

    for x in val {
        write_byte(w, *x)?;
    }

    Ok(())
}

fn write_string<W: Write>(w: &mut W, val: &String) -> Result<()> {
    let bytes = val.as_bytes();
    w.write_u16::<BigEndian>(bytes.len() as u16)?;
    w.write_all(bytes).map_err(|e| e.into())
}

fn write_list<W: Write>(w: &mut W, val: &Vec<NBT>) -> Result<()> {
    /* If the list has length 0, then it just defaults to type "End". */
    let tag_type = if val.len() > 0 {
        val[0].type_byte()
    } else {
        0
    };
    w.write_all(&[tag_type])?;
    write_int(w, val.len() as i32)?;

    for tag in val {
        write_tag(w, tag)?;
    }

    Ok(())
}

fn write_compound<W: Write>(w: &mut W,
                            map: &Vec<(String, NBT)>,
                            end: bool)
                            -> Result<()> {
    for &(ref key, ref tag) in map {
        w.write_all(&[tag.type_byte()])?;
        write_string(w, key)?;
        write_tag(w, &tag)?;
    }

    /* Append the End tag, but not on the implicit Compound */
    if end {
        w.write_all(&[0])?;
    }

    Ok(())
}

fn write_int_array<W: Write>(w: &mut W, val: &Vec<i32>) -> Result<()> {
    write_int(w, val.len() as i32)?;

    for x in val {
        write_int(w, *x)?;
    }

    Ok(())
}
<commit_msg>Change an accidental panic into a bail<commit_after>use data::{Compression, NBT, NBTFile};
use errors::Result;

use std::io::Write;

use byteorder::{BigEndian, WriteBytesExt};

use flate2;
use flate2::write::{GzEncoder, ZlibEncoder};

macro_rules! compression_level {
    () => { flate2::Compression::default() };
}

/// Given an NBT file, write it as a binary NBT file to the writer
pub fn write_file<W: Write>(w: &mut W, file: &NBTFile) -> Result<()> {
    let map = match file.root {
        NBT::Compound(ref x) => x,
        _ => unreachable!(),
    };

    match file.compression {
        Compression::None => write_compound(w, &map, false)?,
        Compression::Gzip => {
            let mut w = GzEncoder::new(w, compression_level!());
            write_compound(&mut w, map, false)?;
            let _: &mut W = w.finish()?;
        },
        Compression::Zlib => {
            let mut w = ZlibEncoder::new(w, compression_level!());
            write_compound(&mut w, map, false)?;
            let _: &mut W = w.finish()?;
        },
    }

    Ok(())
}

fn write_tag<W: Write>(w: &mut W, tag: &NBT) -> Result<()> {
    match tag {
        &NBT::End => bail!("Unable to write End tag"),
        &NBT::Byte(x) => write_byte(w, x),
        &NBT::Short(x) => write_short(w, x),
        &NBT::Int(x) => write_int(w, x),
        &NBT::Long(x) => write_long(w, x),
        &NBT::Float(x) => write_float(w, x),
        &NBT::Double(x) => write_double(w, x),
        &NBT::ByteArray(ref x) => write_byte_array(w, x),
        &NBT::String(ref x) => write_string(w, x),
        &NBT::List(ref x) => write_list(w, x),
        &NBT::Compound(ref x) => write_compound(w, x, true),
        &NBT::IntArray(ref x) => write_int_array(w, x),
    }
}

fn write_byte<W: Write>(w: &mut W, val: i8) -> Result<()> {
    w.write_i8(val).map_err(|e| e.into())
}

fn write_short<W: Write>(w: &mut W, val: i16) -> Result<()> {
    w.write_i16::<BigEndian>(val).map_err(|e| e.into())
}

fn write_int<W: Write>(w: &mut W, val: i32) -> Result<()> {
    w.write_i32::<BigEndian>(val).map_err(|e| e.into())
}

fn write_long<W: Write>(w: &mut W, val: i64) -> Result<()> {
    w.write_i64::<BigEndian>(val).map_err(|e| e.into())
}

fn write_float<W: Write>(w: &mut W, val: f32) -> Result<()> {
    w.write_f32::<BigEndian>(val).map_err(|e| e.into())
}

fn write_double<W: Write>(w: &mut W, val: f64) -> Result<()> {
    w.write_f64::<BigEndian>(val).map_err(|e| e.into())
}

fn write_byte_array<W: Write>(w: &mut W, val: &Vec<i8>) -> Result<()> {
    write_int(w, val.len() as i32)?;

    for x in val {
        write_byte(w, *x)?;
    }

    Ok(())
}

fn write_string<W: Write>(w: &mut W, val: &String) -> Result<()> {
    let bytes = val.as_bytes();
    w.write_u16::<BigEndian>(bytes.len() as u16)?;
    w.write_all(bytes).map_err(|e| e.into())
}

fn write_list<W: Write>(w: &mut W, val: &Vec<NBT>) -> Result<()> {
    /* If the list has length 0, then it just defaults to type "End". */
    let tag_type = if val.len() > 0 {
        val[0].type_byte()
    } else {
        0
    };
    w.write_all(&[tag_type])?;
    write_int(w, val.len() as i32)?;

    for tag in val {
        write_tag(w, tag)?;
    }

    Ok(())
}

fn write_compound<W: Write>(w: &mut W,
                            map: &Vec<(String, NBT)>,
                            end: bool)
                            -> Result<()> {
    for &(ref key, ref tag) in map {
        w.write_all(&[tag.type_byte()])?;
        write_string(w, key)?;
        write_tag(w, &tag)?;
    }

    /* Append the End tag, but not on the implicit Compound */
    if end {
        w.write_all(&[0])?;
    }

    Ok(())
}

fn write_int_array<W: Write>(w: &mut W, val: &Vec<i32>) -> Result<()> {
    write_int(w, val.len() as i32)?;

    for x in val {
        write_int(w, *x)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! Game world display utilities.

use std::sync::Arc;
use calx_resource::Resource;
use calx_grid::{Dir12, Dir6};
use world::{Brush, Location, World};
use world::{TerrainQuery, terrain};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    XWallBack,
    Northeast,
    East,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    West,
    Northwest,
    YWallBack,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
/// Draw layer for visual map elements.
pub enum Layer {
    /// Floor sprites, below other map forms.
    Floor,
    /// Blood splatters etc. on floor.
    Decal,
    /// Small items on floor,.
    Items,
    /// Large map objects, walls, most entities etc.
    Object,
    /// Transient effects shown on top of other map content.
    Effect,
    /// Text captions for map elements, on top of everything else.
    Text,
}

pub fn draw_terrain_sprites<F>(w: &World, loc: Location, mut draw: F)
    where F: FnMut(Layer, Angle, &Resource<Brush>, usize)
{
    use self::Angle::*;

    let terrain = w.terrain(loc);
    let kernel = Kernel::new(w, loc);

    match terrain.form {
        terrain::Form::Void | terrain::Form::Floor => {
            draw(Layer::Floor, Up, &terrain.brush, 0);
        }
        terrain::Form::Gate => {
            if let Some(d12) = Dir12::away_from(&kernel.void_mask()) {
                draw(Layer::Floor, Up, &terrain.brush, d12 as usize + 1);
            } else {
                draw(Layer::Floor, Up, &terrain.brush, 0);
            }
        }
        terrain::Form::Prop => {
            draw(Layer::Object, South, &terrain.brush, 0);
        }
        terrain::Form::Blob => {
            // This part gets a little tricky. Basic idea is that there's an inner pointy-top
            // hex core and the blob hull will snap to that instead of the outer flat-top hex
            // edge if neither adjacent face to the outer hex vertex is connected to another
            // blob.
            //
            // Based on how the sprites split up, the processing is done in four vertical
            // segments.

            let faces = kernel.blob_faces();

            // Do we snap to the outer vertices?
            let ne_vertex = !faces[0] || !faces[1];
            let e_vertex = !faces[1] || !faces[2];
            let se_vertex = !faces[2] || !faces[3];
            let sw_vertex = !faces[3] || !faces[4];
            let w_vertex = !faces[4] || !faces[5];
            let nw_vertex = !faces[5] || !faces[0];

            // Segment 2, middle left
            {
                if faces[0] {
                    if nw_vertex && ne_vertex {
                        draw(Layer::Object, North, &terrain.brush, 7);
                    } else if nw_vertex {
                        draw(Layer::Object, XWallBack, &terrain.brush, 15);
                    } else {
                        draw(Layer::Object, YWallBack, &terrain.brush, 11);
                    }
                }
                if faces[3] {
                    if sw_vertex && se_vertex {
                        draw(Layer::Object, South, &terrain.brush, 19);
                    } else if sw_vertex {
                        draw(Layer::Object, YWall, &terrain.brush, 23);
                    } else {
                        draw(Layer::Object, XWall, &terrain.brush, 27);
                    }
                }
            }

            // Segment 3, middle right
            {
                if faces[0] {
                    if ne_vertex && nw_vertex {
                        draw(Layer::Object, North, &terrain.brush, 8);
                    } else if ne_vertex {
                        draw(Layer::Object, YWallBack, &terrain.brush, 12);
                    } else {
                        draw(Layer::Object, XWallBack, &terrain.brush, 16);
                    }
                }
                if faces[3] {
                    if se_vertex && sw_vertex {
                        draw(Layer::Object, South, &terrain.brush, 20);
                    } else if se_vertex {
                        draw(Layer::Object, XWall, &terrain.brush, 28);
                    } else {
                        draw(Layer::Object, YWall, &terrain.brush, 24);
                    }
                }
            }

            // The side segments need to come after the middle
            // segments so that the vertical edges can overwrite the
            // middle segment pixels.

            // Segment 1, left edge
            {
                if w_vertex {
                    if faces[5] {
                        if nw_vertex {
                            draw(Layer::Object, Northwest, &terrain.brush, 6);
                        } else {
                            draw(Layer::Object, YWallBack, &terrain.brush, 10);
                        }
                    }

                    if faces[4] {
                        if sw_vertex {
                            draw(Layer::Object, Southwest, &terrain.brush, 18);
                        } else {
                            draw(Layer::Object, XWall, &terrain.brush, 26);
                        }
                    }
                } else {
                    // Draw the left vertical line.
                    draw(Layer::Object, West, &terrain.brush, 2);
                    if !faces[0] {
                        draw(Layer::Object, West, &terrain.brush, 0);
                    }
                    if !faces[3] {
                        draw(Layer::Object, West, &terrain.brush, 4);
                    }
                }
            }

            // Segment 4, right edge
            {
                if e_vertex {
                    if faces[1] {
                        if ne_vertex {
                            draw(Layer::Object, Northeast, &terrain.brush, 9);
                        } else {
                            draw(Layer::Object, XWallBack, &terrain.brush, 17);
                        }
                    }

                    if faces[2] {
                        if se_vertex {
                            draw(Layer::Object, Southeast, &terrain.brush, 21);
                        } else {
                            draw(Layer::Object, YWall, &terrain.brush, 25);
                        }
                    }
                } else {
                    // Draw the right vertical line.
                    draw(Layer::Object, East, &terrain.brush, 3);
                    if !faces[0] {
                        draw(Layer::Object, East, &terrain.brush, 1);
                    }
                    if !faces[3] {
                        draw(Layer::Object, East, &terrain.brush, 5);
                    }
                }
            }
        }
        terrain::Form::Wall => {
            let extends = kernel.wall_extends();
            if extends[0] {
                draw(Layer::Object, XWall, &terrain.brush, 2);
            } else {
                draw(Layer::Object, XWall, &terrain.brush, 0);
            }
            if extends[1] {
                draw(Layer::Object, YWall, &terrain.brush, 3);
            } else {
                draw(Layer::Object, YWall, &terrain.brush, 1);
            }
        }
    }
    // TODO: Generate sprites for entities, with tweening state projection.
    // TODO: Generate special effect sprites grounded on this location.
}

#[derive(Clone)]
pub struct Kernel {
    pub n: Arc<terrain::Tile>,
    pub ne: Arc<terrain::Tile>,
    pub nw: Arc<terrain::Tile>,
    pub center: Arc<terrain::Tile>,
    pub se: Arc<terrain::Tile>,
    pub sw: Arc<terrain::Tile>,
    pub s: Arc<terrain::Tile>,
}

fn neighbor(w: &World, loc: Location, dir: Dir6) -> Arc<terrain::Tile> {
    let loc = w.visible_portal(loc + dir.to_v2()).unwrap_or(loc + dir.to_v2());
    w.terrain(loc)
}

impl Kernel {
    pub fn new(w: &World, loc: Location) -> Kernel {
        Kernel {
            n: neighbor(w, loc, Dir6::North),
            ne: neighbor(w, loc, Dir6::Northeast),
            nw: neighbor(w, loc, Dir6::Northwest),
            center: w.terrain(loc),
            se: neighbor(w, loc, Dir6::Southeast),
            sw: neighbor(w, loc, Dir6::Southwest),
            s: neighbor(w, loc, Dir6::South),
        }
    }

    /// Bool is true if left/right half of wall should be extended.
    pub fn wall_extends(&self) -> [bool; 2] { [self.nw.is_wall(), self.ne.is_wall()] }

    /// Bool is true if n/ne/se/s/sw/nw face of block is facing open air.
    pub fn blob_faces(&self) -> [bool; 6] {
        [!self.n.is_hull(),
         !self.ne.is_hull(),
         !self.se.is_hull(),
         !self.s.is_hull(),
         !self.sw.is_hull(),
         !self.nw.is_hull()]
    }

    /// Mark neighbors that are not void terrain as true.
    pub fn void_mask(&self) -> [bool; 6] {
        [self.n.form != terrain::Form::Void,
         self.ne.form != terrain::Form::Void,
         self.se.form != terrain::Form::Void,
         self.s.form != terrain::Form::Void,
         self.sw.form != terrain::Form::Void,
         self.nw.form != terrain::Form::Void]
    }
}
<commit_msg>Fill blobforms with dark top sprites<commit_after>//! Game world display utilities.

use std::sync::Arc;
use calx_resource::Resource;
use calx_grid::{Dir12, Dir6};
use world::{Brush, Location, World};
use world::{TerrainQuery, terrain};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    XWallBack,
    Northeast,
    East,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    West,
    Northwest,
    YWallBack,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
/// Draw layer for visual map elements.
pub enum Layer {
    /// Floor sprites, below other map forms.
    Floor,
    /// Blood splatters etc. on floor.
    Decal,
    /// Small items on floor,.
    Items,
    /// Large map objects, walls, most entities etc.
    Object,
    /// Transient effects shown on top of other map content.
    Effect,
    /// Text captions for map elements, on top of everything else.
    Text,
}

fn blobform<F>(kernel: &Kernel, brush: &Resource<Brush>, is_solid: bool, draw: &mut F)
    where F: FnMut(Layer, Angle, &Resource<Brush>, usize)
{
    use self::Angle::*;
    // This part gets a little tricky. Basic idea is that there's an inner pointy-top
    // hex core and the blob hull will snap to that instead of the outer flat-top hex
    // edge if neither adjacent face to the outer hex vertex is connected to another
    // blob.
    //
    // Based on how the sprites split up, the processing is done in four vertical
    // segments.

    // Shape the blob based on neighbors that are also blob forms.
    let neighbors = kernel.blob_neighbors();

    // Do we snap to the outer vertices?
    let ne_vertex = !neighbors[0] || !neighbors[1];
    let e_vertex = !neighbors[1] || !neighbors[2];
    let se_vertex = !neighbors[2] || !neighbors[3];
    let sw_vertex = !neighbors[3] || !neighbors[4];
    let w_vertex = !neighbors[4] || !neighbors[5];
    let nw_vertex = !neighbors[5] || !neighbors[0];

    // Show exposed faces if neighbors are not blob and not wall.
    let faces = if is_solid { [true, true, true, true, true, true] } else { kernel.blob_faces() };

    // Segment 2, middle left
    {
        if faces[0] {
            if nw_vertex && ne_vertex {
                draw(Layer::Object, North, brush, 7);
            } else if nw_vertex {
                draw(Layer::Object, XWallBack, brush, 15);
            } else {
                draw(Layer::Object, YWallBack, brush, 11);
            }
        }
        if faces[3] {
            if sw_vertex && se_vertex {
                draw(Layer::Object, South, brush, 19);
            } else if sw_vertex {
                draw(Layer::Object, YWall, brush, 23);
            } else {
                draw(Layer::Object, XWall, brush, 27);
            }
        }
    }

    // Segment 3, middle right
    {
        if faces[0] {
            if ne_vertex && nw_vertex {
                draw(Layer::Object, North, brush, 8);
            } else if ne_vertex {
                draw(Layer::Object, YWallBack, brush, 12);
            } else {
                draw(Layer::Object, XWallBack, brush, 16);
            }
        }
        if faces[3] {
            if se_vertex && sw_vertex {
                draw(Layer::Object, South, brush, 20);
            } else if se_vertex {
                draw(Layer::Object, XWall, brush, 28);
            } else {
                draw(Layer::Object, YWall, brush, 24);
            }
        }
    }

    // The side segments need to come after the middle
    // segments so that the vertical edges can overwrite the
    // middle segment pixels.

    // Segment 1, left edge
    {
        if w_vertex {
            if faces[5] {
                if nw_vertex {
                    draw(Layer::Object, Northwest, brush, 6);
                } else {
                    draw(Layer::Object, YWallBack, brush, 10);
                }
            }

            if faces[4] {
                if sw_vertex {
                    draw(Layer::Object, Southwest, brush, 18);
                } else {
                    draw(Layer::Object, XWall, brush, 26);
                }
            }
        } else if !is_solid && (faces[4] || faces[5]) {
            // Draw the left vertical line.
            draw(Layer::Object, West, brush, 2);
            if !faces[0] && nw_vertex {
                draw(Layer::Object, West, brush, 0);
            }
            if !faces[3] && sw_vertex {
                draw(Layer::Object, West, brush, 4);
            }
        }
    }

    // Segment 4, right edge
    {
        if e_vertex {
            if faces[1] {
                if ne_vertex {
                    draw(Layer::Object, Northeast, brush, 9);
                } else {
                    draw(Layer::Object, XWallBack, brush, 17);
                }
            }

            if faces[2] {
                if se_vertex {
                    draw(Layer::Object, Southeast, brush, 21);
                } else {
                    draw(Layer::Object, YWall, brush, 25);
                }
            }
        } else if !is_solid && (faces[1] || faces[2]) {
            // Draw the right vertical line.
            draw(Layer::Object, East, brush, 3);
            if !faces[0] && ne_vertex {
                draw(Layer::Object, East, brush, 1);
            }
            if !faces[3] && se_vertex {
                draw(Layer::Object, East, brush, 5);
            }
        }
    }
}

pub fn draw_terrain_sprites<F>(w: &World, loc: Location, mut draw: F)
    where F: FnMut(Layer, Angle, &Resource<Brush>, usize)
{
    use self::Angle::*;

    let terrain = w.terrain(loc);
    let kernel = Kernel::new(w, loc);

    match terrain.form {
        terrain::Form::Void | terrain::Form::Floor => {
            draw(Layer::Floor, Up, &terrain.brush, 0);
        }
        terrain::Form::Gate => {
            if let Some(d12) = Dir12::away_from(&kernel.void_mask()) {
                draw(Layer::Floor, Up, &terrain.brush, d12 as usize + 1);
            } else {
                draw(Layer::Floor, Up, &terrain.brush, 0);
            }
        }
        terrain::Form::Prop => {
            draw(Layer::Object, South, &terrain.brush, 0);
        }
        terrain::Form::Blob => {
            // XXX: Expensive initialization, needs to be cached somewhere.
            //
            // Draw the solid blob first to block out other stuff.
            let solid = Resource::new("solid-blob".to_string()).unwrap();
            blobform(&kernel, &solid, true, &mut draw);
            // Then draw the decoration with the actual brush.
            blobform(&kernel, &terrain.brush, false, &mut draw);
        }
        terrain::Form::Wall => {
            let extends = kernel.wall_extends();
            if extends[0] {
                draw(Layer::Object, XWall, &terrain.brush, 2);
            } else {
                draw(Layer::Object, XWall, &terrain.brush, 0);
            }
            if extends[1] {
                draw(Layer::Object, YWall, &terrain.brush, 3);
            } else {
                draw(Layer::Object, YWall, &terrain.brush, 1);
            }
        }
    }
    // TODO: Generate sprites for entities, with tweening state projection.
    // TODO: Generate special effect sprites grounded on this location.
}

#[derive(Clone)]
pub struct Kernel {
    pub n: Arc<terrain::Tile>,
    pub ne: Arc<terrain::Tile>,
    pub nw: Arc<terrain::Tile>,
    pub center: Arc<terrain::Tile>,
    pub se: Arc<terrain::Tile>,
    pub sw: Arc<terrain::Tile>,
    pub s: Arc<terrain::Tile>,
}

fn neighbor(w: &World, loc: Location, dir: Dir6) -> Arc<terrain::Tile> {
    let loc = w.visible_portal(loc + dir.to_v2()).unwrap_or(loc + dir.to_v2());
    w.terrain(loc)
}

impl Kernel {
    pub fn new(w: &World, loc: Location) -> Kernel {
        Kernel {
            n: neighbor(w, loc, Dir6::North),
            ne: neighbor(w, loc, Dir6::Northeast),
            nw: neighbor(w, loc, Dir6::Northwest),
            center: w.terrain(loc),
            se: neighbor(w, loc, Dir6::Southeast),
            sw: neighbor(w, loc, Dir6::Southwest),
            s: neighbor(w, loc, Dir6::South),
        }
    }

    /// Bool is true if left/right half of wall should be extended.
    pub fn wall_extends(&self) -> [bool; 2] { [self.nw.is_wall(), self.ne.is_wall()] }

    /// Bool is true if n/ne/se/s/sw/nw face of block is facing open air.
    pub fn blob_faces(&self) -> [bool; 6] {
        [!self.n.is_hull(),
         !self.ne.is_hull(),
         !self.se.is_hull(),
         !self.s.is_hull(),
         !self.sw.is_hull(),
         !self.nw.is_hull()]
    }

    pub fn blob_neighbors(&self) -> [bool; 6] {
        [!self.n.is_blob(),
         !self.ne.is_blob(),
         !self.se.is_blob(),
         !self.s.is_blob(),
         !self.sw.is_blob(),
         !self.nw.is_blob()]
    }

    /// Mark neighbors that are not void terrain as true.
    pub fn void_mask(&self) -> [bool; 6] {
        [self.n.form != terrain::Form::Void,
         self.ne.form != terrain::Form::Void,
         self.se.form != terrain::Form::Void,
         self.s.form != terrain::Form::Void,
         self.sw.form != terrain::Form::Void,
         self.nw.form != terrain::Form::Void]
    }
}
<|endoftext|>