<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod builder;
mod cmp_wrappers;
mod element_types;
mod start_grammar;

use {
  crate::utils::{breadth_first_search, Name},
  std::collections::{BTreeMap, BTreeSet},
};

pub use cmp_wrappers::{NoCompare, ParentRef, RefCompare};
pub use element_types::{
  BaseElementTypes, ElementTypes, NonTerminal, Terminal,
};

/// A single element (terminal or non-terminal).
#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = ""),
  PartialOrd = "feature_allow_slow_enum",
  Ord = "feature_allow_slow_enum"
)]
pub enum Element<E: ElementTypes> {
  Term(E::Term),
  NonTerm(E::NonTerm),
}

impl<E: ElementTypes> Element<E> {
  /// If this element is a terminal, returns a `Some` value containing a
  /// terminal datum. Returns `None` otherwise.
  pub fn as_term(&self) -> Option<&E::Term> {
    match self {
      Element::NonTerm(_) => None,
      Element::Term(t) => Some(t),
    }
  }

  /// Gets an element as a nonterm. Returns a `None` value otherwise.
  pub fn as_nonterm(&self) -> Option<&E::NonTerm> {
    match self {
      Element::NonTerm(nt) => Some(nt),
      Element::Term(_) => None,
    }
  }

  /// Clone this element into an element of another ElementTypes instance.
  /// The `E::Term` and `E::NonTerm` datum types must be the same as those in
  /// `E2`.
  pub fn clone_as_other<E2>(&self) -> Element<E2>
  where
    E2: ElementTypes<Term = E::Term, NonTerm = E::NonTerm>,
  {
    match self {
      Element::Term(t) => Element::Term(t.clone()),
      Element::NonTerm(nt) => Element::NonTerm(nt.clone()),
    }
  }
}

/// An element within a production. Includes an optional identifier.
#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct ProductionElement<E: ElementTypes> {
  identifier: Option<Name>,
  element: Element<E>,
}

impl<E: ElementTypes> ProductionElement<E> {
  pub fn new_with_name(name: Name, e: Element<E>) -> Self {
    ProductionElement {
      identifier: Some(name),
      element: e,
    }
  }

  pub fn new(name: Option<Name>, e: Element<E>) -> Self {
    ProductionElement {
      identifier: name,
      element: e,
    }
  }

  pub fn new_empty(e: Element<E>) -> Self {
    ProductionElement {
      identifier: None,
      element: e,
    }
  }

  pub fn id(&self) -> Option<&Name> {
    self.identifier.as_ref()
  }

  pub fn elem(&self) -> &Element<E> {
    &self.element
  }

  pub fn clone_as_other<E2>(&self) -> ProductionElement<E2>
  where
    E2: ElementTypes<Term = E::Term, NonTerm = E::NonTerm>,
  {
    ProductionElement {
      identifier: self.identifier.clone(),
      element: self.element.clone_as_other(),
    }
  }
}

impl<E: ElementTypes> From<Element<E>> for ProductionElement<E> {
  fn from(e: Element<E>) -> ProductionElement<E> {
    ProductionElement {
      identifier: None,
      element: e,
    }
  }
}

/// A production within a rule.
#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
struct ProductionInner<E: ElementTypes> {
  action_key: E::ActionKey,
  elements: Vec<ProductionElement<E>>,
}

impl<E: ElementTypes> ProductionInner<E> {
  fn new(
    action_key: E::ActionKey,
    elements: Vec<ProductionElement<E>>,
  ) -> Self {
    ProductionInner {
      action_key,
      elements,
    }
  }

  pub fn prod_elements(&self) -> &Vec<ProductionElement<E>> {
    &self.elements
  }

  pub fn elements_iter(&self) -> impl Iterator<Item = &Element<E>> {
    self.elements.iter().map(|prod_elem| &prod_elem.element)
  }

  pub fn element_at(&self, index: usize) -> Option<&Element<E>> {
    self.elements.get(index).map(|prod_elem| &prod_elem.element)
  }

  pub fn action_key(&self) -> &E::ActionKey {
    &self.action_key
  }
}

#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct ProdKey<E: ElementTypes> {
  head: E::NonTerm,
  action_key: E::ActionKey,
}

/// A concrete raw rule value as stored inside a Grammar struct.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
struct RuleInner<E: ElementTypes> {
  head: E::NonTerm,
  prods: Vec<ProductionInner<E>>,
}

impl<E: ElementTypes> RuleInner<E> {
  pub fn new(head: E::NonTerm, prods: Vec<ProductionInner<E>>) -> Self {
    RuleInner { head, prods }
  }

  pub fn head(&self) -> &E::NonTerm {
    &self.head
  }

  pub fn prods(&self) -> &Vec<ProductionInner<E>> {
    &self.prods
  }
}

/// A context-free language grammar.
///
/// This is a context-free grammar consisting of
///
/// - A start nonterminal
/// - A set of rules, each which consist of
///   - A head nonterminal
///   - A set of productions, where each production consist of
///     - A list of production elements, which may have an identifier,
///       and is either a terminal or nonterminal (an element).
///     - An action key (identifier unique to that production)
///     - An action value (Data associated with that action key)
///
/// Grammars are read-only, and the accessors use the lifetime of the
/// grammar object.
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
pub struct Grammar<E: ElementTypes> {
  start_symbol: E::NonTerm,
  rule_set: BTreeMap<E::NonTerm, RuleInner<E>>,
  action_map: BTreeMap<E::ActionKey, E::ActionValue>,
}

impl<E: ElementTypes> std::fmt::Debug for Grammar<E> {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    let mut dbg_struct = f.debug_struct("Grammar");
    dbg_struct.field("Terms", &self.get_terminals().collect::<Vec<_>>());
    unimplemented!()
  }
}

impl<E: ElementTypes> Grammar<E> {
  fn new(
    start: E::NonTerm,
    rule_set: impl IntoIterator<Item = RuleInner<E>>,
    action_map: BTreeMap<E::ActionKey, E::ActionValue>,
  ) -> Result<Self, GrammarErrors<E>> {
    let g = Grammar {
      start_symbol: start,
      rule_set: rule_set
        .into_iter()
        .map(|r| (r.head().clone(), r))
        .collect(),
      action_map,
    };

    g.check_grammar().map(|_| g)
  }

  /// Returns the start nonterminal for this grammar.
  pub fn start_nt(&self) -> &E::NonTerm {
    &self.start_symbol
  }

  /// Returns an iterator over all of the rules for this grammar.
  pub fn rules<'a>(&'a self) -> impl Iterator<Item = Rule<'a, E>> {
    self.rule_set.iter().map(move |(_, rule)| Rule {
      grammar: ParentRef::new(self),
      rule: RefCompare::new(rule),
    })
  }

  /// Returns a map over rules of the grammar, keyed by the rule's head nonterminal.
  pub fn rule_set<'a>(&'a self) -> BTreeMap<&'a E::NonTerm, Rule<'a, E>> {
    self
      .rule_set
      .iter()
      .map(|(k, rule)| {
        (
          k,
          Rule {
            grammar: ParentRef::new(self),
            rule: RefCompare::new(rule),
          },
        )
      })
      .collect()
  }

  /// Gets the rule that has the given nonterminal as a head.
  pub fn get_rule<'a>(&'a self, nt: &E::NonTerm) -> Option<Rule<'a, E>> {
    self.rule_set.get(nt).map(|rule| Rule {
      grammar: ParentRef::new(self),
      rule: RefCompare::new(rule),
    })
  }

  /// Gets an iterator over all productions in the grammar.
  pub fn prods<'a>(&'a self) -> impl Iterator<Item = Prod<'a, E>> {
    self.rules().flat_map(|rule| rule.prods())
  }

  fn get_elements(&self) -> impl Iterator<Item = &Element<E>> {
    self
      .rule_set
      .values()
      .flat_map(|r| &r.prods)
      .flat_map(|p| p.elements_iter())
  }

  fn get_terminals(&self) -> impl Iterator<Item = &E::Term> {
    self.get_elements().filter_map(|e| e.as_term())
  }

  fn get_nonterminals(&self) -> impl Iterator<Item = &E::NonTerm> {
    self.get_elements().filter_map(|e| e.as_nonterm())
  }

  fn nonterminals_without_rules(&self) -> BTreeSet<&E::NonTerm> {
    self
      .get_nonterminals()
      .filter(move |nt| !self.rule_set.contains_key(nt))
      .collect()
  }

  fn rules_without_prods<'a>(&'a self) -> BTreeSet<&'a E::NonTerm> {
    let rules = self.rules();
    let prodless_rules = rules.into_iter().filter(|r| r.prods().is_empty());
    let head_iter = prodless_rules.map(|r| r.head());
    head_iter.collect()
  }

  fn reachable_nonterms(&self) -> BTreeSet<&E::NonTerm> {
    breadth_first_search(std::iter::once(&self.start_symbol), |nt| {
      match self.get_rule(nt) {
        Some(rule) => rule
          .prods()
          .iter()
          .flat_map(|p| p.elements_iter())
          .filter_map(|e| e.as_nonterm())
          .collect(),
        None => BTreeSet::new(),
      }
    })
  }

  fn unreachable_nonterms(&self) -> BTreeSet<&E::NonTerm> {
    let reachable_nonterms = self.reachable_nonterms();
    self
      .get_nonterminals()
      .filter(|nt| !reachable_nonterms.contains(nt))
      .collect()
  }
}

#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
pub struct GrammarErrors<E: ElementTypes> {
  unreachable_nonterms: BTreeSet<E::NonTerm>,
  nonterms_without_rules: BTreeSet<E::NonTerm>,
  rules_without_prods: BTreeSet<E::NonTerm>,
}

impl<E: ElementTypes> GrammarErrors<E> {
  fn into_result(self) -> Result<(), Self> {
    if self.unreachable_nonterms.is_empty()
      && self.nonterms_without_rules.is_empty()
      && self.rules_without_prods.is_empty()
    {
      Ok(())
    } else {
      Err(self)
    }
  }
}

impl<E: ElementTypes> Grammar<E> {
  fn check_grammar(&self) -> Result<(), GrammarErrors<E>> {
    GrammarErrors {
      unreachable_nonterms: self
        .unreachable_nonterms()
        .into_iter()
        .cloned()
        .collect(),
      nonterms_without_rules: self
        .nonterminals_without_rules()
        .into_iter()
        .cloned()
        .collect(),
      rules_without_prods: self
        .rules_without_prods()
        .into_iter()
        .cloned()
        .collect(),
    }
    .into_result()
  }
}

// ------------

#[derive(Derivative)]
#[derivative(
  Copy(bound = ""),
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct Rule<'a, E: ElementTypes> {
  grammar: ParentRef<'a, Grammar<E>>,
  rule: RefCompare<'a, RuleInner<E>>,
}

impl<'a, E: ElementTypes> Rule<'a, E> {
  pub fn head(&self) -> &'a E::NonTerm {
    &self.rule.head
  }

  pub fn prods(&self) -> Vec<Prod<'a, E>> {
    self
      .rule
      .prods
      .iter()
      .map(|prod| Prod {
        grammar: self.grammar,
        head: &self.rule.head,
        prod: RefCompare::new(prod),
        action_value: NoCompare::new(
          self.grammar.action_map.get(&prod.action_key).unwrap(),
        ),
      })
      .collect()
  }
}

// ------------

#[derive(Derivative)]
#[derivative(
  Copy(bound = ""),
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct Prod<'a, E: ElementTypes> {
  grammar: ParentRef<'a, Grammar<E>>,
  head: &'a E::NonTerm,
  prod: RefCompare<'a, ProductionInner<E>>,
  action_value: NoCompare<&'a E::ActionValue>,
}

impl<'a, E: ElementTypes> Prod<'a, E> {
  pub fn head(&self) -> &'a E::NonTerm {
    self.head
  }

  pub fn prod_elements(&self) -> &'a Vec<ProductionElement<E>> {
    &self.prod.elements
  }

  pub fn elements_iter(&self) -> impl Iterator<Item = &'a Element<E>> {
    self.prod.elements_iter()
  }

  pub fn prod_element_at(
    &self,
    index: usize,
  ) -> Option<&'a ProductionElement<E>> {
    self.prod.elements.get(index)
  }

  pub fn element_at(&self, index: usize) -> Option<&'a Element<E>> {
    self.prod.element_at(index)
  }

  pub fn action_key(&self) -> &'a E::ActionKey {
    self.prod.action_key()
  }

  pub fn action_value(&self) -> &'a E::ActionValue {
    *self.action_value
  }

  pub fn prod_key(&self) -> ProdKey<E> {
    ProdKey {
      head: self.head().clone(),
      action_key: self.action_key().clone(),
    }
  }
}
<commit_msg>Temporarily fixed a fmt helper.<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod builder;
mod cmp_wrappers;
mod element_types;
mod start_grammar;

use {
  crate::utils::{breadth_first_search, Name},
  std::collections::{BTreeMap, BTreeSet},
};

pub use cmp_wrappers::{NoCompare, ParentRef, RefCompare};
pub use element_types::{
  BaseElementTypes, ElementTypes, NonTerminal, Terminal,
};

/// A single element (terminal or non-terminal).
#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = ""),
  PartialOrd = "feature_allow_slow_enum",
  Ord = "feature_allow_slow_enum"
)]
pub enum Element<E: ElementTypes> {
  Term(E::Term),
  NonTerm(E::NonTerm),
}

impl<E: ElementTypes> Element<E> {
  /// If this element is a terminal, returns a `Some` value containing a
  /// terminal datum. Returns `None` otherwise.
  pub fn as_term(&self) -> Option<&E::Term> {
    match self {
      Element::NonTerm(_) => None,
      Element::Term(t) => Some(t),
    }
  }

  /// Gets an element as a nonterm. Returns a `None` value otherwise.
  pub fn as_nonterm(&self) -> Option<&E::NonTerm> {
    match self {
      Element::NonTerm(nt) => Some(nt),
      Element::Term(_) => None,
    }
  }

  /// Clone this element into an element of another ElementTypes instance.
  /// The `E::Term` and `E::NonTerm` datum types must be the same as those in
  /// `E2`.
  pub fn clone_as_other<E2>(&self) -> Element<E2>
  where
    E2: ElementTypes<Term = E::Term, NonTerm = E::NonTerm>,
  {
    match self {
      Element::Term(t) => Element::Term(t.clone()),
      Element::NonTerm(nt) => Element::NonTerm(nt.clone()),
    }
  }
}

/// An element within a production. Includes an optional identifier.
#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct ProductionElement<E: ElementTypes> {
  identifier: Option<Name>,
  element: Element<E>,
}

impl<E: ElementTypes> ProductionElement<E> {
  pub fn new_with_name(name: Name, e: Element<E>) -> Self {
    ProductionElement {
      identifier: Some(name),
      element: e,
    }
  }

  pub fn new(name: Option<Name>, e: Element<E>) -> Self {
    ProductionElement {
      identifier: name,
      element: e,
    }
  }

  pub fn new_empty(e: Element<E>) -> Self {
    ProductionElement {
      identifier: None,
      element: e,
    }
  }

  pub fn id(&self) -> Option<&Name> {
    self.identifier.as_ref()
  }

  pub fn elem(&self) -> &Element<E> {
    &self.element
  }

  pub fn clone_as_other<E2>(&self) -> ProductionElement<E2>
  where
    E2: ElementTypes<Term = E::Term, NonTerm = E::NonTerm>,
  {
    ProductionElement {
      identifier: self.identifier.clone(),
      element: self.element.clone_as_other(),
    }
  }
}

impl<E: ElementTypes> From<Element<E>> for ProductionElement<E> {
  fn from(e: Element<E>) -> ProductionElement<E> {
    ProductionElement {
      identifier: None,
      element: e,
    }
  }
}

/// A production within a rule.
#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
struct ProductionInner<E: ElementTypes> {
  action_key: E::ActionKey,
  elements: Vec<ProductionElement<E>>,
}

impl<E: ElementTypes> ProductionInner<E> {
  fn new(
    action_key: E::ActionKey,
    elements: Vec<ProductionElement<E>>,
  ) -> Self {
    ProductionInner {
      action_key,
      elements,
    }
  }

  pub fn prod_elements(&self) -> &Vec<ProductionElement<E>> {
    &self.elements
  }

  pub fn elements_iter(&self) -> impl Iterator<Item = &Element<E>> {
    self.elements.iter().map(|prod_elem| &prod_elem.element)
  }

  pub fn element_at(&self, index: usize) -> Option<&Element<E>> {
    self.elements.get(index).map(|prod_elem| &prod_elem.element)
  }

  pub fn action_key(&self) -> &E::ActionKey {
    &self.action_key
  }
}

#[derive(Derivative)]
#[derivative(
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct ProdKey<E: ElementTypes> {
  head: E::NonTerm,
  action_key: E::ActionKey,
}

/// A concrete raw rule value as stored inside a Grammar struct.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
struct RuleInner<E: ElementTypes> {
  head: E::NonTerm,
  prods: Vec<ProductionInner<E>>,
}

impl<E: ElementTypes> RuleInner<E> {
  pub fn new(head: E::NonTerm, prods: Vec<ProductionInner<E>>) -> Self {
    RuleInner { head, prods }
  }

  pub fn head(&self) -> &E::NonTerm {
    &self.head
  }

  pub fn prods(&self) -> &Vec<ProductionInner<E>> {
    &self.prods
  }
}

/// A context-free language grammar.
///
/// This is a context-free grammar consisting of
///
/// - A start nonterminal
/// - A set of rules, each which consist of
///   - A head nonterminal
///   - A set of productions, where each production consist of
///     - A list of production elements, which may have an identifier,
///       and is either a terminal or nonterminal (an element).
///     - An action key (identifier unique to that production)
///     - An action value (Data associated with that action key)
///
/// Grammars are read-only, and the accessors use the lifetime of the
/// grammar object.
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
pub struct Grammar<E: ElementTypes> {
  start_symbol: E::NonTerm,
  rule_set: BTreeMap<E::NonTerm, RuleInner<E>>,
  action_map: BTreeMap<E::ActionKey, E::ActionValue>,
}

impl<E: ElementTypes> std::fmt::Debug for Grammar<E> {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    let mut dbg_struct = f.debug_struct("Grammar");
    dbg_struct.field("Terms", &self.get_terminals().collect::<Vec<_>>());
    dbg_struct.finish()
  }
}

impl<E: ElementTypes> Grammar<E> {
  fn new(
    start: E::NonTerm,
    rule_set: impl IntoIterator<Item = RuleInner<E>>,
    action_map: BTreeMap<E::ActionKey, E::ActionValue>,
  ) -> Result<Self, GrammarErrors<E>> {
    let g = Grammar {
      start_symbol: start,
      rule_set: rule_set
        .into_iter()
        .map(|r| (r.head().clone(), r))
        .collect(),
      action_map,
    };

    g.check_grammar().map(|_| g)
  }

  /// Returns the start nonterminal for this grammar.
  pub fn start_nt(&self) -> &E::NonTerm {
    &self.start_symbol
  }

  /// Returns an iterator over all of the rules for this grammar.
  pub fn rules<'a>(&'a self) -> impl Iterator<Item = Rule<'a, E>> {
    self.rule_set.iter().map(move |(_, rule)| Rule {
      grammar: ParentRef::new(self),
      rule: RefCompare::new(rule),
    })
  }

  /// Returns a map over rules of the grammar, keyed by the rule's head nonterminal.
  pub fn rule_set<'a>(&'a self) -> BTreeMap<&'a E::NonTerm, Rule<'a, E>> {
    self
      .rule_set
      .iter()
      .map(|(k, rule)| {
        (
          k,
          Rule {
            grammar: ParentRef::new(self),
            rule: RefCompare::new(rule),
          },
        )
      })
      .collect()
  }

  /// Gets the rule that has the given nonterminal as a head.
  pub fn get_rule<'a>(&'a self, nt: &E::NonTerm) -> Option<Rule<'a, E>> {
    self.rule_set.get(nt).map(|rule| Rule {
      grammar: ParentRef::new(self),
      rule: RefCompare::new(rule),
    })
  }

  /// Gets an iterator over all productions in the grammar.
  pub fn prods<'a>(&'a self) -> impl Iterator<Item = Prod<'a, E>> {
    self.rules().flat_map(|rule| rule.prods())
  }

  fn get_elements(&self) -> impl Iterator<Item = &Element<E>> {
    self
      .rule_set
      .values()
      .flat_map(|r| &r.prods)
      .flat_map(|p| p.elements_iter())
  }

  fn get_terminals(&self) -> impl Iterator<Item = &E::Term> {
    self.get_elements().filter_map(|e| e.as_term())
  }

  fn get_nonterminals(&self) -> impl Iterator<Item = &E::NonTerm> {
    self.get_elements().filter_map(|e| e.as_nonterm())
  }

  fn nonterminals_without_rules(&self) -> BTreeSet<&E::NonTerm> {
    self
      .get_nonterminals()
      .filter(move |nt| !self.rule_set.contains_key(nt))
      .collect()
  }

  fn rules_without_prods<'a>(&'a self) -> BTreeSet<&'a E::NonTerm> {
    let rules = self.rules();
    let prodless_rules = rules.into_iter().filter(|r| r.prods().is_empty());
    let head_iter = prodless_rules.map(|r| r.head());
    head_iter.collect()
  }

  fn reachable_nonterms(&self) -> BTreeSet<&E::NonTerm> {
    breadth_first_search(std::iter::once(&self.start_symbol), |nt| {
      match self.get_rule(nt) {
        Some(rule) => rule
          .prods()
          .iter()
          .flat_map(|p| p.elements_iter())
          .filter_map(|e| e.as_nonterm())
          .collect(),
        None => BTreeSet::new(),
      }
    })
  }

  fn unreachable_nonterms(&self) -> BTreeSet<&E::NonTerm> {
    let reachable_nonterms = self.reachable_nonterms();
    self
      .get_nonterminals()
      .filter(|nt| !reachable_nonterms.contains(nt))
      .collect()
  }
}

#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
pub struct GrammarErrors<E: ElementTypes> {
  unreachable_nonterms: BTreeSet<E::NonTerm>,
  nonterms_without_rules: BTreeSet<E::NonTerm>,
  rules_without_prods: BTreeSet<E::NonTerm>,
}

impl<E: ElementTypes> GrammarErrors<E> {
  fn into_result(self) -> Result<(), Self> {
    if self.unreachable_nonterms.is_empty()
      && self.nonterms_without_rules.is_empty()
      && self.rules_without_prods.is_empty()
    {
      Ok(())
    } else {
      Err(self)
    }
  }
}

impl<E: ElementTypes> Grammar<E> {
  fn check_grammar(&self) -> Result<(), GrammarErrors<E>> {
    GrammarErrors {
      unreachable_nonterms: self
        .unreachable_nonterms()
        .into_iter()
        .cloned()
        .collect(),
      nonterms_without_rules: self
        .nonterminals_without_rules()
        .into_iter()
        .cloned()
        .collect(),
      rules_without_prods: self
        .rules_without_prods()
        .into_iter()
        .cloned()
        .collect(),
    }
    .into_result()
  }
}

// ------------

#[derive(Derivative)]
#[derivative(
  Copy(bound = ""),
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct Rule<'a, E: ElementTypes> {
  grammar: ParentRef<'a, Grammar<E>>,
  rule: RefCompare<'a, RuleInner<E>>,
}

impl<'a, E: ElementTypes> Rule<'a, E> {
  pub fn head(&self) -> &'a E::NonTerm {
    &self.rule.head
  }

  pub fn prods(&self) -> Vec<Prod<'a, E>> {
    self
      .rule
      .prods
      .iter()
      .map(|prod| Prod {
        grammar: self.grammar,
        head: &self.rule.head,
        prod: RefCompare::new(prod),
        action_value: NoCompare::new(
          self.grammar.action_map.get(&prod.action_key).unwrap(),
        ),
      })
      .collect()
  }
}

// ------------

#[derive(Derivative)]
#[derivative(
  Copy(bound = ""),
  Clone(bound = ""),
  PartialEq(bound = ""),
  Eq(bound = ""),
  PartialOrd(bound = ""),
  Ord(bound = ""),
  Debug(bound = "")
)]
pub struct Prod<'a, E: ElementTypes> {
  grammar: ParentRef<'a, Grammar<E>>,
  head: &'a E::NonTerm,
  prod: RefCompare<'a, ProductionInner<E>>,
  action_value: NoCompare<&'a E::ActionValue>,
}

impl<'a, E: ElementTypes> Prod<'a, E> {
  pub fn head(&self) -> &'a E::NonTerm {
    self.head
  }

  pub fn prod_elements(&self) -> &'a Vec<ProductionElement<E>> {
    &self.prod.elements
  }

  pub fn elements_iter(&self) -> impl Iterator<Item = &'a Element<E>> {
    self.prod.elements_iter()
  }

  pub fn prod_element_at(
    &self,
    index: usize,
  ) -> Option<&'a ProductionElement<E>> {
    self.prod.elements.get(index)
  }

  pub fn element_at(&self, index: usize) -> Option<&'a Element<E>> {
    self.prod.element_at(index)
  }

  pub fn action_key(&self) -> &'a E::ActionKey {
    self.prod.action_key()
  }

  pub fn action_value(&self) -> &'a E::ActionValue {
    *self.action_value
  }

  pub fn prod_key(&self) -> ProdKey<E> {
    ProdKey {
      head: self.head().clone(),
      action_key: self.action_key().clone(),
    }
  }
}
<|endoftext|>


<commit_before>use errors::*;
use multirust_dist::dist;
use multirust_utils::utils;
use multirust_dist::prefix::InstallPrefix;
use multirust_dist::dist::ToolchainDesc;
use multirust_dist::manifestation::{Manifestation, Changes};
use multirust_dist::manifest::Component;
use config::Cfg;
use env_var;
use install::{self, InstallMethod};

use std::process::Command;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::env;

use hyper;

#[derive(Debug)]
pub struct Toolchain<'a> {
    cfg: &'a Cfg,
    name: String,
    path: PathBuf,
}

impl<'a> Toolchain<'a> {
    pub fn from(cfg: &'a Cfg, name: &str) -> Self {
        let path = cfg.toolchains_dir.join(name);
        Toolchain {
            cfg: cfg,
            name: name.to_owned(),
            path: path.clone(),
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn exists(&self) -> bool {
        utils::is_directory(&self.path)
    }
    pub fn verify(&self) -> Result<()> {
        Ok(try!(utils::assert_is_directory(&self.path)))
    }
    pub fn remove(&self) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name));
            return Ok(());
        }
        if let Some(update_hash) = try!(self.update_hash()) {
            try!(utils::remove_file("update hash", &update_hash));
        }
        let handler = self.cfg.notify_handler.as_ref();
        let result = install::uninstall(&self.path, ntfy!(&handler));
        if !self.exists() {
            self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name));
        }
        Ok(try!(result))
    }
    fn install(&self, install_method: InstallMethod) -> Result<()> {
        assert!(self.is_valid_install_method(install_method));
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name));
        }
        self.cfg
            .notify_handler
            .call(Notification::ToolchainDirectory(&self.path, &self.name));
        let handler = self.cfg.notify_handler.as_ref();
        let updated = try!(install_method.run(&self.path, ntfy!(&handler)));

        if !updated {
            self.cfg.notify_handler.call(Notification::UpdateHashMatches);
        } else {
            self.cfg.notify_handler.call(Notification::InstalledToolchain(&self.name));
        }

        Ok(())
    }
    fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> {
        assert!(self.is_valid_install_method(install_method));
        self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name));
        if !self.exists() {
            self.install(install_method)
        } else {
            self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name));
            Ok(())
        }
    }
    fn is_valid_install_method(&self, install_method: InstallMethod) -> bool {
        match install_method {
            InstallMethod::Copy(_) |
            InstallMethod::Link(_) |
            InstallMethod::Installer(_, _) => self.is_custom(),
            InstallMethod::Dist(_, _, _) => !self.is_custom(),
        }
    }
    fn update_hash(&self) -> Result<Option<PathBuf>> {
        if self.is_custom() {
            Ok(None)
        } else {
            Ok(Some(try!(self.cfg.get_hash_file(&self.name, true))))
        }
    }

    fn download_cfg(&self) -> dist::DownloadCfg {
        dist::DownloadCfg {
            dist_root: &self.cfg.dist_root_url,
            temp_cfg: &self.cfg.temp_cfg,
            notify_handler: ntfy!(&self.cfg.notify_handler),
        }
    }

    pub fn install_from_dist(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install(InstallMethod::Dist(&self.name,
                                         update_hash.as_ref().map(|p| &**p),
                                         self.download_cfg()))
    }
    pub fn install_from_dist_if_not_installed(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install_if_not_installed(InstallMethod::Dist(&self.name,
                                                          update_hash.as_ref().map(|p| &**p),
                                                          self.download_cfg()))
    }
    pub fn is_custom(&self) -> bool {
        ToolchainDesc::from_str(&self.name).is_err()
    }
    pub fn is_tracking(&self) -> bool {
        ToolchainDesc::from_str(&self.name).ok().map(|d| d.is_tracking()) == Some(true)
    }

    fn ensure_custom(&self) -> Result<()> {
        if !self.is_custom() {
            Err(Error::Install(::multirust_dist::Error::InvalidToolchainName(self.name.to_string())))
        } else {
            Ok(())
        }
    }

    pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> {
        try!(self.ensure_custom());

        try!(self.remove());

        // FIXME: This should do all downloads first, then do
        // installs, and do it all in a single transaction.
        for installer in installers {
            let installer_str = installer.to_str().unwrap_or("bogus");
            match installer_str.rfind(".") {
                Some(i) => {
                    let extension = &installer_str[i+1..];
                    if extension != "gz" {
                        return Err(Error::BadInstallerType(extension.to_string()));
                    }
                }
                None => return Err(Error::BadInstallerType(String::from("(none)")))
            }

            // FIXME: Pretty hacky
            let is_url = installer_str.starts_with("file://")
                || installer_str.starts_with("http://")
                || installer_str.starts_with("https://");
            let url = hyper::Url::parse(installer_str).ok();
            let url = if is_url { url } else { None };
            if let Some(url) = url {

                // Download to a local file
                let local_installer = try!(self.cfg.temp_cfg.new_file_with_ext("", ".tar.gz"));
                try!(utils::download_file(url,
                                          &local_installer,
                                          None,
                                          ntfy!(&self.cfg.notify_handler)));
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            } else {
                // If installer is a filename

                // No need to download
                let local_installer = Path::new(installer);

                // Install from file
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            }
        }

        Ok(())
    }

    pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> {
        try!(self.ensure_custom());

        if link {
            self.install(InstallMethod::Link(&try!(utils::to_absolute(src))))
        } else {
            self.install(InstallMethod::Copy(src))
        }
    }

    pub fn create_command<T: AsRef<OsStr>>(&self, binary: T) -> Result<Command> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let mut cmd = Command::new(binary);
        self.set_env(&mut cmd);
        Ok(cmd)
    }

    fn set_env(&self, cmd: &mut Command) {
        let ref bin_path = self.path.join("bin");

        self.set_ldpath(cmd);

        // Because multirust and cargo use slightly different
        // definitions of cargo home (multirust doesn't read HOME on
        // windows), we must set it here to ensure cargo and
        // multirust agree.
        if let Ok(cargo_home) = utils::cargo_home() {
            env_var::set_path("CARGO_HOME", &cargo_home, cmd);
        }

        env_var::set_path("PATH", bin_path, cmd);
        env_var::inc("RUST_RECURSION_COUNT", cmd);

        // FIXME: This should not be a path, but a toolchain name.
        // Not sure what's going on here.
        cmd.env("MULTIRUST_TOOLCHAIN", &self.path);
        cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir);
    }

    pub fn set_ldpath(&self, cmd: &mut Command) {
        let new_path = self.path.join("lib");

        env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd);
        env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd);
    }

    pub fn doc_path(&self, relative: &str) -> Result<PathBuf> {
        try!(self.verify());

        let parts = vec!["share", "doc", "rust", "html"];
        let mut doc_dir = self.path.clone();
        for part in parts {
            doc_dir.push(part);
        }
        doc_dir.push(relative);

        Ok(doc_dir)
    }
    pub fn open_docs(&self, relative: &str) -> Result<()> {
        try!(self.verify());

        Ok(try!(utils::open_browser(&try!(self.doc_path(relative)))))
    }

    pub fn make_default(&self) -> Result<()> {
        self.cfg.set_default(&self.name)
    }
    pub fn make_override(&self, path: &Path) -> Result<()> {
        Ok(try!(self.cfg.override_db.set(path,
                                         &self.name,
                                         &self.cfg.temp_cfg,
                                         self.cfg.notify_handler.as_ref())))
    }

    pub fn list_components(&self) -> Result<Vec<Component>> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        // FIXME: This toolchain handling is a mess. Just do it once
        // when the toolchain is created.
        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {
            // Return all optional components of the "rust" package for the
            // toolchain's target triple.
            let mut res = Vec::new();

            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            for extension in &targ_pkg.extensions {
                res.push(extension.clone())
            }

            Ok(res)
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn add_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::AddingRequiredComponent(self.name.to_string(), component));
            }

            if !targ_pkg.extensions.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![component],
                remove_extensions: vec![]
            };

            try!(manifestation.update(&manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn remove_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::RemovingRequiredComponent(self.name.to_string(), component));
            }

            let dist_config = try!(manifestation.read_config()).unwrap();
            if !dist_config.components.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![],
                remove_extensions: vec![component]
            };

            try!(manifestation.update(&manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn binary_file(&self, name: &str) -> PathBuf {
        let mut path = self.path.clone();
        path.push("bin");
        path.push(name.to_owned() + env::consts::EXE_SUFFIX);
        path
    }
}
<commit_msg>Fix setting of CARGO_HOME<commit_after>use errors::*;
use multirust_dist::dist;
use multirust_utils::utils;
use multirust_dist::prefix::InstallPrefix;
use multirust_dist::dist::ToolchainDesc;
use multirust_dist::manifestation::{Manifestation, Changes};
use multirust_dist::manifest::Component;
use config::Cfg;
use env_var;
use install::{self, InstallMethod};

use std::process::Command;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::env;

use hyper;

#[derive(Debug)]
pub struct Toolchain<'a> {
    cfg: &'a Cfg,
    name: String,
    path: PathBuf,
}

impl<'a> Toolchain<'a> {
    pub fn from(cfg: &'a Cfg, name: &str) -> Self {
        let path = cfg.toolchains_dir.join(name);
        Toolchain {
            cfg: cfg,
            name: name.to_owned(),
            path: path.clone(),
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn exists(&self) -> bool {
        utils::is_directory(&self.path)
    }
    pub fn verify(&self) -> Result<()> {
        Ok(try!(utils::assert_is_directory(&self.path)))
    }
    pub fn remove(&self) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name));
            return Ok(());
        }
        if let Some(update_hash) = try!(self.update_hash()) {
            try!(utils::remove_file("update hash", &update_hash));
        }
        let handler = self.cfg.notify_handler.as_ref();
        let result = install::uninstall(&self.path, ntfy!(&handler));
        if !self.exists() {
            self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name));
        }
        Ok(try!(result))
    }
    fn install(&self, install_method: InstallMethod) -> Result<()> {
        assert!(self.is_valid_install_method(install_method));
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name));
        }
        self.cfg
            .notify_handler
            .call(Notification::ToolchainDirectory(&self.path, &self.name));
        let handler = self.cfg.notify_handler.as_ref();
        let updated = try!(install_method.run(&self.path, ntfy!(&handler)));

        if !updated {
            self.cfg.notify_handler.call(Notification::UpdateHashMatches);
        } else {
            self.cfg.notify_handler.call(Notification::InstalledToolchain(&self.name));
        }

        Ok(())
    }
    fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> {
        assert!(self.is_valid_install_method(install_method));
        self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name));
        if !self.exists() {
            self.install(install_method)
        } else {
            self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name));
            Ok(())
        }
    }
    fn is_valid_install_method(&self, install_method: InstallMethod) -> bool {
        match install_method {
            InstallMethod::Copy(_) |
            InstallMethod::Link(_) |
            InstallMethod::Installer(_, _) => self.is_custom(),
            InstallMethod::Dist(_, _, _) => !self.is_custom(),
        }
    }
    fn update_hash(&self) -> Result<Option<PathBuf>> {
        if self.is_custom() {
            Ok(None)
        } else {
            Ok(Some(try!(self.cfg.get_hash_file(&self.name, true))))
        }
    }

    fn download_cfg(&self) -> dist::DownloadCfg {
        dist::DownloadCfg {
            dist_root: &self.cfg.dist_root_url,
            temp_cfg: &self.cfg.temp_cfg,
            notify_handler: ntfy!(&self.cfg.notify_handler),
        }
    }

    pub fn install_from_dist(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install(InstallMethod::Dist(&self.name,
                                         update_hash.as_ref().map(|p| &**p),
                                         self.download_cfg()))
    }
    pub fn install_from_dist_if_not_installed(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install_if_not_installed(InstallMethod::Dist(&self.name,
                                                          update_hash.as_ref().map(|p| &**p),
                                                          self.download_cfg()))
    }
    pub fn is_custom(&self) -> bool {
        ToolchainDesc::from_str(&self.name).is_err()
    }
    pub fn is_tracking(&self) -> bool {
        ToolchainDesc::from_str(&self.name).ok().map(|d| d.is_tracking()) == Some(true)
    }

    fn ensure_custom(&self) -> Result<()> {
        if !self.is_custom() {
            Err(Error::Install(::multirust_dist::Error::InvalidToolchainName(self.name.to_string())))
        } else {
            Ok(())
        }
    }

    pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> {
        try!(self.ensure_custom());

        try!(self.remove());

        // FIXME: This should do all downloads first, then do
        // installs, and do it all in a single transaction.
        for installer in installers {
            let installer_str = installer.to_str().unwrap_or("bogus");
            match installer_str.rfind(".") {
                Some(i) => {
                    let extension = &installer_str[i+1..];
                    if extension != "gz" {
                        return Err(Error::BadInstallerType(extension.to_string()));
                    }
                }
                None => return Err(Error::BadInstallerType(String::from("(none)")))
            }

            // FIXME: Pretty hacky
            let is_url = installer_str.starts_with("file://")
                || installer_str.starts_with("http://")
                || installer_str.starts_with("https://");
            let url = hyper::Url::parse(installer_str).ok();
            let url = if is_url { url } else { None };
            if let Some(url) = url {

                // Download to a local file
                let local_installer = try!(self.cfg.temp_cfg.new_file_with_ext("", ".tar.gz"));
                try!(utils::download_file(url,
                                          &local_installer,
                                          None,
                                          ntfy!(&self.cfg.notify_handler)));
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            } else {
                // If installer is a filename

                // No need to download
                let local_installer = Path::new(installer);

                // Install from file
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            }
        }

        Ok(())
    }

    pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> {
        try!(self.ensure_custom());

        if link {
            self.install(InstallMethod::Link(&try!(utils::to_absolute(src))))
        } else {
            self.install(InstallMethod::Copy(src))
        }
    }

    pub fn create_command<T: AsRef<OsStr>>(&self, binary: T) -> Result<Command> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let mut cmd = Command::new(binary);
        self.set_env(&mut cmd);
        Ok(cmd)
    }

    fn set_env(&self, cmd: &mut Command) {
        let ref bin_path = self.path.join("bin");

        self.set_ldpath(cmd);

        // Because multirust and cargo use slightly different
        // definitions of cargo home (multirust doesn't read HOME on
        // windows), we must set it here to ensure cargo and
        // multirust agree.
        if let Ok(cargo_home) = utils::cargo_home() {
            cmd.env("CARGO_HOME", &cargo_home);
        }

        env_var::set_path("PATH", bin_path, cmd);
        env_var::inc("RUST_RECURSION_COUNT", cmd);

        // FIXME: This should not be a path, but a toolchain name.
        // Not sure what's going on here.
        cmd.env("MULTIRUST_TOOLCHAIN", &self.path);
        cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir);
    }

    pub fn set_ldpath(&self, cmd: &mut Command) {
        let new_path = self.path.join("lib");

        env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd);
        env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd);
    }

    pub fn doc_path(&self, relative: &str) -> Result<PathBuf> {
        try!(self.verify());

        let parts = vec!["share", "doc", "rust", "html"];
        let mut doc_dir = self.path.clone();
        for part in parts {
            doc_dir.push(part);
        }
        doc_dir.push(relative);

        Ok(doc_dir)
    }
    pub fn open_docs(&self, relative: &str) -> Result<()> {
        try!(self.verify());

        Ok(try!(utils::open_browser(&try!(self.doc_path(relative)))))
    }

    pub fn make_default(&self) -> Result<()> {
        self.cfg.set_default(&self.name)
    }
    pub fn make_override(&self, path: &Path) -> Result<()> {
        Ok(try!(self.cfg.override_db.set(path,
                                         &self.name,
                                         &self.cfg.temp_cfg,
                                         self.cfg.notify_handler.as_ref())))
    }

    pub fn list_components(&self) -> Result<Vec<Component>> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        // FIXME: This toolchain handling is a mess. Just do it once
        // when the toolchain is created.
        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {
            // Return all optional components of the "rust" package for the
            // toolchain's target triple.
            let mut res = Vec::new();

            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            for extension in &targ_pkg.extensions {
                res.push(extension.clone())
            }

            Ok(res)
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn add_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::AddingRequiredComponent(self.name.to_string(), component));
            }

            if !targ_pkg.extensions.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![component],
                remove_extensions: vec![]
            };

            try!(manifestation.update(&manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn remove_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::RemovingRequiredComponent(self.name.to_string(), component));
            }

            let dist_config = try!(manifestation.read_config()).unwrap();
            if !dist_config.components.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![],
                remove_extensions: vec![component]
            };

            try!(manifestation.update(&manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn binary_file(&self, name: &str) -> PathBuf {
        let mut path = self.path.clone();
        path.push("bin");
        path.push(name.to_owned() + env::consts::EXE_SUFFIX);
        path
    }
}
<|endoftext|>


<commit_before>extern crate elasticsearch;

use elasticsearch::JsonObjectBuilder;

fn main() {
    let mut client = elasticsearch::Client::new("http://localhost:9200");
    /*
    println!("{}\n", client.transport.head("/"));
    println!("{}\n", client.transport.get("/"));
    */

    println!("{}\n", client.prepare_create_index("test".to_string())
        .set_source(JsonObjectBuilder::new()
            .insert_object("settings".to_string(), |bld| {
                bld.
                    insert("index.number_of_shards".to_string(), 1u).
                    insert("index.number_of_replicas".to_string(), 0u)
            })
            .unwrap()
        )
        .execute());

    println!("{}\n", client.get("test", "test", "1"));

    println!("{}\n", client.prepare_index("test".to_string(), "test".to_string())
      .set_id("1".to_string())
      .set_version(2u)
      .set_source(JsonObjectBuilder::new()
          .insert("foo".to_string(), 5.0f64)
          .insert("bar".to_string(), "wee".to_string())
          .insert_object("baz".to_string(), |bld| bld.insert("a".to_string(), 2.0f64))
          .insert_list("boo".to_string(), |bld| bld.push("aaa".to_string()).push("zzz".to_string()))
          .unwrap()
      )
      .set_refresh(true)
      .execute());

    println!("{}\n", client.get("test", "test", "1"));

    println!("{}\n", client.prepare_search()
      .set_indices(vec!["test".to_string()])
      .set_source(JsonObjectBuilder::new()
          .insert("fields".to_string(), vec!["foo".to_string(), "bar".to_string()])
          .unwrap()
      )
      .execute());

    println!("{}\n", client.delete("test".to_string(), "test".to_string(), "1".to_string()));

    println!("{}\n", client.prepare_index("test".to_string(), "test".to_string())
      .set_id("2".to_string())
      .set_source(JsonObjectBuilder::new()
          .insert("bar".to_string(), "lala".to_string())
          .unwrap()
      )
      .set_refresh(true)
      .execute());

    println!("{}\n", client.prepare_delete_by_query()
      .set_indices(vec!["test".to_string()])
      .set_source(JsonObjectBuilder::new()
          .insert_object("term".to_string(), |bld| bld.insert("bar".to_string(), "lala".to_string()))
          .unwrap()
      )
      .execute());

    println!("{}\n", client.prepare_delete_index()
      .set_indices(vec!["test".to_string()])
      .execute());
}
<commit_msg>describe what's happening in the example<commit_after>extern crate elasticsearch;

use elasticsearch::JsonObjectBuilder;

fn main() {
    let mut client = elasticsearch::Client::new("http://localhost:9200");
    /*
    println!("{}\n", client.transport.head("/"));
    println!("{}\n", client.transport.get("/"));
    */

    println!("create an index");
    println!("{}\n", client.prepare_create_index("test".to_string())
        .set_source(JsonObjectBuilder::new()
            .insert_object("settings".to_string(), |bld| {
                bld.
                    insert("index.number_of_shards".to_string(), 1u).
                    insert("index.number_of_replicas".to_string(), 0u)
            })
            .unwrap()
        )
        .execute());

    println!("try to fetch a non-existing document");
    println!("{}\n", client.get("test", "test", "1"));

    println!("make that document");
    println!("{}\n", client.prepare_index("test".to_string(), "test".to_string())
      .set_id("1".to_string())
      //.set_version(2u)
      .set_source(JsonObjectBuilder::new()
          .insert("foo".to_string(), 5.0f64)
          .insert("bar".to_string(), "wee".to_string())
          .insert_object("baz".to_string(), |bld| bld.insert("a".to_string(), 2.0f64))
          .insert_list("boo".to_string(), |bld| bld.push("aaa".to_string()).push("zzz".to_string()))
          .unwrap()
      )
      .set_refresh(true)
      .execute());

    println!("we should conflict if we have a conflicting version");
    println!("{}\n", client.prepare_index("test".to_string(), "test".to_string())
      .set_id("1".to_string())
      .set_version(2u)
      .set_source(JsonObjectBuilder::new()
          .insert("foo".to_string(), 5.0f64)
          .insert("bar".to_string(), "wee".to_string())
          .insert_object("baz".to_string(), |bld| bld.insert("a".to_string(), 2.0f64))
          .insert_list("boo".to_string(), |bld| bld.push("aaa".to_string()).push("zzz".to_string()))
          .unwrap()
      )
      .set_refresh(true)
      .execute());

    println!("try to fetch that document");
    println!("{}\n", client.get("test", "test", "1"));

    println!("{}\n", client.prepare_search()
      .set_indices(vec!["test".to_string()])
      .set_source(JsonObjectBuilder::new()
          .insert("fields".to_string(), vec!["foo".to_string(), "bar".to_string()])
          .unwrap()
      )
      .execute());

    println!("try to delete that document");
    println!("{}\n", client.delete("test".to_string(), "test".to_string(), "1".to_string()));

    println!("index another document");
    println!("{}\n", client.prepare_index("test".to_string(), "test".to_string())
      .set_id("2".to_string())
      .set_source(JsonObjectBuilder::new()
          .insert("bar".to_string(), "lala".to_string())
          .unwrap()
      )
      .set_refresh(true)
      .execute());

    println!("delete that document with a query");
    println!("{}\n", client.prepare_delete_by_query()
      .set_indices(vec!["test".to_string()])
      .set_source(JsonObjectBuilder::new()
          .insert_object("term".to_string(), |bld| bld.insert("bar".to_string(), "lala".to_string()))
          .unwrap()
      )
      .execute());

    println!("finally, delete our test index");
    println!("{}\n", client.prepare_delete_index()
      .set_indices(vec!["test".to_string()])
      .execute());
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

extern crate gtk;
#[macro_use]
extern crate relm;
#[macro_use]
extern crate relm_derive;

use gtk::{
    ButtonExt,
    Inhibit,
    LabelExt,
    NotebookExt,
    WidgetExt,
};
use relm::Widget;

use self::Msg::*;

#[derive(Msg)]
pub enum Msg {
    Quit,
}

relm_widget! {
    impl ::relm::Widget for Win {
        fn model() -> () {
            ()
        }

        fn update(&mut self, event: Msg) {
            match event {
                Quit => gtk::main_quit(),
            }
        }

        view! {
            gtk::Window {
                gtk::Notebook {
                    gtk::Button {
                        child: {
                            tab_label: Some("First Button"),
                        },
                        label: "+",
                    },
                    gtk::Label {
                        tab: {
                            label: &gtk::Label::new("Second page"),
                        },
                        text: "0",
                    },
                    gtk::Button {
                        label: "-",
                    },
                },
                delete_event(_, _) => (Quit, Inhibit(false)),
            }
        }
    }
}

fn main() {
    Win::run(()).unwrap();
}
<commit_msg>Add test for tabs<commit_after>/*
 * Copyright (c) 2017-2018 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

extern crate gtk;
#[macro_use]
extern crate relm;
#[macro_use]
extern crate relm_derive;
#[macro_use]
extern crate relm_test;

use gtk::{
    ButtonExt,
    Inhibit,
    LabelExt,
    NotebookExt,
    WidgetExt,
};
use relm::Widget;

use self::Msg::*;

#[derive(Msg)]
pub enum Msg {
    Quit,
}

relm_widget! {
    impl ::relm::Widget for Win {
        fn model() -> () {
            ()
        }

        fn update(&mut self, event: Msg) {
            match event {
                Quit => gtk::main_quit(),
            }
        }

        view! {
            gtk::Window {
                #[name="tabs"]
                gtk::Notebook {
                    #[name="inc_button"]
                    gtk::Button {
                        child: {
                            tab_label: Some("First Button"),
                        },
                        label: "+",
                    },
                    #[name="label"]
                    gtk::Label {
                        tab: {
                            label: &gtk::Label::new("Second page"),
                        },
                        text: "0",
                    },
                    #[name="dec_button"]
                    gtk::Button {
                        label: "-",
                    },
                },
                delete_event(_, _) => (Quit, Inhibit(false)),
            }
        }
    }
}

fn main() {
    Win::run(()).unwrap();
}

#[cfg(test)]
mod tests {
    use gtk::{Cast, Label, LabelExt, NotebookExt};

    use relm;

    use Win;

    #[test]
    fn root_widget() {
        let (_component, widgets) = relm::init_test::<Win>(()).unwrap();
        let tabs = &widgets.tabs;
        let inc_button = &widgets.inc_button;
        let label = &widgets.label;
        let dec_button = &widgets.dec_button;

        assert_eq!(tabs.get_tab_label_text(inc_button).expect("inc button label"), "First Button");
        let label_widget: Label = tabs.get_tab_label(label).expect("label widget").downcast::<Label>()
            .expect("downcast");
        assert_text!(label_widget, "Second page");
        assert_eq!(tabs.get_tab_label(dec_button), None);
        assert_eq!(tabs.get_tab_label_text(dec_button), None);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

pub trait ToOutput {
    fn to_output(&self) -> &[u8];
}

impl ToOutput for String {
    fn to_output(&self) -> &[u8] {
        self.as_bytes()
    }
}

#[derive(Debug)]
pub struct Response {
    code:       i32,
    cmsg:       String,
    ctype:      String,
    headers:    HashMap<String, String>,
    payload:    Vec<u8>,
}

impl Response {
    pub fn new() -> Response {
        Response {
            code:       200,
            cmsg:       String::new(),
            ctype:      String::from("text/plain"),
            headers:    HashMap::new(),
            payload:    Vec::new(),
        }
    }

    pub fn set_code(&mut self, code: i32, cmsg: &str) {
        self.code = code;
        self.cmsg = String::from(cmsg);
    }

    pub fn set_content_type(&mut self, ctype: &str) {
        self.ctype = String::from(ctype);
    }

    pub fn add_header(&mut self, key: &str, value: &str) {
        self.headers.insert(String::from(key), String::from(value));
    }

    pub fn append<T: ToOutput>(&mut self, payload: T) {
        self.payload.extend(payload.to_output().into_iter());
    }

    pub fn gen_output(&self) -> Vec<u8> {
        let mut output: Vec<u8> = Vec::new();
        let mut inter = String::new();

        inter.push_str(&format!("HTTP/1.1 {} {}\r\n", self.code, self.cmsg));
        inter.push_str("Connection: close\r\n");
        inter.push_str("Server: canteen/0.0.1\r\n");
        inter.push_str(&format!("Content-Type: {}\r\n", self.ctype));
        inter.push_str(&format!("Content-Length: {}\r\n", self.payload.len()));
        inter.push_str("\r\n\r\n");

        output.extend(inter.as_bytes());
        output.extend(self.payload.iter());

        output
    }
}
<commit_msg>start commenting out some of the code, Response improvements<commit_after>use std::collections::HashMap;

pub trait ToOutput {
    fn to_output(&self) -> &[u8];
}

impl ToOutput for String {
    fn to_output(&self) -> &[u8] {
        self.as_bytes()
    }
}

#[derive(Debug)]
pub struct Response {
    code:       i32,
    cmsg:       String,
    ctype:      String,
    headers:    HashMap<String, String>,
    payload:    Vec<u8>,
}

impl Response {
    pub fn new() -> Response {
        let mut res = Response {
            code:       200,
            cmsg:       String::from("OK"),
            ctype:      String::from("text/plain"),
            headers:    HashMap::new(),
            payload:    Vec::new(),
        };

        res.headers.insert(String::from("Connection"), String::from("close"));
        res.headers.insert(String::from("Server"), String::from("canteen/0.0.1"));

        res
    }

    /* set the response code
     * ex: res.set_code(200, "OK");
     */
    pub fn set_code(&mut self, code: i32, cmsg: &str) {
        self.code = code;
        self.cmsg = String::from(cmsg);
    }

    /* set the content type
     * ex: res.set_content_type("text/html");
     */
    pub fn set_content_type(&mut self, ctype: &str) {
        self.ctype = String::from(ctype);
    }

    /* add an HTTP header
     * ex: res.add_header("Connection", "close");
     */
    pub fn add_header(&mut self, key: &str, value: &str) {
        if !self.headers.contains_key(key) {
            self.headers.insert(String::from(key), String::from(value));
        }
    }

    /* add data to the payload -- can take any type that has
     * implemented the canteen::response::ToOutput trait
     */
    pub fn append<T: ToOutput>(&mut self, payload: T) {
        self.payload.extend(payload.to_output().into_iter());
    }

    pub fn gen_output(&self) -> Vec<u8> {
        let mut output: Vec<u8> = Vec::new();
        let mut inter = String::new();

        inter.push_str(&format!("HTTP/1.1 {} {}\r\n", self.code, self.cmsg));

        for (key, value) in &self.headers {
            inter.push_str(&format!("{}: {}\r\n", key, value));
        }

        inter.push_str(&format!("Content-Type: {}\r\n", self.ctype));
        inter.push_str(&format!("Content-Length: {}\r\n", self.payload.len()));
        inter.push_str("\r\n");

        output.extend(inter.as_bytes());
        output.extend(self.payload.iter());

        output
    }
}
<|endoftext|>


<commit_before>//src: https://doc.rust-lang.org/nightly/book/structs.html

#[derive(Debug)]
struct Point {
	x: i32,
	y: i32,
}

fn main() {
	println!("Hello, struct world!");
	let origin = Point { y: 2, x: 1 }; // origin: Point
	let o1 = Point { x:1, y:2 };
	//    o1.x=3;//immutable! not allowed, good!
	//	let o2 = Point { 1,2 };//not allowed, good!
	println!("The origin is at ({}, {})", origin.x, origin.y);
	println!("{:?}",o1);

	let mut point = Point { x: 0, y: 0 };
	point.x = 5;
	println!("The point is at ({}, {})", point.x, point.y);
	let point=point; //make immutable now
	//    point.x=6; //not working anymore!
	println!("The point is at ({}, {})", point.x, point.y);

	//update syntax: https://doc.rust-lang.org/nightly/book/structs.html#update-syntax
	#[derive(Debug)]
	struct Point3d {
		x: i32,
		y: i32,
		z: i32,
	}

	let mut point = Point3d { x: 3, y: 4, z: 5 };
	point = Point3d { y: 1, .. point };
    println!("{:?}", point);//3,1,5

    let origin = Point3d { x: 9, y: 10, z: 11 };
    let point = Point3d { z: 1, x: 2, .. origin };
    println!("{:?}", point);//2,10,1
}
<commit_msg>tuple struct and unit<commit_after>//src: https://doc.rust-lang.org/nightly/book/structs.html

#[derive(Debug)]
struct Point {
	x: i32,
	y: i32,
}

#[allow(unused_variables)]
fn main() {
	println!("Hello, struct world!");
	let origin = Point { y: 2, x: 1 }; // origin: Point
	let o1 = Point { x:1, y:2 };
	//    o1.x=3;//immutable! not allowed, good!
	//	let o2 = Point { 1,2 };//not allowed, good!
	println!("The origin is at ({}, {})", origin.x, origin.y);
	println!("{:?}",o1);

	let mut point = Point { x: 0, y: 0 };
	point.x = 5;
	println!("The point is at ({}, {})", point.x, point.y);
	let point=point; //make immutable now
	//    point.x=6; //not working anymore!
	println!("The point is at ({}, {})", point.x, point.y);

	//update syntax: https://doc.rust-lang.org/nightly/book/structs.html#update-syntax
	#[derive(Debug)]
	struct Point3d {
		x: i32,
		y: i32,
		z: i32,
	}

	let mut point = Point3d { x: 3, y: 4, z: 5 };
	point = Point3d { y: 1, .. point };
    println!("{:?}", point);//3,1,5

    let origin = Point3d { x: 9, y: 10, z: 11 };
    let point = Point3d { z: 1, x: 2, .. origin };
    println!("{:?}", point);//2,10,1

    //tuple struct
    struct Inches(i32);

    let length = Inches(10);

    let Inches(integer_length) = length;
    //As you can see here, you can extract the inner integer type through a destructuring let, just as with regular tuples. In this case, the let Inches(integer_length) assigns 10 to integer_length.
    println!("length is {} inches", integer_length);

    struct Electron;
//    #[allow(unused_variables)]//XXX: this here needs a better error message, rust!
    let x = Electron;
    //Such a struct is called ‘unit-like’ because it resembles the empty tuple, (), sometimes called ‘unit’. Like a tuple struct, it defines a new type.
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;

use std::{
  ops::AddAssign,
  sync::Arc,
};

#[derive(Clone, Debug)]
pub(crate) struct IdSequence<T> {
  inner: Arc<Mutex<Inner<T>>>,
}

impl<T: Default + Copy + AddAssign<T> + PartialEq<T> + PartialOrd<T> + From<u8>> IdSequence<T> {
  pub(crate) fn new(allow_zero: bool) -> Self {
    Self { inner: Arc::new(Mutex::new(Inner::new(allow_zero))) }
  }

  pub(crate) fn next(&self) -> T {
    self.inner.lock().next()
  }

  pub(crate) fn set_max(&self, max: T) {
    self.inner.lock().max = Some(max);
  }
}

#[derive(Debug)]
struct Inner<T> {
  allow_zero: bool,
  zero:       T,
  one:        T,
  max:        Option<T>,
  id:         T,
}

impl<T: Default + Copy + AddAssign<T> + PartialEq<T> + PartialOrd<T> + From<u8>> Inner<T> {
  fn new(allow_zero: bool) -> Self {
    Self {
      allow_zero,
      zero: 0.into(),
      one:  1.into(),
      max:  None,
      id:   T::default(),
    }
  }

  // FIXME: use Step trait once stable (https://github.com/rust-lang/rust/issues/42168)
  fn next(&mut self) -> T {
    if !self.allow_zero && self.id == self.zero {
      self.id += self.one;
    }
    if self.check_max() {
      let id = self.id;
      self.id += self.one;
      id
    } else {
      self.id = self.zero;
      self.next()
    }
  }

  fn check_max(&self) -> bool {
    if let Some(max) = self.max {
      self.id < max
    } else {
      true
    }
  }
}
<commit_msg>async: id_sequence: consider 0 as no maximum set<commit_after>use parking_lot::Mutex;

use std::{
  ops::AddAssign,
  sync::Arc,
};

#[derive(Clone, Debug)]
pub(crate) struct IdSequence<T> {
  inner: Arc<Mutex<Inner<T>>>,
}

impl<T: Default + Copy + AddAssign<T> + PartialEq<T> + PartialOrd<T> + From<u8>> IdSequence<T> {
  pub(crate) fn new(allow_zero: bool) -> Self {
    Self { inner: Arc::new(Mutex::new(Inner::new(allow_zero))) }
  }

  pub(crate) fn next(&self) -> T {
    self.inner.lock().next()
  }

  pub(crate) fn set_max(&self, max: T) {
    self.inner.lock().set_max(max)
  }
}

#[derive(Debug)]
struct Inner<T> {
  allow_zero: bool,
  zero:       T,
  one:        T,
  max:        Option<T>,
  id:         T,
}

impl<T: Default + Copy + AddAssign<T> + PartialEq<T> + PartialOrd<T> + From<u8>> Inner<T> {
  fn new(allow_zero: bool) -> Self {
    Self {
      allow_zero,
      zero: 0.into(),
      one:  1.into(),
      max:  None,
      id:   T::default(),
    }
  }

  fn set_max(&mut self, max: T) {
    self.max = if max == self.zero { None } else { Some(max) };
  }

  // FIXME: use Step trait once stable (https://github.com/rust-lang/rust/issues/42168)
  fn next(&mut self) -> T {
    if !self.allow_zero && self.id == self.zero {
      self.id += self.one;
    }
    if self.check_max() {
      let id = self.id;
      self.id += self.one;
      id
    } else {
      self.id = self.zero;
      self.next()
    }
  }

  fn check_max(&self) -> bool {
    if let Some(max) = self.max {
      self.id < max
    } else {
      true
    }
  }
}
<|endoftext|>