<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    epoch_info::EpochInfo,
    ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
    transaction::Version,
    validator_change::{ValidatorChangeProof, VerifierType},
    validator_set::ValidatorSet,
    waypoint::Waypoint,
};
use anyhow::{ensure, format_err, Result};
use std::sync::Arc;

/// `TrustedState` keeps track of our latest trusted state, including the latest
/// verified version and the latest verified validator set.
#[derive(Clone, Debug)]
pub struct TrustedState {
    /// The latest verified version of either a waypoint or a ledger info, either
    /// inside an epoch or the epoch change ledger info. If the TrustedState is
    /// generated from an initial waypoint, the latest_version is coincidentally
    /// the same as the waypoint version.
    latest_version: Version,
    /// The current verifier. If we're starting up fresh, this is probably a
    /// waypoint from our config. Otherwise, this is generated from the validator
    /// set in the last known epoch change ledger info.
    verifier: VerifierType,
}

/// `TrustedStateChange` is the result of attempting to ratchet to a new trusted
/// state. In order to reduce redundant error checking, `TrustedStateChange` also
/// contains references to relevant items used to ratchet us.
#[derive(Clone, Debug)]
pub enum TrustedStateChange<'a> {
    /// We have a newer `TrustedState` but it's still in the same epoch, so only
    /// the latest trusted version changed.
    Version {
        new_state: TrustedState,
        latest_li: &'a LedgerInfoWithSignatures,
    },
    /// We have a newer `TrustedState` and there was at least one epoch change,
    /// so we have a newer trusted version and a newer trusted validator set.
    Epoch {
        new_state: TrustedState,
        latest_li: &'a LedgerInfoWithSignatures,
        latest_epoch_change_li: &'a LedgerInfoWithSignatures,
        latest_validator_set: &'a ValidatorSet,
    },
}

impl TrustedState {
    /// Create an initial trusted state from a waypoint.
    pub fn from_waypoint(waypoint: Waypoint) -> Self {
        Self {
            latest_version: waypoint.version(),
            verifier: VerifierType::Waypoint(waypoint),
        }
    }

    /// Create an initial trusted state that will trust the first genesis
    /// presented to it.
    ///
    /// WARNING: this is obviously unsafe, as a malicious peer could present any
    /// arbitrary genesis and this TrustedState would gladly accept it.
    // TODO(philiphayes/dmitrip): remove this when waypoints are completely
    // integrated with client code.
    #[allow(non_snake_case)]
    pub fn new_trust_any_genesis_WARNING_UNSAFE() -> Self {
        Self {
            latest_version: 0,
            verifier: VerifierType::TrustedVerifier(EpochInfo::empty()),
        }
    }

    /// Create an initial trusted state from an epoch change ledger info and
    /// a version inside that epoch.
    pub fn from_epoch_change_ledger_info(
        latest_version: Version,
        epoch_change_li: &LedgerInfo,
    ) -> Result<Self> {
        ensure!(
            latest_version != epoch_change_li.version(),
            "A client can only enter an epoch on the boundary; only with a version inside that epoch",
        );
        ensure!(
            latest_version > epoch_change_li.version(),
            "The given version must be inside the epoch",
        );

        let validator_set = epoch_change_li.next_validator_set().ok_or_else(|| {
            format_err!("No ValidatorSet in LedgerInfo; it must not be on an epoch boundary")
        })?;

        // Generate the EpochInfo from the new validator set.
        let epoch_info = EpochInfo {
            epoch: epoch_change_li.epoch() + 1,
            verifier: Arc::new(validator_set.into()),
        };
        let verifier = VerifierType::TrustedVerifier(epoch_info);

        Ok(Self {
            latest_version,
            verifier,
        })
    }

    /// Verify and ratchet forward our trusted state using a `ValidatorChangeProof`
    /// (that moves us into the latest epoch) and a `LedgerInfoWithSignatures`
    /// inside that epoch.
    ///
    /// For example, a client sends an `UpdateToLatestLedgerRequest` to a
    /// FullNode and receives some validator change proof along with a latest
    /// ledger info inside the `UpdateToLatestLedgerResponse`. This function
    /// verifies the change proof and ratchets the trusted state version forward
    /// if the response successfully moves us into a new epoch or a new latest
    /// ledger info within our current epoch.
    ///
    /// IMPORTANT NOTE: this verification logic is currently only inteded to be
    /// used by clients and not validators, since their epoch transition logic
    /// is subtly different. Specifically, a client cannot enter an epoch when
    /// the latest ledger info is an epoch change but there is no following
    /// ledger info inside that epoch (i.e., the server is exactly on an epoch
    /// boundary).
    ///
    /// + If there was a validation error, e.g., the epoch change proof was
    /// invalid, we return an `Err`.
    ///
    /// + If the message was well formed but stale (i.e., the returned latest
    /// ledger is behind our trusted version), we also return an `Err` since
    /// stale responses should always be rejected.
    ///
    /// + If the response is fresh and there is no epoch change, we just ratchet
    /// our trusted version to the latest ledger info and return
    /// `Ok(TrustedStateChange::Version { .. })`.
    ///
    /// + If there is a new epoch and the server provides a correct proof, we
    /// ratchet our trusted version forward, update our verifier to contain
    /// the new validator set, and return `Ok(TrustedStateChange::Epoch { .. })`.
    pub fn verify_and_ratchet<'a>(
        &self,
        latest_li: &'a LedgerInfoWithSignatures,
        validator_change_proof: &'a ValidatorChangeProof,
    ) -> Result<TrustedStateChange<'a>> {
        let res_version = latest_li.ledger_info().version();
        ensure!(
            res_version >= self.latest_version,
            "The target latest ledger info is stale and behind our current trusted version",
        );

        if self
            .verifier
            .epoch_change_verification_required(latest_li.ledger_info().epoch())
        {
            // Verify the ValidatorChangeProof to move us into the latest epoch.
            let epoch_change_li = validator_change_proof.verify(&self.verifier)?;
            let new_validator_set = epoch_change_li
                    .ledger_info()
                    .next_validator_set()
                    .ok_or_else(|| format_err!(
                        "A valid ValidatorChangeProof will never return a non-epoch change ledger info"
                    ))?;
            let new_validator_verifier = Arc::new(new_validator_set.into());
            let new_epoch_info = EpochInfo {
                epoch: epoch_change_li.ledger_info().epoch() + 1,
                verifier: new_validator_verifier,
            };

            // Verify the latest ledger info inside the latest epoch.
            let new_verifier = VerifierType::TrustedVerifier(new_epoch_info);
            new_verifier.verify(latest_li)?;

            let new_state = TrustedState {
                latest_version: res_version,
                verifier: new_verifier,
            };

            Ok(TrustedStateChange::Epoch {
                new_state,
                latest_li,
                latest_epoch_change_li: epoch_change_li,
                latest_validator_set: new_validator_set,
            })
        } else {
            // The ValidatorChangeProof is empty, stale, or only gets us into our
            // current epoch. We then try to verify that the latest ledger info
            // is this epoch.
            self.verifier.verify(latest_li)?;

            let new_state = TrustedState {
                latest_version: res_version,
                verifier: self.verifier.clone(),
            };

            Ok(TrustedStateChange::Version {
                new_state,
                latest_li,
            })
        }
    }

    pub fn latest_version(&self) -> Version {
        self.latest_version
    }

    pub fn verifier(&self) -> &VerifierType {
        &self.verifier
    }
}
<commit_msg>[types] Fix a logic check in TrustedState<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    epoch_info::EpochInfo,
    ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
    transaction::Version,
    validator_change::{ValidatorChangeProof, VerifierType},
    validator_set::ValidatorSet,
    waypoint::Waypoint,
};
use anyhow::{ensure, format_err, Result};
use std::sync::Arc;

/// `TrustedState` keeps track of our latest trusted state, including the latest
/// verified version and the latest verified validator set.
#[derive(Clone, Debug)]
pub struct TrustedState {
    /// The latest verified version of either a waypoint or a ledger info, either
    /// inside an epoch or the epoch change ledger info. If the TrustedState is
    /// generated from an initial waypoint, the latest_version is coincidentally
    /// the same as the waypoint version.
    latest_version: Version,
    /// The current verifier. If we're starting up fresh, this is probably a
    /// waypoint from our config. Otherwise, this is generated from the validator
    /// set in the last known epoch change ledger info.
    verifier: VerifierType,
}

/// `TrustedStateChange` is the result of attempting to ratchet to a new trusted
/// state. In order to reduce redundant error checking, `TrustedStateChange` also
/// contains references to relevant items used to ratchet us.
#[derive(Clone, Debug)]
pub enum TrustedStateChange<'a> {
    /// We have a newer `TrustedState` but it's still in the same epoch, so only
    /// the latest trusted version changed.
    Version {
        new_state: TrustedState,
        latest_li: &'a LedgerInfoWithSignatures,
    },
    /// We have a newer `TrustedState` and there was at least one epoch change,
    /// so we have a newer trusted version and a newer trusted validator set.
    Epoch {
        new_state: TrustedState,
        latest_li: &'a LedgerInfoWithSignatures,
        latest_epoch_change_li: &'a LedgerInfoWithSignatures,
        latest_validator_set: &'a ValidatorSet,
    },
}

impl TrustedState {
    /// Create an initial trusted state from a waypoint.
    pub fn from_waypoint(waypoint: Waypoint) -> Self {
        Self {
            latest_version: waypoint.version(),
            verifier: VerifierType::Waypoint(waypoint),
        }
    }

    /// Create an initial trusted state that will trust the first genesis
    /// presented to it.
    ///
    /// WARNING: this is obviously unsafe, as a malicious peer could present any
    /// arbitrary genesis and this TrustedState would gladly accept it.
    // TODO(philiphayes/dmitrip): remove this when waypoints are completely
    // integrated with client code.
    #[allow(non_snake_case)]
    pub fn new_trust_any_genesis_WARNING_UNSAFE() -> Self {
        Self {
            latest_version: 0,
            verifier: VerifierType::TrustedVerifier(EpochInfo::empty()),
        }
    }

    /// Create an initial trusted state from an epoch change ledger info and
    /// a version inside that epoch.
    pub fn from_epoch_change_ledger_info(
        latest_version: Version,
        epoch_change_li: &LedgerInfo,
    ) -> Result<Self> {
        ensure!(
            latest_version != epoch_change_li.version(),
            "A client can only enter an epoch on the boundary; only with a version inside that epoch",
        );
        ensure!(
            latest_version > epoch_change_li.version(),
            "The given version must be inside the epoch",
        );

        let validator_set = epoch_change_li.next_validator_set().ok_or_else(|| {
            format_err!("No ValidatorSet in LedgerInfo; it must not be on an epoch boundary")
        })?;

        // Generate the EpochInfo from the new validator set.
        let epoch_info = EpochInfo {
            epoch: epoch_change_li.epoch() + 1,
            verifier: Arc::new(validator_set.into()),
        };
        let verifier = VerifierType::TrustedVerifier(epoch_info);

        Ok(Self {
            latest_version,
            verifier,
        })
    }

    /// Verify and ratchet forward our trusted state using a `ValidatorChangeProof`
    /// (that moves us into the latest epoch) and a `LedgerInfoWithSignatures`
    /// inside that epoch.
    ///
    /// For example, a client sends an `UpdateToLatestLedgerRequest` to a
    /// FullNode and receives some validator change proof along with a latest
    /// ledger info inside the `UpdateToLatestLedgerResponse`. This function
    /// verifies the change proof and ratchets the trusted state version forward
    /// if the response successfully moves us into a new epoch or a new latest
    /// ledger info within our current epoch.
    ///
    /// + If there was a validation error, e.g., the epoch change proof was
    /// invalid, we return an `Err`.
    ///
    /// + If the message was well formed but stale (i.e., the returned latest
    /// ledger is behind our trusted version), we also return an `Err` since
    /// stale responses should always be rejected.
    ///
    /// + If the response is fresh and there is no epoch change, we just ratchet
    /// our trusted version to the latest ledger info and return
    /// `Ok(TrustedStateChange::Version { .. })`.
    ///
    /// + If there is a new epoch and the server provides a correct proof, we
    /// ratchet our trusted version forward, update our verifier to contain
    /// the new validator set, and return `Ok(TrustedStateChange::Epoch { .. })`.
    pub fn verify_and_ratchet<'a>(
        &self,
        latest_li: &'a LedgerInfoWithSignatures,
        validator_change_proof: &'a ValidatorChangeProof,
    ) -> Result<TrustedStateChange<'a>> {
        let res_version = latest_li.ledger_info().version();
        ensure!(
            res_version >= self.latest_version,
            "The target latest ledger info is stale and behind our current trusted version",
        );

        if self
            .verifier
            .epoch_change_verification_required(latest_li.ledger_info().epoch())
        {
            // Verify the ValidatorChangeProof to move us into the latest epoch.
            let epoch_change_li = validator_change_proof.verify(&self.verifier)?;
            let new_validator_set = epoch_change_li
                    .ledger_info()
                    .next_validator_set()
                    .ok_or_else(|| format_err!(
                        "A valid ValidatorChangeProof will never return a non-epoch change ledger info"
                    ))?;
            let new_validator_verifier = Arc::new(new_validator_set.into());
            let new_epoch_info = EpochInfo {
                epoch: epoch_change_li.ledger_info().epoch() + 1,
                verifier: new_validator_verifier,
            };

            // Verify the latest ledger info inside the latest epoch.
            let new_verifier = VerifierType::TrustedVerifier(new_epoch_info);

            // If these are the same, then we do not have a LI for the next Epoch and hence there
            // is nothing to verify.
            if epoch_change_li != latest_li {
                new_verifier.verify(latest_li)?;
            }

            let new_state = TrustedState {
                latest_version: res_version,
                verifier: new_verifier,
            };

            Ok(TrustedStateChange::Epoch {
                new_state,
                latest_li,
                latest_epoch_change_li: epoch_change_li,
                latest_validator_set: new_validator_set,
            })
        } else {
            // The ValidatorChangeProof is empty, stale, or only gets us into our
            // current epoch. We then try to verify that the latest ledger info
            // is this epoch.
            self.verifier.verify(latest_li)?;

            let new_state = TrustedState {
                latest_version: res_version,
                verifier: self.verifier.clone(),
            };

            Ok(TrustedStateChange::Version {
                new_state,
                latest_li,
            })
        }
    }

    pub fn latest_version(&self) -> Version {
        self.latest_version
    }

    pub fn verifier(&self) -> &VerifierType {
        &self.verifier
    }
}
<|endoftext|>


<commit_before>#![crate_name = "document"]

use std::collections::hashmap::HashMap;
use std::collections::hashmap::HashSet;

#[deriving(Show)]
pub struct Document {
    /// Storage for each maintained type
    elements: Vec<Element>,
    attributes: Vec<Attribute>,
    texts: Vec<Text>,

    // Primary associations between nodes
    children: HashMap<Parent, Vec<Child>>,
    parents: HashMap<Child, Parent>,

    // Associated attributes
    assigned_attributes: HashMap<ElementNode, HashSet<AttributeNode>>,
}

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct ElementNode   { i: uint }
#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct AttributeNode { i: uint }
#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct TextNode      { i: uint }

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Parent {
    ElementParent(ElementNode),
}

impl Parent {
    pub fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementParent(e) => Some(e),
        }
    }
}

pub trait ToParent {
    fn to_parent(&self) -> Parent;
}

impl ToParent for ElementNode {
    fn to_parent(&self) -> Parent { ElementParent(*self) }
}

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Child {
    ElementChild(ElementNode),
    TextChild(TextNode),
}

impl Child {
    pub fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementChild(e) => Some(e),
            _ => None,
        }
    }

    pub fn text(&self) -> Option<TextNode> {
        match self {
            &TextChild(t) => Some(t),
            _ => None,
        }
    }
}

pub trait ToChild {
    fn to_child(&self) -> Child;
}

impl ToChild for ElementNode {
    fn to_child(&self) -> Child { ElementChild(*self) }
}

impl ToChild for TextNode {
    fn to_child(&self) -> Child { TextChild(*self) }
}

impl Document {
    pub fn new() -> Document {
        Document {
            elements: Vec::new(),
            attributes: Vec::new(),
            texts: Vec::new(),
            children: HashMap::new(),
            parents: HashMap::new(),
            assigned_attributes: HashMap::new(),
        }
    }

    pub fn new_element(&mut self, name: &str) -> ElementNode {
        let eref = ElementNode{i: self.elements.len()};
        self.elements.push(Element {
            name: name.to_string(),
        });
        eref
    }

    fn new_attribute(&mut self, name: &str, value: &str) -> AttributeNode {
        let aref = AttributeNode{i: self.attributes.len()} ;
        self.attributes.push(Attribute {
            name: name.to_string(),
            value: value.to_string(),
        });
        aref
    }

    fn new_text(&mut self, value: &str) -> TextNode {
        let tref = TextNode{i: self.texts.len()};
        self.texts.push(Text {
            value: value.to_string(),
        });
        tref
    }

    pub fn element<'a>(&'a self, element: ElementNode) -> &'a Element {
        &self.elements[element.i]
    }

    pub fn mut_element<'a>(&'a mut self, element: ElementNode) -> &'a mut Element {
        self.elements.get_mut(element.i)
    }

    fn attribute<'a>(&'a self, attribute: AttributeNode) -> &'a Attribute {
        &self.attributes[attribute.i]
    }

    fn mut_attribute<'a>(&'a mut self, attribute: AttributeNode) -> &'a mut Attribute {
        self.attributes.get_mut(attribute.i)
    }

    fn text<'a>(&'a self, text: TextNode) -> &'a Text {
        &self.texts[text.i]
    }

    fn mut_text<'a>(&'a mut self, text: TextNode) -> &'a mut Text {
        self.texts.get_mut(text.i)
    }

    fn attribute_for(&self, element: ElementNode, name: &str) -> Option<AttributeNode> {
        match self.assigned_attributes.find(&element) {
            Some(ref attributes) => {
                let mut node_attrs = attributes.iter().map(|node| (node, self.attribute(*node)));
                match node_attrs.find(|&(_, attr)| attr.name.as_slice() == name) {
                    Some((node, _)) => Some(*node),
                    None => None,
                }
            },
            None => None,
        }
    }

    pub fn get_attribute(&self, element: ElementNode, name: &str) -> Option<&str> {
        match self.attribute_for(element, name) {
            Some(aref) => Some(self.attribute(aref).value.as_slice()),
            None => None,
        }
    }

    pub fn set_attribute(&mut self, element: ElementNode, name: &str, value: &str) -> AttributeNode {
        match self.attribute_for(element, name) {
            Some(aref) => {
                self.mut_attribute(aref).value = value.to_string();
                aref
            },
            None => {
                let aref = self.new_attribute(name, value);
                let assigned_attributes = self.assigned_attributes.find_or_insert(element, HashSet::new());
                assigned_attributes.insert(aref);
                aref
            },
        }
    }

    fn remove_parentage(&mut self, child: Child) {
        match self.parent(child) {
            Some(ref parent) => {
                match self.children.find_mut(parent) {
                    Some(children) => {
                        match children.as_slice().position_elem(&child) {
                            Some(idx) => { children.remove(idx); },
                            None => {},
                        }
                    },
                    None => {},
                }
            },
            None => {}
        }
    }

    pub fn append_child<P: ToParent, C: ToChild>(&mut self, parent: P, child: C) {
        let parent = parent.to_parent();
        let child = child.to_child();

        {
            let kids = self.children.find_or_insert(parent, Vec::new());
            kids.push(child);
        }

        self.remove_parentage(child);
        self.parents.insert(child, parent);
    }

    pub fn children<P: ToParent>(&self, parent: P) -> Vec<Child> {
        let parent = parent.to_parent();

        match self.children.find(&parent) {
            Some(v) => v.iter().map(|r| r.clone()).collect(),
            None => vec![],
        }
    }

    pub fn parent(&self, child: Child) -> Option<Parent> {
        self.parents.find_copy(&child)
    }
}

#[deriving(Show)]
pub struct Element {
    name: String,
}

impl Element {
    pub fn name(&self) -> &str {
        self.name.as_slice()
    }

    pub fn set_name(&mut self, name: &str) {
        self.name = name.to_string();
    }
}

#[deriving(Show)]
pub struct Attribute {
    name: String,
    value: String,
}

#[deriving(Show)]
pub struct Text {
    value: String,
}

impl Text {
    pub fn value(&self) -> &str {
        self.value.as_slice()
    }

    pub fn set_value(& mut self, value: &str) {
        self.value = value.to_string()
    }
}

pub struct Comment;
pub struct Root;

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Any {
    ElementAny(ElementNode),
    AttributeAny(AttributeNode),
    TextAny(TextNode),
}

impl Any {
    fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementAny(e) => Some(e),
            _ => None,
        }
    }

    fn attribute(&self) -> Option<AttributeNode> {
        match self {
            &AttributeAny(e) => Some(e),
            _ => None,
        }
    }

    fn text(&self) -> Option<TextNode> {
        match self {
            &TextAny(e) => Some(e),
            _ => None,
        }
    }
}

pub trait ToAny {
    fn to_any(&self) -> Any;
}

impl ToAny for ElementNode {
    fn to_any(&self) -> Any { ElementAny(*self) }
}

impl ToAny for AttributeNode {
    fn to_any(&self) -> Any { AttributeAny(*self) }
}

impl ToAny for TextNode {
    fn to_any(&self) -> Any { TextAny(*self) }
}

#[deriving(Show,PartialEq,Clone)]
pub struct Nodeset {
    nodes: Vec<Any>,
}

impl Nodeset {
    pub fn new() -> Nodeset {
        Nodeset{nodes: Vec::new()}
    }

    pub fn add<N: ToAny>(& mut self, node: N) {
        self.nodes.push(node.to_any());
    }

    fn add_nodeset(& mut self, nodes: &Nodeset) {
        self.nodes.push_all(nodes.nodes.as_slice());
    }

    pub fn size(&self) -> uint {
        self.nodes.len()
    }

    fn iter(&self) -> std::slice::Items<Any> {
        self.nodes.iter()
    }
}

#[test]
fn can_add_an_element_as_a_child() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let children = d.children(alpha);
    assert_eq!(1, children.len());

    let child_elem = children[0].element().unwrap();
    let result = d.element(child_elem);
    assert_eq!(result.name(), "beta");
}

#[test]
fn children_are_ordered() {
    let mut d = Document::new();
    let greek = d.new_element("greek");
    let alpha = d.new_element("alpha");
    let omega = d.new_element("omega");

    d.append_child(greek, alpha);
    d.append_child(greek, omega);
    let children = d.children(greek);

    assert_eq!(2, children.len());
    let child_elem1 = children[0].element().unwrap();
    let child_elem2 = children[1].element().unwrap();
    assert_eq!(d.element(child_elem1).name(), "alpha");
    assert_eq!(d.element(child_elem2).name(), "omega");
}

#[test]
fn children_know_their_parent() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let child = d.children(alpha)[0];
    let parent = d.parent(child).unwrap();

    let parent_elem = parent.element().unwrap();
    let result = d.element(parent_elem);
    assert_eq!(result.name(), "alpha");
}

#[test]
fn replacing_parent_updates_original_parent() {
    let mut d = Document::new();
    let parent1 = d.new_element("parent1");
    let parent2 = d.new_element("parent2");
    let child = d.new_element("child");

    d.append_child(parent1, child);
    d.append_child(parent2, child);

    assert!(d.children(parent1).is_empty());
    assert_eq!(1, d.children(parent2).len());
}

#[test]
fn can_rename_an_element() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    {
        let element = d.mut_element(alpha);
        element.set_name("beta");
    }

    let beta = d.element(alpha);
    assert_eq!(beta.name(), "beta");
}

#[test]
fn elements_have_attributes() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "world");
}

#[test]
fn attributes_can_be_reset() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    d.set_attribute(alpha, "hello", "universe");

    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "universe");
}

#[test]
fn elements_can_have_text_children() {
    let mut d = Document::new();
    let sentence = d.new_element("sentence");
    let text = d.new_text("Now is the winter of our discontent.");

    d.append_child(sentence, text);

    let children = d.children(sentence);
    assert_eq!(1, children.len());

    let child_text = children[0].text().unwrap();
    assert_eq!(d.text(child_text).value(), "Now is the winter of our discontent.");
}

#[test]
fn text_can_be_changed() {
    let mut d = Document::new();
    let text = d.new_text("Now is the winter of our discontent.");

    let text_data = d.mut_text(text);
    text_data.set_value("Made glorious summer by this sun of York");

    assert_eq!(text_data.value(), "Made glorious summer by this sun of York");
}

#[test]
fn nodeset_can_include_all_node_types() {
    let mut nodes = Nodeset::new();
    let mut d = Document::new();
    let e = d.new_element("element");
    let a = d.set_attribute(e, "name", "value");
    let t = d.new_text("text");

    nodes.add(e);
    nodes.add(a);
    nodes.add(t);

    let node_vec: Vec<&Any> = nodes.iter().collect();

    assert_eq!(3, node_vec.len());
    assert_eq!(e, node_vec[0].element().unwrap());
    assert_eq!(a, node_vec[1].attribute().unwrap());
    assert_eq!(t, node_vec[2].text().unwrap());
}

#[test]
fn nodesets_can_be_combined() {
    let mut all_nodes = Nodeset::new();
    let mut nodes1 = Nodeset::new();
    let mut nodes2 = Nodeset::new();

    let mut d = Document::new();
    let e1 = d.new_element("element1");
    let e2 = d.new_element("element2");

    all_nodes.add(e1);
    all_nodes.add(e2);

    nodes1.add(e1);
    nodes2.add(e2);

    nodes1.add_nodeset(&nodes2);

    assert_eq!(all_nodes, nodes1);
}
<commit_msg>Allow iterating over all attributes for an element<commit_after>#![crate_name = "document"]

use std::collections::hashmap::HashMap;
use std::collections::hashmap::HashSet;

#[deriving(Show)]
pub struct Document {
    /// Storage for each maintained type
    elements: Vec<Element>,
    attributes: Vec<Attribute>,
    texts: Vec<Text>,

    // Primary associations between nodes
    children: HashMap<Parent, Vec<Child>>,
    parents: HashMap<Child, Parent>,

    // Associated attributes
    assigned_attributes: HashMap<ElementNode, HashSet<AttributeNode>>,
}

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct ElementNode   { i: uint }
#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct AttributeNode { i: uint }
#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub struct TextNode      { i: uint }

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Parent {
    ElementParent(ElementNode),
}

impl Parent {
    pub fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementParent(e) => Some(e),
        }
    }
}

pub trait ToParent {
    fn to_parent(&self) -> Parent;
}

impl ToParent for ElementNode {
    fn to_parent(&self) -> Parent { ElementParent(*self) }
}

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Child {
    ElementChild(ElementNode),
    TextChild(TextNode),
}

impl Child {
    pub fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementChild(e) => Some(e),
            _ => None,
        }
    }

    pub fn text(&self) -> Option<TextNode> {
        match self {
            &TextChild(t) => Some(t),
            _ => None,
        }
    }
}

pub trait ToChild {
    fn to_child(&self) -> Child;
}

impl ToChild for ElementNode {
    fn to_child(&self) -> Child { ElementChild(*self) }
}

impl ToChild for TextNode {
    fn to_child(&self) -> Child { TextChild(*self) }
}

impl Document {
    pub fn new() -> Document {
        Document {
            elements: Vec::new(),
            attributes: Vec::new(),
            texts: Vec::new(),
            children: HashMap::new(),
            parents: HashMap::new(),
            assigned_attributes: HashMap::new(),
        }
    }

    pub fn new_element(&mut self, name: &str) -> ElementNode {
        let eref = ElementNode{i: self.elements.len()};
        self.elements.push(Element {
            name: name.to_string(),
        });
        eref
    }

    fn new_attribute(&mut self, name: &str, value: &str) -> AttributeNode {
        let aref = AttributeNode{i: self.attributes.len()} ;
        self.attributes.push(Attribute {
            name: name.to_string(),
            value: value.to_string(),
        });
        aref
    }

    fn new_text(&mut self, value: &str) -> TextNode {
        let tref = TextNode{i: self.texts.len()};
        self.texts.push(Text {
            value: value.to_string(),
        });
        tref
    }

    pub fn element<'a>(&'a self, element: ElementNode) -> &'a Element {
        &self.elements[element.i]
    }

    pub fn mut_element<'a>(&'a mut self, element: ElementNode) -> &'a mut Element {
        self.elements.get_mut(element.i)
    }

    fn attribute<'a>(&'a self, attribute: AttributeNode) -> &'a Attribute {
        &self.attributes[attribute.i]
    }

    fn mut_attribute<'a>(&'a mut self, attribute: AttributeNode) -> &'a mut Attribute {
        self.attributes.get_mut(attribute.i)
    }

    fn text<'a>(&'a self, text: TextNode) -> &'a Text {
        &self.texts[text.i]
    }

    fn mut_text<'a>(&'a mut self, text: TextNode) -> &'a mut Text {
        self.texts.get_mut(text.i)
    }

    fn attribute_for(&self, element: ElementNode, name: &str) -> Option<AttributeNode> {
        match self.assigned_attributes.find(&element) {
            Some(ref attributes) => {
                let mut node_attrs = attributes.iter().map(|node| (node, self.attribute(*node)));
                match node_attrs.find(|&(_, attr)| attr.name.as_slice() == name) {
                    Some((node, _)) => Some(*node),
                    None => None,
                }
            },
            None => None,
        }
    }

    pub fn attributes(&self, element: ElementNode) -> Vec<AttributeNode> {
        match self.assigned_attributes.find(&element) {
            Some(ref attrs) => attrs.iter().map(|a| a.clone()).collect(),
            None => vec![],
        }
    }

    pub fn get_attribute(&self, element: ElementNode, name: &str) -> Option<&str> {
        match self.attribute_for(element, name) {
            Some(aref) => Some(self.attribute(aref).value.as_slice()),
            None => None,
        }
    }

    pub fn set_attribute(&mut self, element: ElementNode, name: &str, value: &str) -> AttributeNode {
        match self.attribute_for(element, name) {
            Some(aref) => {
                self.mut_attribute(aref).value = value.to_string();
                aref
            },
            None => {
                let aref = self.new_attribute(name, value);
                let assigned_attributes = self.assigned_attributes.find_or_insert(element, HashSet::new());
                assigned_attributes.insert(aref);
                aref
            },
        }
    }

    fn remove_parentage(&mut self, child: Child) {
        match self.parent(child) {
            Some(ref parent) => {
                match self.children.find_mut(parent) {
                    Some(children) => {
                        match children.as_slice().position_elem(&child) {
                            Some(idx) => { children.remove(idx); },
                            None => {},
                        }
                    },
                    None => {},
                }
            },
            None => {}
        }
    }

    pub fn append_child<P: ToParent, C: ToChild>(&mut self, parent: P, child: C) {
        let parent = parent.to_parent();
        let child = child.to_child();

        {
            let kids = self.children.find_or_insert(parent, Vec::new());
            kids.push(child);
        }

        self.remove_parentage(child);
        self.parents.insert(child, parent);
    }

    pub fn children<P: ToParent>(&self, parent: P) -> Vec<Child> {
        let parent = parent.to_parent();

        match self.children.find(&parent) {
            Some(v) => v.iter().map(|r| r.clone()).collect(),
            None => vec![],
        }
    }

    pub fn parent(&self, child: Child) -> Option<Parent> {
        self.parents.find_copy(&child)
    }
}

#[deriving(Show)]
pub struct Element {
    name: String,
}

impl Element {
    pub fn name(&self) -> &str {
        self.name.as_slice()
    }

    pub fn set_name(&mut self, name: &str) {
        self.name = name.to_string();
    }
}

#[deriving(Show)]
pub struct Attribute {
    name: String,
    value: String,
}

impl Attribute {
    pub fn name(&self)  -> &str { self.name.as_slice() }
    pub fn value(&self) -> &str { self.value.as_slice() }
}

#[deriving(Show)]
pub struct Text {
    value: String,
}

impl Text {
    pub fn value(&self) -> &str {
        self.value.as_slice()
    }

    pub fn set_value(& mut self, value: &str) {
        self.value = value.to_string()
    }
}

pub struct Comment;
pub struct Root;

#[deriving(Show,Eq,PartialEq,Hash,Clone)]
pub enum Any {
    ElementAny(ElementNode),
    AttributeAny(AttributeNode),
    TextAny(TextNode),
}

impl Any {
    fn element(&self) -> Option<ElementNode> {
        match self {
            &ElementAny(e) => Some(e),
            _ => None,
        }
    }

    fn attribute(&self) -> Option<AttributeNode> {
        match self {
            &AttributeAny(e) => Some(e),
            _ => None,
        }
    }

    fn text(&self) -> Option<TextNode> {
        match self {
            &TextAny(e) => Some(e),
            _ => None,
        }
    }
}

pub trait ToAny {
    fn to_any(&self) -> Any;
}

impl ToAny for ElementNode {
    fn to_any(&self) -> Any { ElementAny(*self) }
}

impl ToAny for AttributeNode {
    fn to_any(&self) -> Any { AttributeAny(*self) }
}

impl ToAny for TextNode {
    fn to_any(&self) -> Any { TextAny(*self) }
}

#[deriving(Show,PartialEq,Clone)]
pub struct Nodeset {
    nodes: Vec<Any>,
}

impl Nodeset {
    pub fn new() -> Nodeset {
        Nodeset{nodes: Vec::new()}
    }

    pub fn add<N: ToAny>(& mut self, node: N) {
        self.nodes.push(node.to_any());
    }

    fn add_nodeset(& mut self, nodes: &Nodeset) {
        self.nodes.push_all(nodes.nodes.as_slice());
    }

    pub fn size(&self) -> uint {
        self.nodes.len()
    }

    fn iter(&self) -> std::slice::Items<Any> {
        self.nodes.iter()
    }
}

#[test]
fn can_add_an_element_as_a_child() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let children = d.children(alpha);
    assert_eq!(1, children.len());

    let child_elem = children[0].element().unwrap();
    let result = d.element(child_elem);
    assert_eq!(result.name(), "beta");
}

#[test]
fn children_are_ordered() {
    let mut d = Document::new();
    let greek = d.new_element("greek");
    let alpha = d.new_element("alpha");
    let omega = d.new_element("omega");

    d.append_child(greek, alpha);
    d.append_child(greek, omega);
    let children = d.children(greek);

    assert_eq!(2, children.len());
    let child_elem1 = children[0].element().unwrap();
    let child_elem2 = children[1].element().unwrap();
    assert_eq!(d.element(child_elem1).name(), "alpha");
    assert_eq!(d.element(child_elem2).name(), "omega");
}

#[test]
fn children_know_their_parent() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");
    let beta  = d.new_element("beta");

    d.append_child(alpha, beta);

    let child = d.children(alpha)[0];
    let parent = d.parent(child).unwrap();

    let parent_elem = parent.element().unwrap();
    let result = d.element(parent_elem);
    assert_eq!(result.name(), "alpha");
}

#[test]
fn replacing_parent_updates_original_parent() {
    let mut d = Document::new();
    let parent1 = d.new_element("parent1");
    let parent2 = d.new_element("parent2");
    let child = d.new_element("child");

    d.append_child(parent1, child);
    d.append_child(parent2, child);

    assert!(d.children(parent1).is_empty());
    assert_eq!(1, d.children(parent2).len());
}

#[test]
fn can_rename_an_element() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    {
        let element = d.mut_element(alpha);
        element.set_name("beta");
    }

    let beta = d.element(alpha);
    assert_eq!(beta.name(), "beta");
}

#[test]
fn elements_have_attributes() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "world");
}

#[test]
fn attributes_can_be_reset() {
    let mut d = Document::new();
    let alpha = d.new_element("alpha");

    d.set_attribute(alpha, "hello", "world");
    d.set_attribute(alpha, "hello", "universe");

    let val = d.get_attribute(alpha, "hello").unwrap();
    assert_eq!(val, "universe");
}

#[test]
fn attributes_can_be_iterated() {
    let mut d = Document::new();
    let e = d.new_element("element");

    d.set_attribute(e, "name1", "value1");
    d.set_attribute(e, "name2", "value2");

    let mut attrs: Vec<&Attribute> = d.attributes(e).iter().map(|a| d.attribute(*a)).collect();
    attrs.sort_by(|a, b| a.name.cmp(&b.name));

    assert_eq!(2, attrs.len());
    assert_eq!("name1",  attrs[0].name());
    assert_eq!("value1", attrs[0].value());
    assert_eq!("name2",  attrs[1].name());
    assert_eq!("value2", attrs[1].value());
}

#[test]
fn elements_can_have_text_children() {
    let mut d = Document::new();
    let sentence = d.new_element("sentence");
    let text = d.new_text("Now is the winter of our discontent.");

    d.append_child(sentence, text);

    let children = d.children(sentence);
    assert_eq!(1, children.len());

    let child_text = children[0].text().unwrap();
    assert_eq!(d.text(child_text).value(), "Now is the winter of our discontent.");
}

#[test]
fn text_can_be_changed() {
    let mut d = Document::new();
    let text = d.new_text("Now is the winter of our discontent.");

    let text_data = d.mut_text(text);
    text_data.set_value("Made glorious summer by this sun of York");

    assert_eq!(text_data.value(), "Made glorious summer by this sun of York");
}

#[test]
fn nodeset_can_include_all_node_types() {
    let mut nodes = Nodeset::new();
    let mut d = Document::new();
    let e = d.new_element("element");
    let a = d.set_attribute(e, "name", "value");
    let t = d.new_text("text");

    nodes.add(e);
    nodes.add(a);
    nodes.add(t);

    let node_vec: Vec<&Any> = nodes.iter().collect();

    assert_eq!(3, node_vec.len());
    assert_eq!(e, node_vec[0].element().unwrap());
    assert_eq!(a, node_vec[1].attribute().unwrap());
    assert_eq!(t, node_vec[2].text().unwrap());
}

#[test]
fn nodesets_can_be_combined() {
    let mut all_nodes = Nodeset::new();
    let mut nodes1 = Nodeset::new();
    let mut nodes2 = Nodeset::new();

    let mut d = Document::new();
    let e1 = d.new_element("element1");
    let e2 = d.new_element("element2");

    all_nodes.add(e1);
    all_nodes.add(e2);

    nodes1.add(e1);
    nodes2.add(e2);

    nodes1.add_nodeset(&nodes2);

    assert_eq!(all_nodes, nodes1);
}
<|endoftext|>


<commit_before>use std::io::{stdout, stderr, Write};
use std::rc::Rc;
use std::iter::once;

use argparse::{ArgumentParser, StoreTrue, StoreFalse};

use config::Config;

pub fn print_list(config: &Config, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut all = false;
    let mut builtin = false;
    let mut hidden = false;
    let mut aliases = true;
    let mut containers = false;
    let mut zsh = false;
    let mut verbose = false;
    {
        args.insert(0, String::from("vagga _list"));
        let mut ap = ArgumentParser::new();
        ap.refer(&mut containers)
            .add_option(&["--containers"], StoreTrue,
                "Show containers instead of commands");
        ap.refer(&mut all)
            .add_option(&["-A", "--all"], StoreTrue,
                "Show all commands");
        ap.refer(&mut builtin)
            .add_option(&["--builtin"], StoreTrue,
                "Show built-in commands (starting with underscore)");
        ap.refer(&mut hidden)
            .add_option(&["--hidden"], StoreTrue,
                "Show hidden commands");
        ap.refer(&mut zsh)
            .add_option(&["--zsh"], StoreTrue,
                "Use zsh completion compatible format");
        ap.refer(&mut aliases)
            .add_option(&["--no-aliases"], StoreFalse,
                "Do not show command aliases");
        ap.refer(&mut verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
                "Verbose output (show source files
                 for containers and commands)");
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(x) => return Ok(x),
        }
    }
    if containers {
        for (cname, container) in config.containers.iter() {
            println!("{}", cname);
            if let Some(ref src) = container.source {
                if verbose {
                    println!("{:19} (from {:?})", " ", &src);
                }
            }
        }
    } else {
        let ref builtins = Some(Rc::new("<builtins>".into()));
        let mut commands: Vec<_> = config.commands.iter()
            .map(|(name, cmd)|
                (&name[..], if aliases { cmd.aliases() } else { &[][..] },
                 cmd.description().unwrap_or(&"".to_string()).to_string(),
                 cmd.source()))
            .collect();
        // TODO(tailhook) fetch builtins from completion code
        commands.push(
            ("_build", &[][..],
             "Build a container".to_string(),
             &builtins));
        commands.push(
            ("_run", &[][..],
             "Run arbitrary command, optionally building container".to_string(),
             &builtins));
        commands.push((
            "_clean", &[][..],
            "Clean containers and build artifacts".to_string(),
            &builtins));
        commands.push((
            "_list", &[][..],
            "List of built-in commands".to_string(),
            &builtins));
        commands.push((
            "_base_dir", &[][..],
            "Display a directory which contains vagga.yaml".to_string(),
            &builtins));
        commands.push((
            "_relative_work_dir", &[][..],
            "Display a relative path from the current \
            working directory to the directory \
            containing vagga.yaml".to_string(),
            &builtins));
        commands.push((
            "_update_symlinks", &[][..],
            "Updates symlinks to vagga for commands having ``symlink-name`` \
            in this project".to_string(),
            &builtins));

        let mut out = stdout();
        for (orig_name, aliases, description, source) in commands {
            if (orig_name.starts_with("_")) && !(hidden || all) {
                continue;
            }
            let all_aliases = once(orig_name).chain(
                aliases.iter().map(|x| &x[..])
                .filter(|&x| !config.commands.contains_key(x)));
            for name in all_aliases {
                if name.starts_with("_") && !(hidden || all) {
                    continue;
                }
                if zsh {
                    let descr_line = description
                        .lines().next().unwrap_or(&"");
                    out.write_all(name.as_bytes()).ok();
                    out.write_all(":".as_bytes()).ok();
                    out.write_all(descr_line.as_bytes()).ok();
                    out.write_all(b"\n").ok();
                } else {
                    out.write_all(name.as_bytes()).ok();
                    if name.len() > 19 {
                        out.write_all(b"\n                    ").ok();
                    } else {
                        for _ in name.len()..19 {
                            out.write_all(b" ").ok();
                        }
                        out.write_all(b" ").ok();
                    }
                    if description.contains("\n") {
                        for line in description.lines() {
                            out.write_all(line.as_bytes()).ok();
                            out.write_all(b"\n                    ").ok();
                        };
                    } else {
                        out.write_all(description.as_bytes()).ok();
                    }
                    out.write_all(b"\n").ok();
                    if let Some(ref src) = *source {
                        if verbose {
                            if name != orig_name {
                                println!("{:19} (from {:?}:{})", " ",
                                    &src, orig_name);
                            } else {
                                println!("{:19} (from {:?})", " ", &src);
                            }
                        }
                    }
                }
            }
        }
    }
    return Ok(0);
}

pub fn print_help(config: &Config)
    -> Result<i32, String>
{
    let mut err = stderr();
    writeln!(&mut err, "Available commands:").ok();
    for (k, cmd) in config.commands.iter() {
        if k.starts_with("_") {
            continue;
        }
        write!(&mut err, "    {}", k).ok();
        match cmd.description() {
            Some(ref val) => {
                if k.len() > 19 {
                    err.write_all(b"\n                        ").ok();
                } else {
                    for _ in k.len()..19 {
                        err.write_all(b" ").ok();
                    }
                    err.write_all(b" ").ok();
                }
                if val.contains("\n") {
                    for line in val.lines() {
                        err.write_all(line.as_bytes()).ok();
                        err.write_all(b"\n                        ").ok();
                    };
                } else {
                    err.write_all(val.as_bytes()).ok();
                }
            }
            None => {}
        }
        let aliases = cmd.aliases()
            .iter().filter(|&x| !config.commands.contains_key(x))
            .map(|x| &x[..])
            .collect::<Vec<_>>();
        if aliases.len() > 0 {
            err.write_all(b"\n                        ").ok();
            writeln!(&mut err, "(aliases: {})",
                aliases.join(", ")).ok();
        }
        err.write_all(b"\n").ok();
    }
    Ok(127)
}
<commit_msg>Remove excessive end of line in vagga output with aliases<commit_after>use std::io::{stdout, stderr, Write};
use std::rc::Rc;
use std::iter::once;

use argparse::{ArgumentParser, StoreTrue, StoreFalse};

use config::Config;

pub fn print_list(config: &Config, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut all = false;
    let mut builtin = false;
    let mut hidden = false;
    let mut aliases = true;
    let mut containers = false;
    let mut zsh = false;
    let mut verbose = false;
    {
        args.insert(0, String::from("vagga _list"));
        let mut ap = ArgumentParser::new();
        ap.refer(&mut containers)
            .add_option(&["--containers"], StoreTrue,
                "Show containers instead of commands");
        ap.refer(&mut all)
            .add_option(&["-A", "--all"], StoreTrue,
                "Show all commands");
        ap.refer(&mut builtin)
            .add_option(&["--builtin"], StoreTrue,
                "Show built-in commands (starting with underscore)");
        ap.refer(&mut hidden)
            .add_option(&["--hidden"], StoreTrue,
                "Show hidden commands");
        ap.refer(&mut zsh)
            .add_option(&["--zsh"], StoreTrue,
                "Use zsh completion compatible format");
        ap.refer(&mut aliases)
            .add_option(&["--no-aliases"], StoreFalse,
                "Do not show command aliases");
        ap.refer(&mut verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
                "Verbose output (show source files
                 for containers and commands)");
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(x) => return Ok(x),
        }
    }
    if containers {
        for (cname, container) in config.containers.iter() {
            println!("{}", cname);
            if let Some(ref src) = container.source {
                if verbose {
                    println!("{:19} (from {:?})", " ", &src);
                }
            }
        }
    } else {
        let ref builtins = Some(Rc::new("<builtins>".into()));
        let mut commands: Vec<_> = config.commands.iter()
            .map(|(name, cmd)|
                (&name[..], if aliases { cmd.aliases() } else { &[][..] },
                 cmd.description().unwrap_or(&"".to_string()).to_string(),
                 cmd.source()))
            .collect();
        // TODO(tailhook) fetch builtins from completion code
        commands.push(
            ("_build", &[][..],
             "Build a container".to_string(),
             &builtins));
        commands.push(
            ("_run", &[][..],
             "Run arbitrary command, optionally building container".to_string(),
             &builtins));
        commands.push((
            "_clean", &[][..],
            "Clean containers and build artifacts".to_string(),
            &builtins));
        commands.push((
            "_list", &[][..],
            "List of built-in commands".to_string(),
            &builtins));
        commands.push((
            "_base_dir", &[][..],
            "Display a directory which contains vagga.yaml".to_string(),
            &builtins));
        commands.push((
            "_relative_work_dir", &[][..],
            "Display a relative path from the current \
            working directory to the directory \
            containing vagga.yaml".to_string(),
            &builtins));
        commands.push((
            "_update_symlinks", &[][..],
            "Updates symlinks to vagga for commands having ``symlink-name`` \
            in this project".to_string(),
            &builtins));

        let mut out = stdout();
        for (orig_name, aliases, description, source) in commands {
            if (orig_name.starts_with("_")) && !(hidden || all) {
                continue;
            }
            let all_aliases = once(orig_name).chain(
                aliases.iter().map(|x| &x[..])
                .filter(|&x| !config.commands.contains_key(x)));
            for name in all_aliases {
                if name.starts_with("_") && !(hidden || all) {
                    continue;
                }
                if zsh {
                    let descr_line = description
                        .lines().next().unwrap_or(&"");
                    out.write_all(name.as_bytes()).ok();
                    out.write_all(":".as_bytes()).ok();
                    out.write_all(descr_line.as_bytes()).ok();
                    out.write_all(b"\n").ok();
                } else {
                    out.write_all(name.as_bytes()).ok();
                    if name.len() > 19 {
                        out.write_all(b"\n                    ").ok();
                    } else {
                        for _ in name.len()..19 {
                            out.write_all(b" ").ok();
                        }
                        out.write_all(b" ").ok();
                    }
                    if description.contains("\n") {
                        for line in description.lines() {
                            out.write_all(line.as_bytes()).ok();
                            out.write_all(b"\n                    ").ok();
                        };
                    } else {
                        out.write_all(description.as_bytes()).ok();
                    }
                    out.write_all(b"\n").ok();
                    if let Some(ref src) = *source {
                        if verbose {
                            if name != orig_name {
                                println!("{:19} (from {:?}:{})", " ",
                                    &src, orig_name);
                            } else {
                                println!("{:19} (from {:?})", " ", &src);
                            }
                        }
                    }
                }
            }
        }
    }
    return Ok(0);
}

pub fn print_help(config: &Config)
    -> Result<i32, String>
{
    let mut err = stderr();
    writeln!(&mut err, "Available commands:").ok();
    for (k, cmd) in config.commands.iter() {
        if k.starts_with("_") {
            continue;
        }
        write!(&mut err, "    {}", k).ok();
        match cmd.description() {
            Some(ref val) => {
                if k.len() > 19 {
                    err.write_all(b"\n                        ").ok();
                } else {
                    for _ in k.len()..19 {
                        err.write_all(b" ").ok();
                    }
                    err.write_all(b" ").ok();
                }
                if val.contains("\n") {
                    for line in val.lines() {
                        err.write_all(line.as_bytes()).ok();
                        err.write_all(b"\n                        ").ok();
                    };
                } else {
                    err.write_all(val.as_bytes()).ok();
                }
            }
            None => {}
        }
        let aliases = cmd.aliases()
            .iter().filter(|&x| !config.commands.contains_key(x))
            .map(|x| &x[..])
            .collect::<Vec<_>>();
        if aliases.len() > 0 {
            err.write_all(b"\n                        ").ok();
            write!(&mut err, "(aliases: {})",
                aliases.join(", ")).ok();
        }
        err.write_all(b"\n").ok();
    }
    Ok(127)
}
<|endoftext|>


<commit_before>//! Panic support in the standard library.

#![stable(feature = "core_panic_info", since = "1.41.0")]

use crate::any::Any;
use crate::fmt;

/// A struct providing information about a panic.
///
/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
/// function.
///
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
///         println!("panic occurred: {:?}", s);
///     } else {
///         println!("panic occurred");
///     }
/// }));
///
/// panic!("Normal panic");
/// ```
#[lang = "panic_info"]
#[stable(feature = "panic_hooks", since = "1.10.0")]
#[derive(Debug)]
pub struct PanicInfo<'a> {
    payload: &'a (dyn Any + Send),
    message: Option<&'a fmt::Arguments<'a>>,
    location: &'a Location<'a>,
}

impl<'a> PanicInfo<'a> {
    #[unstable(
        feature = "panic_internals",
        reason = "internal details of the implementation of the `panic!` and related macros",
        issue = "none"
    )]
    #[doc(hidden)]
    #[inline]
    pub fn internal_constructor(
        message: Option<&'a fmt::Arguments<'a>>,
        location: &'a Location<'a>,
    ) -> Self {
        struct NoPayload;
        PanicInfo { location, message, payload: &NoPayload }
    }

    #[unstable(
        feature = "panic_internals",
        reason = "internal details of the implementation of the `panic!` and related macros",
        issue = "none"
    )]
    #[doc(hidden)]
    #[inline]
    pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
        self.payload = info;
    }

    /// Returns the payload associated with the panic.
    ///
    /// This will commonly, but not always, be a `&'static str` or [`String`].
    ///
    /// [`String`]: ../../std/string/struct.String.html
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
    ///         println!("panic occurred: {:?}", s);
    ///     } else {
    ///         println!("panic occurred");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn payload(&self) -> &(dyn Any + Send) {
        self.payload
    }

    /// If the `panic!` macro from the `core` crate (not from `std`)
    /// was used with a formatting string and some additional arguments,
    /// returns that message ready to be used for example with [`fmt::write`]
    ///
    /// [`fmt::write`]: ../fmt/fn.write.html
    #[unstable(feature = "panic_info_message", issue = "66745")]
    pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
        self.message
    }

    /// Returns information about the location from which the panic originated,
    /// if available.
    ///
    /// This method will currently always return [`Some`], but this may change
    /// in future versions.
    ///
    /// [`Some`]: ../../std/option/enum.Option.html#variant.Some
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred in file '{}' at line {}",
    ///             location.file(),
    ///             location.line(),
    ///         );
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn location(&self) -> Option<&Location<'_>> {
        // NOTE: If this is changed to sometimes return None,
        // deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt.
        Some(&self.location)
    }
}

#[stable(feature = "panic_hook_display", since = "1.26.0")]
impl fmt::Display for PanicInfo<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("panicked at ")?;
        if let Some(message) = self.message {
            write!(formatter, "'{}', ", message)?
        } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() {
            write!(formatter, "'{}', ", payload)?
        }
        // NOTE: we cannot use downcast_ref::<String>() here
        // since String is not available in libcore!
        // The payload is a String when `std::panic!` is called with multiple arguments,
        // but in that case the message is also available.

        self.location.fmt(formatter)
    }
}

/// A struct containing information about the location of a panic.
///
/// This structure is created by the [`location`] method of [`PanicInfo`].
///
/// [`location`]: ../../std/panic/struct.PanicInfo.html#method.location
/// [`PanicInfo`]: ../../std/panic/struct.PanicInfo.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
///     if let Some(location) = panic_info.location() {
///         println!("panic occurred in file '{}' at line {}", location.file(), location.line());
///     } else {
///         println!("panic occurred but can't get location information...");
///     }
/// }));
///
/// panic!("Normal panic");
/// ```
#[lang = "panic_location"]
#[derive(Debug)]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub struct Location<'a> {
    file: &'a str,
    line: u32,
    col: u32,
}

impl<'a> Location<'a> {
    /// Returns the source location of the caller of this function. If that function's caller is
    /// annotated then its call location will be returned, and so on up the stack to the first call
    /// within a non-tracked function body.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(track_caller)]
    /// use core::panic::Location;
    ///
    /// /// Returns the [`Location`] at which it is called.
    /// #[track_caller]
    /// fn get_caller_location() -> &'static Location<'static> {
    ///     Location::caller()
    /// }
    ///
    /// /// Returns a [`Location`] from within this function's definition.
    /// fn get_just_one_location() -> &'static Location<'static> {
    ///     get_caller_location()
    /// }
    ///
    /// let fixed_location = get_just_one_location();
    /// assert_eq!(fixed_location.file(), file!());
    /// assert_eq!(fixed_location.line(), 15);
    /// assert_eq!(fixed_location.column(), 5);
    ///
    /// // running the same untracked function in a different location gives us the same result
    /// let second_fixed_location = get_just_one_location();
    /// assert_eq!(fixed_location.file(), second_fixed_location.file());
    /// assert_eq!(fixed_location.line(), second_fixed_location.line());
    /// assert_eq!(fixed_location.column(), second_fixed_location.column());
    ///
    /// let this_location = get_caller_location();
    /// assert_eq!(this_location.file(), file!());
    /// assert_eq!(this_location.line(), 29);
    /// assert_eq!(this_location.column(), 21);
    ///
    /// // running the tracked function in a different location produces a different value
    /// let another_location = get_caller_location();
    /// assert_eq!(this_location.file(), another_location.file());
    /// assert_ne!(this_location.line(), another_location.line());
    /// assert_ne!(this_location.column(), another_location.column());
    /// ```
    // FIXME: When stabilizing this method, please also update the documentation
    // of `intrinsics::caller_location`.
    #[unstable(
        feature = "track_caller",
        reason = "uses #[track_caller] which is not yet stable",
        issue = "47809"
    )]
    #[track_caller]
    pub const fn caller() -> &'static Location<'static> {
        crate::intrinsics::caller_location()
    }
}

impl<'a> Location<'a> {
    #![unstable(
        feature = "panic_internals",
        reason = "internal details of the implementation of the `panic!` and related macros",
        issue = "none"
    )]
    #[doc(hidden)]
    pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self {
        Location { file, line, col }
    }

    /// Returns the name of the source file from which the panic originated.
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred in file '{}'", location.file());
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn file(&self) -> &str {
        self.file
    }

    /// Returns the line number from which the panic originated.
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred at line {}", location.line());
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn line(&self) -> u32 {
        self.line
    }

    /// Returns the column from which the panic originated.
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred at column {}", location.column());
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_col", since = "1.25.0")]
    pub fn column(&self) -> u32 {
        self.col
    }
}

#[stable(feature = "panic_hook_display", since = "1.26.0")]
impl fmt::Display for Location<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "{}:{}:{}", self.file, self.line, self.col)
    }
}

/// An internal trait used by libstd to pass data from libstd to `panic_unwind`
/// and other panic runtimes. Not intended to be stabilized any time soon, do
/// not use.
#[unstable(feature = "std_internals", issue = "none")]
#[doc(hidden)]
pub unsafe trait BoxMeUp {
    /// Take full ownership of the contents.
    /// The return type is actually `Box<dyn Any + Send>`, but we cannot use `Box` in libcore.
    ///
    /// After this method got called, only some dummy default value is left in `self`.
    /// Calling this method twice, or calling `get` after calling this method, is an error.
    ///
    /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only
    /// gets a borrowed `dyn BoxMeUp`.
    fn take_box(&mut self) -> *mut (dyn Any + Send);

    /// Just borrow the contents.
    fn get(&mut self) -> &(dyn Any + Send);
}
<commit_msg>Derive common traits for panic::Location.<commit_after>//! Panic support in the standard library.

#![stable(feature = "core_panic_info", since = "1.41.0")]

use crate::any::Any;
use crate::fmt;

/// A struct providing information about a panic.
///
/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
/// function.
///
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
///         println!("panic occurred: {:?}", s);
///     } else {
///         println!("panic occurred");
///     }
/// }));
///
/// panic!("Normal panic");
/// ```
#[lang = "panic_info"]
#[stable(feature = "panic_hooks", since = "1.10.0")]
#[derive(Debug)]
pub struct PanicInfo<'a> {
    payload: &'a (dyn Any + Send),
    message: Option<&'a fmt::Arguments<'a>>,
    location: &'a Location<'a>,
}

impl<'a> PanicInfo<'a> {
    #[unstable(
        feature = "panic_internals",
        reason = "internal details of the implementation of the `panic!` and related macros",
        issue = "none"
    )]
    #[doc(hidden)]
    #[inline]
    pub fn internal_constructor(
        message: Option<&'a fmt::Arguments<'a>>,
        location: &'a Location<'a>,
    ) -> Self {
        struct NoPayload;
        PanicInfo { location, message, payload: &NoPayload }
    }

    #[unstable(
        feature = "panic_internals",
        reason = "internal details of the implementation of the `panic!` and related macros",
        issue = "none"
    )]
    #[doc(hidden)]
    #[inline]
    pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
        self.payload = info;
    }

    /// Returns the payload associated with the panic.
    ///
    /// This will commonly, but not always, be a `&'static str` or [`String`].
    ///
    /// [`String`]: ../../std/string/struct.String.html
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
    ///         println!("panic occurred: {:?}", s);
    ///     } else {
    ///         println!("panic occurred");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn payload(&self) -> &(dyn Any + Send) {
        self.payload
    }

    /// If the `panic!` macro from the `core` crate (not from `std`)
    /// was used with a formatting string and some additional arguments,
    /// returns that message ready to be used for example with [`fmt::write`]
    ///
    /// [`fmt::write`]: ../fmt/fn.write.html
    #[unstable(feature = "panic_info_message", issue = "66745")]
    pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
        self.message
    }

    /// Returns information about the location from which the panic originated,
    /// if available.
    ///
    /// This method will currently always return [`Some`], but this may change
    /// in future versions.
    ///
    /// [`Some`]: ../../std/option/enum.Option.html#variant.Some
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred in file '{}' at line {}",
    ///             location.file(),
    ///             location.line(),
    ///         );
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn location(&self) -> Option<&Location<'_>> {
        // NOTE: If this is changed to sometimes return None,
        // deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt.
        Some(&self.location)
    }
}

#[stable(feature = "panic_hook_display", since = "1.26.0")]
impl fmt::Display for PanicInfo<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("panicked at ")?;
        if let Some(message) = self.message {
            write!(formatter, "'{}', ", message)?
        } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() {
            write!(formatter, "'{}', ", payload)?
        }
        // NOTE: we cannot use downcast_ref::<String>() here
        // since String is not available in libcore!
        // The payload is a String when `std::panic!` is called with multiple arguments,
        // but in that case the message is also available.

        self.location.fmt(formatter)
    }
}

/// A struct containing information about the location of a panic.
///
/// This structure is created by the [`location`] method of [`PanicInfo`].
///
/// [`location`]: ../../std/panic/struct.PanicInfo.html#method.location
/// [`PanicInfo`]: ../../std/panic/struct.PanicInfo.html
///
/// # Examples
///
/// ```should_panic
/// use std::panic;
///
/// panic::set_hook(Box::new(|panic_info| {
///     if let Some(location) = panic_info.location() {
///         println!("panic occurred in file '{}' at line {}", location.file(), location.line());
///     } else {
///         println!("panic occurred but can't get location information...");
///     }
/// }));
///
/// panic!("Normal panic");
/// ```
///
/// # Comparisons
///
/// Comparisons for equality and ordering are made in file, line, then column priority. Such
/// comparisons can occasionally have surprising results. See [`Location::file`]'s documentation for
/// more discussion.
#[lang = "panic_location"]
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub struct Location<'a> {
    file: &'a str,
    line: u32,
    col: u32,
}

impl<'a> Location<'a> {
    /// Returns the source location of the caller of this function. If that function's caller is
    /// annotated then its call location will be returned, and so on up the stack to the first call
    /// within a non-tracked function body.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(track_caller)]
    /// use core::panic::Location;
    ///
    /// /// Returns the [`Location`] at which it is called.
    /// #[track_caller]
    /// fn get_caller_location() -> &'static Location<'static> {
    ///     Location::caller()
    /// }
    ///
    /// /// Returns a [`Location`] from within this function's definition.
    /// fn get_just_one_location() -> &'static Location<'static> {
    ///     get_caller_location()
    /// }
    ///
    /// let fixed_location = get_just_one_location();
    /// assert_eq!(fixed_location.file(), file!());
    /// assert_eq!(fixed_location.line(), 15);
    /// assert_eq!(fixed_location.column(), 5);
    ///
    /// // running the same untracked function in a different location gives us the same result
    /// let second_fixed_location = get_just_one_location();
    /// assert_eq!(fixed_location.file(), second_fixed_location.file());
    /// assert_eq!(fixed_location.line(), second_fixed_location.line());
    /// assert_eq!(fixed_location.column(), second_fixed_location.column());
    ///
    /// let this_location = get_caller_location();
    /// assert_eq!(this_location.file(), file!());
    /// assert_eq!(this_location.line(), 29);
    /// assert_eq!(this_location.column(), 21);
    ///
    /// // running the tracked function in a different location produces a different value
    /// let another_location = get_caller_location();
    /// assert_eq!(this_location.file(), another_location.file());
    /// assert_ne!(this_location.line(), another_location.line());
    /// assert_ne!(this_location.column(), another_location.column());
    /// ```
    // FIXME: When stabilizing this method, please also update the documentation
    // of `intrinsics::caller_location`.
    #[unstable(
        feature = "track_caller",
        reason = "uses #[track_caller] which is not yet stable",
        issue = "47809"
    )]
    #[track_caller]
    pub const fn caller() -> &'static Location<'static> {
        crate::intrinsics::caller_location()
    }
}

impl<'a> Location<'a> {
    #![unstable(
        feature = "panic_internals",
        reason = "internal details of the implementation of the `panic!` and related macros",
        issue = "none"
    )]
    #[doc(hidden)]
    pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self {
        Location { file, line, col }
    }

    /// Returns the name of the source file from which the panic originated.
    ///
    /// # `&str`, not `&Path`
    ///
    /// The returned name refers to a source path on the compiling system, but it isn't valid to
    /// represent this directly as a `&Path`. The compiled code may run on a different system with
    /// a different `Path` implementation than the system providing the contents and this library
    /// does not currently have a different "host path" type.
    ///
    /// The most surprising behavior occurs when "the same" file is reachable via multiple paths in
    /// the module system (usually using the `#[path = "..."]` attribute or similar), which can
    /// cause what appears to be identical code to return differing values from this function.
    ///
    /// # Cross-compilation
    ///
    /// This value is not suitable for passing to `Path::new` or similar constructors when the host
    /// platform and target platform differ.
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred in file '{}'", location.file());
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn file(&self) -> &str {
        self.file
    }

    /// Returns the line number from which the panic originated.
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred at line {}", location.line());
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_hooks", since = "1.10.0")]
    pub fn line(&self) -> u32 {
        self.line
    }

    /// Returns the column from which the panic originated.
    ///
    /// # Examples
    ///
    /// ```should_panic
    /// use std::panic;
    ///
    /// panic::set_hook(Box::new(|panic_info| {
    ///     if let Some(location) = panic_info.location() {
    ///         println!("panic occurred at column {}", location.column());
    ///     } else {
    ///         println!("panic occurred but can't get location information...");
    ///     }
    /// }));
    ///
    /// panic!("Normal panic");
    /// ```
    #[stable(feature = "panic_col", since = "1.25.0")]
    pub fn column(&self) -> u32 {
        self.col
    }
}

#[stable(feature = "panic_hook_display", since = "1.26.0")]
impl fmt::Display for Location<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "{}:{}:{}", self.file, self.line, self.col)
    }
}

/// An internal trait used by libstd to pass data from libstd to `panic_unwind`
/// and other panic runtimes. Not intended to be stabilized any time soon, do
/// not use.
#[unstable(feature = "std_internals", issue = "none")]
#[doc(hidden)]
pub unsafe trait BoxMeUp {
    /// Take full ownership of the contents.
    /// The return type is actually `Box<dyn Any + Send>`, but we cannot use `Box` in libcore.
    ///
    /// After this method got called, only some dummy default value is left in `self`.
    /// Calling this method twice, or calling `get` after calling this method, is an error.
    ///
    /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only
    /// gets a borrowed `dyn BoxMeUp`.
    fn take_box(&mut self) -> *mut (dyn Any + Send);

    /// Just borrow the contents.
    fn get(&mut self) -> &(dyn Any + Send);
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use libc::{c_char, c_int, c_void};
use std::mem;
use sys::ffi;
use ComponentExt;

pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[
    ("CPU", &['T' as i8, 'C' as i8, '0' as i8, 'P' as i8]), // CPU "TC0P"
    ("GPU", &['T' as i8, 'G' as i8, '0' as i8, 'P' as i8]), // GPU "TG0P"
    ("Battery", &['T' as i8, 'B' as i8, '0' as i8, 'T' as i8]), // Battery "TB0T"
];

pub struct ComponentFFI {
    input_structure: ffi::KeyData_t,
    val: ffi::Val_t,
}

impl ComponentFFI {
    fn new(key: &[i8], con: ffi::io_connect_t) -> Option<ComponentFFI> {
        unsafe { get_key_size(con, key) }
            .ok()
            .map(|(input_structure, val)| ComponentFFI {
                input_structure,
                val,
            })
    }

    fn get_temperature(&self, con: ffi::io_connect_t) -> Option<f32> {
        get_temperature_inner(con, &self.input_structure, &self.val)
    }
}

/// Struct containing a component information (temperature and name for the moment).
pub struct Component {
    temperature: f32,
    max: f32,
    critical: Option<f32>,
    label: String,
    ffi_part: ComponentFFI,
    connection: ffi::io_connect_t,
}

impl Component {
    /// Creates a new `Component` with the given information.
    pub(crate) fn new(
        label: String,
        max: Option<f32>,
        critical: Option<f32>,
        key: &[i8],
        connection: ffi::io_connect_t,
    ) -> Option<Component> {
        let ffi_part = ComponentFFI::new(key, connection)?;
        ffi_part
            .get_temperature(connection)
            .map(|temperature| Component {
                temperature,
                label,
                max: max.unwrap_or(0.0),
                critical,
                ffi_part,
                connection,
            })
    }
}

impl ComponentExt for Component {
    fn get_temperature(&self) -> f32 {
        self.temperature
    }

    fn get_max(&self) -> f32 {
        self.max
    }

    fn get_critical(&self) -> Option<f32> {
        self.critical
    }

    fn get_label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        if let Some(temp) = self.ffi_part.get_temperature(self.connection) {
            self.temperature = temp;
            if self.temperature > self.max {
                self.max = self.temperature;
            }
        }
    }
}

unsafe fn perform_call(
    conn: ffi::io_connect_t,
    index: c_int,
    input_structure: *const ffi::KeyData_t,
    output_structure: *mut ffi::KeyData_t,
) -> i32 {
    let mut structure_output_size = mem::size_of::<ffi::KeyData_t>();

    ffi::IOConnectCallStructMethod(
        conn,
        index as u32,
        input_structure,
        mem::size_of::<ffi::KeyData_t>(),
        output_structure,
        &mut structure_output_size,
    )
}

// Adapted from https://github.com/lavoiesl/osx-cpu-temp/blob/master/smc.c#L28
#[inline]
fn strtoul(s: &[i8]) -> u32 {
    ((s[0] as u32) << (3u32 << 3))
        + ((s[1] as u32) << (2u32 << 3))
        + ((s[2] as u32) << (1u32 << 3))
        + ((s[3] as u32) << (0u32 << 3))
}

#[inline]
unsafe fn ultostr(s: *mut c_char, val: u32) {
    *s.offset(0) = ((val >> 24) % 128) as i8;
    *s.offset(1) = ((val >> 16) % 128) as i8;
    *s.offset(2) = ((val >> 8) % 128) as i8;
    *s.offset(3) = (val % 128) as i8;
    *s.offset(4) = 0;
}

unsafe fn get_key_size(
    con: ffi::io_connect_t,
    key: &[i8],
) -> Result<(ffi::KeyData_t, ffi::Val_t), i32> {
    let mut input_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut val: ffi::Val_t = mem::zeroed::<ffi::Val_t>();

    input_structure.key = strtoul(key);
    input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO;

    let result = perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        &input_structure,
        &mut output_structure,
    );
    if result != ffi::KIO_RETURN_SUCCESS {
        return Err(result);
    }

    val.data_size = output_structure.key_info.data_size;
    ultostr(
        val.data_type.as_mut_ptr(),
        output_structure.key_info.data_type,
    );
    input_structure.key_info.data_size = val.data_size;
    input_structure.data8 = ffi::SMC_CMD_READ_BYTES;
    Ok((input_structure, val))
}

unsafe fn read_key(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    mut val: ffi::Val_t,
) -> Result<ffi::Val_t, i32> {
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();

    match perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        input_structure,
        &mut output_structure,
    ) {
        ffi::KIO_RETURN_SUCCESS => {
            libc::memcpy(
                val.bytes.as_mut_ptr() as *mut c_void,
                output_structure.bytes.as_mut_ptr() as *mut c_void,
                mem::size_of::<[u8; 32]>(),
            );
            Ok(val)
        }
        result => Err(result),
    }
}

fn get_temperature_inner(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    original_val: &ffi::Val_t,
) -> Option<f32> {
    if let Ok(val) = unsafe { read_key(con, input_structure, (*original_val).clone()) } {
        if val.data_size > 0
            && unsafe { libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) } == 0
        {
            // convert sp78 value to temperature
            let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2);
            return Some(x as f32 / 64f32);
        }
    }
    None
}

pub(crate) fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option<f32> {
    let (input_structure, val) = unsafe { get_key_size(con, &key) }.ok()?;
    get_temperature_inner(con, &input_structure, &val)
}
<commit_msg>Renamed CPU proximity temp and added cpu die temp<commit_after>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use libc::{c_char, c_int, c_void};
use std::mem;
use sys::ffi;
use ComponentExt;

pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[
    ("CPU Die", &['T' as i8, 'C' as i8, '0' as i8, 'F' as i8]), // CPU Die "TC0F"
    ("CPU Proximity", &['T' as i8, 'C' as i8, '0' as i8, 'P' as i8]), // CPU Proximity "TC0P"
    ("GPU", &['T' as i8, 'G' as i8, '0' as i8, 'P' as i8]), // GPU "TG0P"
    ("Battery", &['T' as i8, 'B' as i8, '0' as i8, 'T' as i8]), // Battery "TB0T"
];

pub struct ComponentFFI {
    input_structure: ffi::KeyData_t,
    val: ffi::Val_t,
}

impl ComponentFFI {
    fn new(key: &[i8], con: ffi::io_connect_t) -> Option<ComponentFFI> {
        unsafe { get_key_size(con, key) }
            .ok()
            .map(|(input_structure, val)| ComponentFFI {
                input_structure,
                val,
            })
    }

    fn get_temperature(&self, con: ffi::io_connect_t) -> Option<f32> {
        get_temperature_inner(con, &self.input_structure, &self.val)
    }
}

/// Struct containing a component information (temperature and name for the moment).
pub struct Component {
    temperature: f32,
    max: f32,
    critical: Option<f32>,
    label: String,
    ffi_part: ComponentFFI,
    connection: ffi::io_connect_t,
}

impl Component {
    /// Creates a new `Component` with the given information.
    pub(crate) fn new(
        label: String,
        max: Option<f32>,
        critical: Option<f32>,
        key: &[i8],
        connection: ffi::io_connect_t,
    ) -> Option<Component> {
        let ffi_part = ComponentFFI::new(key, connection)?;
        ffi_part
            .get_temperature(connection)
            .map(|temperature| Component {
                temperature,
                label,
                max: max.unwrap_or(0.0),
                critical,
                ffi_part,
                connection,
            })
    }
}

impl ComponentExt for Component {
    fn get_temperature(&self) -> f32 {
        self.temperature
    }

    fn get_max(&self) -> f32 {
        self.max
    }

    fn get_critical(&self) -> Option<f32> {
        self.critical
    }

    fn get_label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        if let Some(temp) = self.ffi_part.get_temperature(self.connection) {
            self.temperature = temp;
            if self.temperature > self.max {
                self.max = self.temperature;
            }
        }
    }
}

unsafe fn perform_call(
    conn: ffi::io_connect_t,
    index: c_int,
    input_structure: *const ffi::KeyData_t,
    output_structure: *mut ffi::KeyData_t,
) -> i32 {
    let mut structure_output_size = mem::size_of::<ffi::KeyData_t>();

    ffi::IOConnectCallStructMethod(
        conn,
        index as u32,
        input_structure,
        mem::size_of::<ffi::KeyData_t>(),
        output_structure,
        &mut structure_output_size,
    )
}

// Adapted from https://github.com/lavoiesl/osx-cpu-temp/blob/master/smc.c#L28
#[inline]
fn strtoul(s: &[i8]) -> u32 {
    ((s[0] as u32) << (3u32 << 3))
        + ((s[1] as u32) << (2u32 << 3))
        + ((s[2] as u32) << (1u32 << 3))
        + ((s[3] as u32) << (0u32 << 3))
}

#[inline]
unsafe fn ultostr(s: *mut c_char, val: u32) {
    *s.offset(0) = ((val >> 24) % 128) as i8;
    *s.offset(1) = ((val >> 16) % 128) as i8;
    *s.offset(2) = ((val >> 8) % 128) as i8;
    *s.offset(3) = (val % 128) as i8;
    *s.offset(4) = 0;
}

unsafe fn get_key_size(
    con: ffi::io_connect_t,
    key: &[i8],
) -> Result<(ffi::KeyData_t, ffi::Val_t), i32> {
    let mut input_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut val: ffi::Val_t = mem::zeroed::<ffi::Val_t>();

    input_structure.key = strtoul(key);
    input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO;

    let result = perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        &input_structure,
        &mut output_structure,
    );
    if result != ffi::KIO_RETURN_SUCCESS {
        return Err(result);
    }

    val.data_size = output_structure.key_info.data_size;
    ultostr(
        val.data_type.as_mut_ptr(),
        output_structure.key_info.data_type,
    );
    input_structure.key_info.data_size = val.data_size;
    input_structure.data8 = ffi::SMC_CMD_READ_BYTES;
    Ok((input_structure, val))
}

unsafe fn read_key(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    mut val: ffi::Val_t,
) -> Result<ffi::Val_t, i32> {
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();

    match perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        input_structure,
        &mut output_structure,
    ) {
        ffi::KIO_RETURN_SUCCESS => {
            libc::memcpy(
                val.bytes.as_mut_ptr() as *mut c_void,
                output_structure.bytes.as_mut_ptr() as *mut c_void,
                mem::size_of::<[u8; 32]>(),
            );
            Ok(val)
        }
        result => Err(result),
    }
}

fn get_temperature_inner(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    original_val: &ffi::Val_t,
) -> Option<f32> {
    if let Ok(val) = unsafe { read_key(con, input_structure, (*original_val).clone()) } {
        if val.data_size > 0
            && unsafe { libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) } == 0
        {
            // convert sp78 value to temperature
            let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2);
            return Some(x as f32 / 64f32);
        }
    }
    None
}

pub(crate) fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option<f32> {
    let (input_structure, val) = unsafe { get_key_size(con, &key) }.ok()?;
    get_temperature_inner(con, &input_structure, &val)
}
<|endoftext|>