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

use std::collections::{BTreeMap, HashMap};
use std::time::SystemTime;

use chrono::{DateTime, Utc};
use exempi;
use exempi::Xmp;
use plist::Value;

use audit::{Report, SkipReason};
use xmp::ns::*;
use xmp::{ToXmp, XmpProperty, XmpTranslator};

lazy_static! {
    /// HashMap for Exif properties (Aperture) to XMP.
    static ref EXIF_TO_XMP: HashMap<&'static str, XmpTranslator> = hashmap!{
        "ApertureValue" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ApertureValue")),
        "Artist" => XmpTranslator::Property(XmpProperty::new(
            NS_DC, "creator")),
        "CameraSerialNumber" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "SerialNumber")),
        "CaptureDayOfMonth" => XmpTranslator::Custom,
        "CaptureDayOfWeek" => XmpTranslator::None,
        "CaptureHourOfDay" => XmpTranslator::None,
        "CaptureMinuteOfHour" => XmpTranslator::None,
        "CaptureMonthOfYear" => XmpTranslator::None,
        "CaptureSecondOfMinute" => XmpTranslator::None,
        "CaptureYear" => XmpTranslator::None,
        //"ColorModel"
        "ColorSpace" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ColorSpace")),
        "Copyright" => XmpTranslator::Property(XmpProperty::new(
            NS_DC, "rights")),
        "Contrast" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "Contrast")),
        "Depth" => XmpTranslator::Property(XmpProperty::new(
            NS_TIFF, "BitsPerSample")),
        "ExifVersion" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExifVersion")),
        "ExposureBiasValue" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExposureBiasValue")),
        "ExposureMode" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExposureMode")),
        "ExposureProgram" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExposureProgram")),
        "Firmware" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "Firmware")),
        "Flash" => XmpTranslator::Custom,
        "FlashExposureComp"  => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "FlashCompensation")),
        "FlashPixVersion" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "FlashPixVersion")),
        "FocalLength" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "FocalLength")),
        "FocusDistance" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "ApproximateFocusDistance")),
        //    +- Exif.FocusMode
        "ISOSpeedRating" => XmpTranslator::Custom, // ISOSpeedRatings[] as int
        "ImageDate" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "DateTimeOriginal")),
        // It is possible that this will be overwritten by IPTC
        // Also it seems that by default Olympus files have this property
        // set to something irrelevant.
        "ImageDescription" => XmpTranslator::Property(XmpProperty::new(
            NS_DC, "description")),
        "Latitude"  => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "GPSLatitude")),
        "LensMaxMM" => XmpTranslator::None,
        "LensMinMM" => XmpTranslator::Custom,
        "LensModel" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "Lens")),
        "LightSource" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "LightSource")),
        "Longitude" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "GPSLongitude")),
        "Make" =>  XmpTranslator::Property(XmpProperty::new(
            NS_TIFF, "Make")),
        "MaxApertureValue" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "MaxApertureValue")),
        "MeteringMode" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "MeteringMode")),
        "Model" => XmpTranslator::Property(XmpProperty::new(
            NS_TIFF, "Model")),
        "OwnerName" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "OwnerName")),
        "PixelHeight" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "PixelYDimension")),
        "PixelWidth" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "PixelXDimension")),
        //    +- Exif.ProfileName
        "Saturation"  => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "Saturation")),
        "SceneCaptureType" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "SceneCaptureType")),
        "Sharpness" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "Sharpness")),
        "ShutterSpeed" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ShutterSpeedValue")),
        "Software" => XmpTranslator::Property(XmpProperty::new(
            NS_XMP, "CreatorTool")),
        "WhiteBalance" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "SceneCaptureType")),
        //    +- Exif.WhiteBalanceIndex
    };
}

#[derive(PartialEq)]
pub enum ExifValue {
    None,
    Int(i64),
    Str(String),
    Date(DateTime<Utc>),
    Real(f64),
}

pub struct ExifProperties {
    pub bag: BTreeMap<String, ExifValue>,
}

impl ExifProperties {
    pub fn from(
        dict: &Option<plist::Dictionary>,
        auditor: &mut Option<&mut Report>,
    ) -> Option<ExifProperties> {
        if dict.is_none() {
            return None;
        }
        let dict = dict.as_ref().unwrap();
        let mut values: BTreeMap<String, ExifValue> = BTreeMap::new();
        for (key, value) in dict {
            let ev = match *value {
                Value::Integer(n) => n.as_signed().map_or(ExifValue::None, |v| ExifValue::Int(v)),
                Value::Real(f) => ExifValue::Real(f),
                Value::String(ref s) => ExifValue::Str(s.to_owned()),
                Value::Date(ref d) => {
                    let t: SystemTime = d.clone().into();
                    ExifValue::Date(t.into())
                }
                _ => ExifValue::None,
            };
            if ev != ExifValue::None {
                values.insert(key.to_owned(), ev);
                if let Some(ref mut r) = *auditor {
                    if !EXIF_TO_XMP.contains_key(&key.as_str()) {
                        r.skip(&format!("Exif.{}", key), SkipReason::UnknownProp);
                    }
                }
            } else if let Some(ref mut r) = *auditor {
                r.skip(&format!("Exif.{}", key), SkipReason::InvalidType);
            }
        }
        Some(ExifProperties { bag: values })
    }

    pub fn value_to_string(value: &ExifValue) -> Option<String> {
        match *value {
            ExifValue::Str(ref str) => Some(str.clone()),
            ExifValue::Int(i) => Some(format!("{}", i)),
            _ => None,
        }
    }

    /// ISOSpeedRatings is an array in XMP.
    fn iso(&self, xmp: &mut Xmp) {
        let iso = self.bag.get("ISOSpeedRatings");
        if let Some(&ExifValue::Int(i)) = iso {
            if let Err(err) = xmp.set_array_item(
                NS_EXIF,
                "ISOSpeedRatings",
                0,
                &format!("{}", i),
                exempi::PROP_NONE,
            ) {
                println!("Error converting ISO {:?}", err);
            }
        }
    }

    /// Will convert to the LensInfo
    fn lens_info(&self, xmp: &mut Xmp) {
        let min = self.bag.get("LensMinMM");
        let max = self.bag.get("LensMaxMM");
        if min.is_none() || max.is_none() {
            return;
        }
        let min = match *min.unwrap() {
            ExifValue::Int(i) => i as f64,
            ExifValue::Real(f) => f,
            _ => return,
        };
        let max = match *max.unwrap() {
            ExifValue::Int(i) => i as f64,
            ExifValue::Real(f) => f,
            _ => return,
        };

        let value = format!("{}/100 {}/100 0/1 0/1", min * 100.0, max * 100.0);
        if let Err(err) = xmp.set_property(NS_EXIF_AUX, "LensInfo", &value, exempi::PROP_NONE) {
            println!("Error converting LensInfo {:?}", err);
        }
    }

    fn custom_value_to_string(&self, key: &str, xmp: &mut Xmp) {
        match key {
            "Flash" => {}
            "ISOSpeedRatings" => {
                self.iso(xmp);
            }
            "LensMinMM" => {
                self.lens_info(xmp);
            }
            _ => {}
        }
    }
}

impl ToXmp for ExifProperties {
    fn to_xmp(&self, xmp: &mut Xmp) -> bool {
        for (key, value) in &self.bag {
            if let Some(ref translator) = EXIF_TO_XMP.get(&key.as_str()) {
                match *(*translator) {
                    XmpTranslator::Property(ref prop) => {
                        if let Some(value) = Self::value_to_string(value) {
                            /*let result = */
                            prop.put_into_xmp(&value, xmp);
                        }
                    }
                    XmpTranslator::Custom => self.custom_value_to_string(key, xmp),
                    _ => {}
                }
            }
        }
        true
    }
}
<commit_msg>exif: Fix clippy warning<commit_after>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

use std::collections::{BTreeMap, HashMap};
use std::time::SystemTime;

use chrono::{DateTime, Utc};
use exempi;
use exempi::Xmp;
use plist::Value;

use audit::{Report, SkipReason};
use xmp::ns::*;
use xmp::{ToXmp, XmpProperty, XmpTranslator};

lazy_static! {
    /// HashMap for Exif properties (Aperture) to XMP.
    static ref EXIF_TO_XMP: HashMap<&'static str, XmpTranslator> = hashmap!{
        "ApertureValue" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ApertureValue")),
        "Artist" => XmpTranslator::Property(XmpProperty::new(
            NS_DC, "creator")),
        "CameraSerialNumber" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "SerialNumber")),
        "CaptureDayOfMonth" => XmpTranslator::Custom,
        "CaptureDayOfWeek" => XmpTranslator::None,
        "CaptureHourOfDay" => XmpTranslator::None,
        "CaptureMinuteOfHour" => XmpTranslator::None,
        "CaptureMonthOfYear" => XmpTranslator::None,
        "CaptureSecondOfMinute" => XmpTranslator::None,
        "CaptureYear" => XmpTranslator::None,
        //"ColorModel"
        "ColorSpace" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ColorSpace")),
        "Copyright" => XmpTranslator::Property(XmpProperty::new(
            NS_DC, "rights")),
        "Contrast" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "Contrast")),
        "Depth" => XmpTranslator::Property(XmpProperty::new(
            NS_TIFF, "BitsPerSample")),
        "ExifVersion" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExifVersion")),
        "ExposureBiasValue" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExposureBiasValue")),
        "ExposureMode" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExposureMode")),
        "ExposureProgram" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ExposureProgram")),
        "Firmware" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "Firmware")),
        "Flash" => XmpTranslator::Custom,
        "FlashExposureComp"  => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "FlashCompensation")),
        "FlashPixVersion" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "FlashPixVersion")),
        "FocalLength" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "FocalLength")),
        "FocusDistance" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "ApproximateFocusDistance")),
        //    +- Exif.FocusMode
        "ISOSpeedRating" => XmpTranslator::Custom, // ISOSpeedRatings[] as int
        "ImageDate" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "DateTimeOriginal")),
        // It is possible that this will be overwritten by IPTC
        // Also it seems that by default Olympus files have this property
        // set to something irrelevant.
        "ImageDescription" => XmpTranslator::Property(XmpProperty::new(
            NS_DC, "description")),
        "Latitude"  => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "GPSLatitude")),
        "LensMaxMM" => XmpTranslator::None,
        "LensMinMM" => XmpTranslator::Custom,
        "LensModel" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "Lens")),
        "LightSource" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "LightSource")),
        "Longitude" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "GPSLongitude")),
        "Make" =>  XmpTranslator::Property(XmpProperty::new(
            NS_TIFF, "Make")),
        "MaxApertureValue" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "MaxApertureValue")),
        "MeteringMode" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "MeteringMode")),
        "Model" => XmpTranslator::Property(XmpProperty::new(
            NS_TIFF, "Model")),
        "OwnerName" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF_AUX, "OwnerName")),
        "PixelHeight" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "PixelYDimension")),
        "PixelWidth" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "PixelXDimension")),
        //    +- Exif.ProfileName
        "Saturation"  => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "Saturation")),
        "SceneCaptureType" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "SceneCaptureType")),
        "Sharpness" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "Sharpness")),
        "ShutterSpeed" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "ShutterSpeedValue")),
        "Software" => XmpTranslator::Property(XmpProperty::new(
            NS_XMP, "CreatorTool")),
        "WhiteBalance" => XmpTranslator::Property(XmpProperty::new(
            NS_EXIF, "SceneCaptureType")),
        //    +- Exif.WhiteBalanceIndex
    };
}

#[derive(PartialEq)]
pub enum ExifValue {
    None,
    Int(i64),
    Str(String),
    Date(DateTime<Utc>),
    Real(f64),
}

pub struct ExifProperties {
    pub bag: BTreeMap<String, ExifValue>,
}

impl ExifProperties {
    pub fn from(
        dict: &Option<plist::Dictionary>,
        auditor: &mut Option<&mut Report>,
    ) -> Option<ExifProperties> {
        if dict.is_none() {
            return None;
        }
        let dict = dict.as_ref().unwrap();
        let mut values: BTreeMap<String, ExifValue> = BTreeMap::new();
        for (key, value) in dict {
            let ev = match *value {
                Value::Integer(n) => n.as_signed().map_or(ExifValue::None, ExifValue::Int),
                Value::Real(f) => ExifValue::Real(f),
                Value::String(ref s) => ExifValue::Str(s.to_owned()),
                Value::Date(ref d) => {
                    let t: SystemTime = d.clone().into();
                    ExifValue::Date(t.into())
                }
                _ => ExifValue::None,
            };
            if ev != ExifValue::None {
                values.insert(key.to_owned(), ev);
                if let Some(ref mut r) = *auditor {
                    if !EXIF_TO_XMP.contains_key(&key.as_str()) {
                        r.skip(&format!("Exif.{}", key), SkipReason::UnknownProp);
                    }
                }
            } else if let Some(ref mut r) = *auditor {
                r.skip(&format!("Exif.{}", key), SkipReason::InvalidType);
            }
        }
        Some(ExifProperties { bag: values })
    }

    pub fn value_to_string(value: &ExifValue) -> Option<String> {
        match *value {
            ExifValue::Str(ref str) => Some(str.clone()),
            ExifValue::Int(i) => Some(format!("{}", i)),
            _ => None,
        }
    }

    /// ISOSpeedRatings is an array in XMP.
    fn iso(&self, xmp: &mut Xmp) {
        let iso = self.bag.get("ISOSpeedRatings");
        if let Some(&ExifValue::Int(i)) = iso {
            if let Err(err) = xmp.set_array_item(
                NS_EXIF,
                "ISOSpeedRatings",
                0,
                &format!("{}", i),
                exempi::PROP_NONE,
            ) {
                println!("Error converting ISO {:?}", err);
            }
        }
    }

    /// Will convert to the LensInfo
    fn lens_info(&self, xmp: &mut Xmp) {
        let min = self.bag.get("LensMinMM");
        let max = self.bag.get("LensMaxMM");
        if min.is_none() || max.is_none() {
            return;
        }
        let min = match *min.unwrap() {
            ExifValue::Int(i) => i as f64,
            ExifValue::Real(f) => f,
            _ => return,
        };
        let max = match *max.unwrap() {
            ExifValue::Int(i) => i as f64,
            ExifValue::Real(f) => f,
            _ => return,
        };

        let value = format!("{}/100 {}/100 0/1 0/1", min * 100.0, max * 100.0);
        if let Err(err) = xmp.set_property(NS_EXIF_AUX, "LensInfo", &value, exempi::PROP_NONE) {
            println!("Error converting LensInfo {:?}", err);
        }
    }

    fn custom_value_to_string(&self, key: &str, xmp: &mut Xmp) {
        match key {
            "Flash" => {}
            "ISOSpeedRatings" => {
                self.iso(xmp);
            }
            "LensMinMM" => {
                self.lens_info(xmp);
            }
            _ => {}
        }
    }
}

impl ToXmp for ExifProperties {
    fn to_xmp(&self, xmp: &mut Xmp) -> bool {
        for (key, value) in &self.bag {
            if let Some(ref translator) = EXIF_TO_XMP.get(&key.as_str()) {
                match *(*translator) {
                    XmpTranslator::Property(ref prop) => {
                        if let Some(value) = Self::value_to_string(value) {
                            /*let result = */
                            prop.put_into_xmp(&value, xmp);
                        }
                    }
                    XmpTranslator::Custom => self.custom_value_to_string(key, xmp),
                    _ => {}
                }
            }
        }
        true
    }
}
<|endoftext|>


<commit_before>// boolean_expression: a Rust crate for Boolean expressions and BDDs.
//
// Copyright (c) 2016 Chris Fallin <cfallin@c1f.net>. Released under the MIT
// License.
//

use std::cmp::Ord;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};

use simplify;

/// An `Expr` is a simple Boolean logic expression. It may contain terminals
/// (i.e., free variables), constants, and the following fundamental operations:
/// AND, OR, NOT.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    /// A terminal (free variable). This expression node represents a value that
    /// is not known until evaluation time.
    Terminal(T),
    /// A boolean constant: true or false.
    Const(bool),

    /// The logical complement of the contained expression argument.
    Not(Box<Expr<T>>),

    /// The logical AND of the two expression arguments.
    And(Box<Expr<T>>, Box<Expr<T>>),

    /// The logical OR of the two expression arguments.
    Or(Box<Expr<T>>, Box<Expr<T>>),
}

impl<T> Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    /// Returns `true` if this `Expr` is a terminal.
    pub fn is_terminal(&self) -> bool {
        match self {
            &Expr::Terminal(_) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is a constant.
    pub fn is_const(&self) -> bool {
        match self {
            &Expr::Const(_) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is a NOT node.
    pub fn is_not(&self) -> bool {
        match self {
            &Expr::Not(_) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is an AND node.
    pub fn is_and(&self) -> bool {
        match self {
            &Expr::And(_, _) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is an OR node.
    pub fn is_or(&self) -> bool {
        match self {
            &Expr::Or(_, _) => true,
            _ => false,
        }
    }

    /// Builds a NOT node around an argument, consuming the argument
    /// expression.
    pub fn not(e: Expr<T>) -> Expr<T> {
        Expr::Not(Box::new(e))
    }

    /// Builds an AND node around two arguments, consuming the argument
    /// expressions.
    pub fn and(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
        Expr::And(Box::new(e1), Box::new(e2))
    }

    /// Builds an OR node around two arguments, consuming the argument
    /// expressions.
    pub fn or(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
        Expr::Or(Box::new(e1), Box::new(e2))
    }

    pub fn xor(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
        let nand = !(e1.clone() & e2.clone());
        let or = e1 | e2;
        nand & or
    }

    /// Evaluates the expression with a particular set of terminal assignments.
    /// If any terminals are not assigned, they default to `false`.
    pub fn evaluate(&self, vals: &HashMap<T, bool>) -> bool {
        match self {
            &Expr::Terminal(ref t) => *vals.get(t).unwrap_or(&false),
            &Expr::Const(val) => val,
            &Expr::And(ref a, ref b) => a.evaluate(vals) && b.evaluate(vals),
            &Expr::Or(ref a, ref b) => a.evaluate(vals) || b.evaluate(vals),
            &Expr::Not(ref x) => !x.evaluate(vals),
        }
    }

    /// Simplify an expression in a relatively cheap way using well-known logic
    /// identities.
    ///
    /// This function performs certain reductions using DeMorgan's Law,
    /// distribution of ANDs over ORs, and certain identities involving
    /// constants, to simplify an expression. The result will always be in
    /// sum-of-products form: nodes will always appear in order (from
    /// expression tree root to leaves) `OR -> AND -> NOT`. In other words,
    /// `AND` nodes may only have `NOT` nodes (or terminals or constants) as
    /// children, and `NOT` nodes may only have terminals or constants as
    /// children.
    ///
    /// This function explicitly does *not* perform any sort of minterm reduction.
    /// The terms may thus be redundant (i.e., `And(a, b)` may appear twice), and
    /// combinable terms may exist (i.e., `And(a, b)` and `And(a, Not(b))` may
    /// appear in the `OR`'d list of terms, where these could be combined to
    /// simply `a` but are not). For example:
    ///
    /// ```
    /// use boolean_expression::Expr;
    ///
    /// // This simplifies using DeMorgan's Law:
    /// let expr = Expr::not(Expr::or(Expr::Terminal(0), Expr::Terminal(1)));
    /// let simplified = expr.simplify_via_laws();
    /// assert_eq!(simplified,
    ///     Expr::and(Expr::not(Expr::Terminal(0)),
    ///               Expr::not(Expr::Terminal(1))));
    ///
    /// // This doesn't simplify, though:
    /// let expr = Expr::or(
    ///             Expr::and(Expr::Terminal(0), Expr::Terminal(1)),
    ///             Expr::and(Expr::Terminal(0), Expr::not(Expr::Terminal(1))));
    /// let simplified = expr.clone().simplify_via_laws();
    /// assert_eq!(simplified, expr);
    /// ```
    ///
    /// For better (but more expensive) simplification, see `simplify_via_bdd()`.
    pub fn simplify_via_laws(self) -> Expr<T> {
        simplify::simplify_via_laws(self)
    }

    /// Simplify an expression via a roundtrip through a `BDD`. This procedure
    /// is more effective than `Expr::simplify_via_laws()`, but more expensive.
    /// This roundtrip will implicitly simplify an arbitrarily complicated
    /// function (by construction, as the BDD is built), and then find a
    /// quasi-minimal set of terms using cubelist-based reduction. For example:
    ///
    /// ```
    /// use boolean_expression::Expr;
    ///
    /// // `simplify_via_laws()` cannot combine these terms, but
    /// // `simplify_via_bdd()` will:
    /// let expr = Expr::or(
    ///             Expr::and(Expr::Terminal(0), Expr::Terminal(1)),
    ///             Expr::and(Expr::Terminal(0), Expr::not(Expr::Terminal(1))));
    /// let simplified = expr.simplify_via_bdd();
    /// assert_eq!(simplified, Expr::Terminal(0));
    /// ```
    pub fn simplify_via_bdd(self) -> Expr<T> {
        simplify::simplify_via_bdd(self)
    }

    /// Map terminal values using the specified mapping function.
    pub fn map<F, R>(&self, f: F) -> Expr<R>
    where
        F: Fn(&T) -> R,
        R: Clone + Debug + Eq + Hash,
    {
        self.map1(&f)
    }

    fn map1<F, R>(&self, f: &F) -> Expr<R>
    where
        F: Fn(&T) -> R,
        R: Clone + Debug + Eq + Hash,
    {
        match self {
            &Expr::Terminal(ref t) => Expr::Terminal(f(t)),
            &Expr::Const(val) => Expr::Const(val),
            &Expr::Not(ref n) => Expr::not(n.map1(f)),
            &Expr::And(ref a, ref b) => Expr::and(a.map1(f), b.map1(f)),
            &Expr::Or(ref a, ref b) => Expr::or(a.map1(f), b.map1(f)),
        }
    }
}

impl<T> Not for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn not(self) -> Self::Output {
        Self::not(self)
    }
}

impl<T> BitAnd<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn bitand(self, rhs: Expr<T>) -> Self::Output {
        Self::and(self, rhs)
    }
}

impl<T> BitAndAssign<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    fn bitand_assign(&mut self, rhs: Expr<T>) {
        *self = Self::and(self.clone(), rhs);
    }
}

impl<T> BitOr<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn bitor(self, rhs: Expr<T>) -> Self::Output {
        Self::or(self, rhs)
    }
}

impl<T> BitOrAssign<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    fn bitor_assign(&mut self, rhs: Expr<T>) {
        *self = Self::or(self.clone(), rhs);
    }
}

impl<T> BitXor<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn bitxor(self, rhs: Expr<T>) -> Self::Output {
        Self::xor(self, rhs)
    }
}

impl<T> BitXorAssign<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    fn bitxor_assign(&mut self, rhs: Expr<T>) {
        *self = Self::xor(self.clone(), rhs);
    }
}
<commit_msg>Add an example to Expr<commit_after>// boolean_expression: a Rust crate for Boolean expressions and BDDs.
//
// Copyright (c) 2016 Chris Fallin <cfallin@c1f.net>. Released under the MIT
// License.
//

use std::cmp::Ord;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};

use simplify;

/// An `Expr` is a simple Boolean logic expression. It may contain terminals
/// (i.e., free variables), constants, and the following fundamental operations:
/// AND, OR, NOT.
///
/// ```
/// use std::collections::HashMap;
/// use boolean_expression::Expr;
///
/// #[derive(Clone, Hash, Eq, PartialEq, Debug)]
/// enum RiverCrossing { Chicken, Fox, Grain }
///
/// let chicken = Expr::Terminal(RiverCrossing::Chicken);
/// let fox_and_grain = Expr::Terminal(RiverCrossing::Fox) & Expr::Terminal(RiverCrossing::Grain);
///
/// let allowed = (!chicken.clone() & fox_and_grain.clone()) | (chicken & !fox_and_grain);
/// let items = [
///    (RiverCrossing::Grain, true),
///    (RiverCrossing::Fox, true),
/// ].iter().cloned().collect();
///
/// // nobody gets eaten!
/// assert!(allowed.evaluate(&items));
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    /// A terminal (free variable). This expression node represents a value that
    /// is not known until evaluation time.
    Terminal(T),
    /// A boolean constant: true or false.
    Const(bool),

    /// The logical complement of the contained expression argument.
    Not(Box<Expr<T>>),

    /// The logical AND of the two expression arguments.
    And(Box<Expr<T>>, Box<Expr<T>>),

    /// The logical OR of the two expression arguments.
    Or(Box<Expr<T>>, Box<Expr<T>>),
}

impl<T> Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    /// Returns `true` if this `Expr` is a terminal.
    pub fn is_terminal(&self) -> bool {
        match self {
            &Expr::Terminal(_) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is a constant.
    pub fn is_const(&self) -> bool {
        match self {
            &Expr::Const(_) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is a NOT node.
    pub fn is_not(&self) -> bool {
        match self {
            &Expr::Not(_) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is an AND node.
    pub fn is_and(&self) -> bool {
        match self {
            &Expr::And(_, _) => true,
            _ => false,
        }
    }

    /// Returns `true` if this `Expr` is an OR node.
    pub fn is_or(&self) -> bool {
        match self {
            &Expr::Or(_, _) => true,
            _ => false,
        }
    }

    /// Builds a NOT node around an argument, consuming the argument
    /// expression.
    pub fn not(e: Expr<T>) -> Expr<T> {
        Expr::Not(Box::new(e))
    }

    /// Builds an AND node around two arguments, consuming the argument
    /// expressions.
    pub fn and(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
        Expr::And(Box::new(e1), Box::new(e2))
    }

    /// Builds an OR node around two arguments, consuming the argument
    /// expressions.
    pub fn or(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
        Expr::Or(Box::new(e1), Box::new(e2))
    }

    pub fn xor(e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
        let nand = !(e1.clone() & e2.clone());
        let or = e1 | e2;
        nand & or
    }

    /// Evaluates the expression with a particular set of terminal assignments.
    /// If any terminals are not assigned, they default to `false`.
    pub fn evaluate(&self, vals: &HashMap<T, bool>) -> bool {
        match self {
            &Expr::Terminal(ref t) => *vals.get(t).unwrap_or(&false),
            &Expr::Const(val) => val,
            &Expr::And(ref a, ref b) => a.evaluate(vals) && b.evaluate(vals),
            &Expr::Or(ref a, ref b) => a.evaluate(vals) || b.evaluate(vals),
            &Expr::Not(ref x) => !x.evaluate(vals),
        }
    }

    /// Simplify an expression in a relatively cheap way using well-known logic
    /// identities.
    ///
    /// This function performs certain reductions using DeMorgan's Law,
    /// distribution of ANDs over ORs, and certain identities involving
    /// constants, to simplify an expression. The result will always be in
    /// sum-of-products form: nodes will always appear in order (from
    /// expression tree root to leaves) `OR -> AND -> NOT`. In other words,
    /// `AND` nodes may only have `NOT` nodes (or terminals or constants) as
    /// children, and `NOT` nodes may only have terminals or constants as
    /// children.
    ///
    /// This function explicitly does *not* perform any sort of minterm reduction.
    /// The terms may thus be redundant (i.e., `And(a, b)` may appear twice), and
    /// combinable terms may exist (i.e., `And(a, b)` and `And(a, Not(b))` may
    /// appear in the `OR`'d list of terms, where these could be combined to
    /// simply `a` but are not). For example:
    ///
    /// ```
    /// use boolean_expression::Expr;
    ///
    /// // This simplifies using DeMorgan's Law:
    /// let expr = Expr::not(Expr::or(Expr::Terminal(0), Expr::Terminal(1)));
    /// let simplified = expr.simplify_via_laws();
    /// assert_eq!(simplified,
    ///     Expr::and(Expr::not(Expr::Terminal(0)),
    ///               Expr::not(Expr::Terminal(1))));
    ///
    /// // This doesn't simplify, though:
    /// let expr = Expr::or(
    ///             Expr::and(Expr::Terminal(0), Expr::Terminal(1)),
    ///             Expr::and(Expr::Terminal(0), Expr::not(Expr::Terminal(1))));
    /// let simplified = expr.clone().simplify_via_laws();
    /// assert_eq!(simplified, expr);
    /// ```
    ///
    /// For better (but more expensive) simplification, see `simplify_via_bdd()`.
    pub fn simplify_via_laws(self) -> Expr<T> {
        simplify::simplify_via_laws(self)
    }

    /// Simplify an expression via a roundtrip through a `BDD`. This procedure
    /// is more effective than `Expr::simplify_via_laws()`, but more expensive.
    /// This roundtrip will implicitly simplify an arbitrarily complicated
    /// function (by construction, as the BDD is built), and then find a
    /// quasi-minimal set of terms using cubelist-based reduction. For example:
    ///
    /// ```
    /// use boolean_expression::Expr;
    ///
    /// // `simplify_via_laws()` cannot combine these terms, but
    /// // `simplify_via_bdd()` will:
    /// let expr = Expr::or(
    ///             Expr::and(Expr::Terminal(0), Expr::Terminal(1)),
    ///             Expr::and(Expr::Terminal(0), Expr::not(Expr::Terminal(1))));
    /// let simplified = expr.simplify_via_bdd();
    /// assert_eq!(simplified, Expr::Terminal(0));
    /// ```
    pub fn simplify_via_bdd(self) -> Expr<T> {
        simplify::simplify_via_bdd(self)
    }

    /// Map terminal values using the specified mapping function.
    pub fn map<F, R>(&self, f: F) -> Expr<R>
    where
        F: Fn(&T) -> R,
        R: Clone + Debug + Eq + Hash,
    {
        self.map1(&f)
    }

    fn map1<F, R>(&self, f: &F) -> Expr<R>
    where
        F: Fn(&T) -> R,
        R: Clone + Debug + Eq + Hash,
    {
        match self {
            &Expr::Terminal(ref t) => Expr::Terminal(f(t)),
            &Expr::Const(val) => Expr::Const(val),
            &Expr::Not(ref n) => Expr::not(n.map1(f)),
            &Expr::And(ref a, ref b) => Expr::and(a.map1(f), b.map1(f)),
            &Expr::Or(ref a, ref b) => Expr::or(a.map1(f), b.map1(f)),
        }
    }
}

impl<T> Not for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn not(self) -> Self::Output {
        Self::not(self)
    }
}

impl<T> BitAnd<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn bitand(self, rhs: Expr<T>) -> Self::Output {
        Self::and(self, rhs)
    }
}

impl<T> BitAndAssign<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    fn bitand_assign(&mut self, rhs: Expr<T>) {
        *self = Self::and(self.clone(), rhs);
    }
}

impl<T> BitOr<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn bitor(self, rhs: Expr<T>) -> Self::Output {
        Self::or(self, rhs)
    }
}

impl<T> BitOrAssign<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    fn bitor_assign(&mut self, rhs: Expr<T>) {
        *self = Self::or(self.clone(), rhs);
    }
}

impl<T> BitXor<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    type Output = Self;

    fn bitxor(self, rhs: Expr<T>) -> Self::Output {
        Self::xor(self, rhs)
    }
}

impl<T> BitXorAssign<Expr<T>> for Expr<T>
where
    T: Clone + Debug + Eq + Hash,
{
    fn bitxor_assign(&mut self, rhs: Expr<T>) {
        *self = Self::xor(self.clone(), rhs);
    }
}
<|endoftext|>


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

// High-level interface to CSS selector matching.

use std::cell::Cell;
use std::comm;
use std::task;
use std::vec;
use extra::arc::{Arc, RWArc};

use css::node_style::StyledNode;
use layout::incremental;
use layout::util::LayoutDataAccess;

use script::dom::node::{AbstractNode, LayoutView};
use style::{TNode, Stylist, cascade};

pub trait MatchMethods {
    fn match_node(&self, stylist: &Stylist);
    fn match_subtree(&self, stylist: RWArc<Stylist>);

    fn cascade_node(&self, parent: Option<AbstractNode<LayoutView>>);
    fn cascade_subtree(&self, parent: Option<AbstractNode<LayoutView>>);
}

impl MatchMethods for AbstractNode<LayoutView> {
    fn match_node(&self, stylist: &Stylist) {
        let applicable_declarations = do self.with_imm_element |element| {
            let style_attribute = match element.style_attribute {
                None => None,
                Some(ref style_attribute) => Some(style_attribute)
            };
            stylist.get_applicable_declarations(self, style_attribute, None)
        };

        match *self.mutate_layout_data().ptr {
            Some(ref mut layout_data) => {
                layout_data.applicable_declarations = applicable_declarations
            }
            None => fail!("no layout data")
        }
    }
    fn match_subtree(&self, stylist: RWArc<Stylist>) {
        // FIXME(pcwalton): Racy. Parallel CSS selector matching is disabled.
        //let num_tasks = rt::default_sched_threads() * 2;
        let num_tasks = 1;
        let mut node_count = 0;
        let mut nodes_per_task = vec::from_elem(num_tasks, ~[]);

        for node in self.traverse_preorder() {
            if node.is_element() {
                nodes_per_task[node_count % num_tasks].push(node);
                node_count += 1;
            }
        }

        let (port, chan) = comm::stream();
        let chan = comm::SharedChan::new(chan);
        let mut num_spawned = 0;

        for nodes in nodes_per_task.move_iter() {
            if nodes.len() > 0 {
                let chan = chan.clone();
                let stylist = stylist.clone();
                do task::spawn_with((nodes, stylist)) |(nodes, stylist)| {
                    let nodes = Cell::new(nodes);
                    do stylist.read |stylist| {
                        for node in nodes.take().move_iter() {
                            node.match_node(stylist);
                        }
                    }
                    chan.send(());
                }
                num_spawned += 1;
            }
        }
        for _ in range(0, num_spawned) {
            port.recv();
        }
    }

    fn cascade_node(&self, parent: Option<AbstractNode<LayoutView>>) {
        let parent_style = match parent {
            Some(ref parent) => Some(parent.style()),
            None => None
        };

        let computed_values = unsafe {
            Arc::new(cascade(self.borrow_layout_data_unchecked()
                                 .as_ref()
                                 .unwrap()
                                 .applicable_declarations,
                             parent_style.map(|parent_style| parent_style.get())))
        };

        match *self.mutate_layout_data().ptr {
            None => fail!("no layout data"),
            Some(ref mut layout_data) => {
                let style = &mut layout_data.style;
                match *style {
                    None => (),
                    Some(ref previous_style) => {
                        layout_data.restyle_damage =
                            Some(incremental::compute_damage(previous_style.get(),
                                                             computed_values.get()).to_int())
                    }
                }
                *style = Some(computed_values)
            }
        }
    }

    fn cascade_subtree(&self, parent: Option<AbstractNode<LayoutView>>) {
        self.cascade_node(parent);

        for kid in self.children() {
            if kid.is_element() {
                kid.cascade_subtree(Some(*self));
            }
        }
    }
}
<commit_msg>Re-enable parallel selector matching.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// High-level interface to CSS selector matching.

use std::cell::Cell;
use std::comm;
use std::task;
use std::vec;
use std::rt;
use extra::arc::{Arc, RWArc};

use css::node_style::StyledNode;
use layout::incremental;
use layout::util::LayoutDataAccess;

use script::dom::node::{AbstractNode, LayoutView};
use style::{TNode, Stylist, cascade};

pub trait MatchMethods {
    fn match_node(&self, stylist: &Stylist);
    fn match_subtree(&self, stylist: RWArc<Stylist>);

    fn cascade_node(&self, parent: Option<AbstractNode<LayoutView>>);
    fn cascade_subtree(&self, parent: Option<AbstractNode<LayoutView>>);
}

impl MatchMethods for AbstractNode<LayoutView> {
    fn match_node(&self, stylist: &Stylist) {
        let applicable_declarations = do self.with_imm_element |element| {
            let style_attribute = match element.style_attribute {
                None => None,
                Some(ref style_attribute) => Some(style_attribute)
            };
            stylist.get_applicable_declarations(self, style_attribute, None)
        };

        match *self.mutate_layout_data().ptr {
            Some(ref mut layout_data) => {
                layout_data.applicable_declarations = applicable_declarations
            }
            None => fail!("no layout data")
        }
    }
    fn match_subtree(&self, stylist: RWArc<Stylist>) {
        let num_tasks = rt::default_sched_threads() * 2;
        let mut node_count = 0;
        let mut nodes_per_task = vec::from_elem(num_tasks, ~[]);

        for node in self.traverse_preorder() {
            if node.is_element() {
                nodes_per_task[node_count % num_tasks].push(node);
                node_count += 1;
            }
        }

        let (port, chan) = comm::stream();
        let chan = comm::SharedChan::new(chan);
        let mut num_spawned = 0;

        for nodes in nodes_per_task.move_iter() {
            if nodes.len() > 0 {
                let chan = chan.clone();
                let stylist = stylist.clone();
                do task::spawn_with((nodes, stylist)) |(nodes, stylist)| {
                    let nodes = Cell::new(nodes);
                    do stylist.read |stylist| {
                        for node in nodes.take().move_iter() {
                            node.match_node(stylist);
                        }
                    }
                    chan.send(());
                }
                num_spawned += 1;
            }
        }
        for _ in range(0, num_spawned) {
            port.recv();
        }
    }

    fn cascade_node(&self, parent: Option<AbstractNode<LayoutView>>) {
        let parent_style = match parent {
            Some(ref parent) => Some(parent.style()),
            None => None
        };

        let computed_values = unsafe {
            Arc::new(cascade(self.borrow_layout_data_unchecked()
                                 .as_ref()
                                 .unwrap()
                                 .applicable_declarations,
                             parent_style.map(|parent_style| parent_style.get())))
        };

        match *self.mutate_layout_data().ptr {
            None => fail!("no layout data"),
            Some(ref mut layout_data) => {
                let style = &mut layout_data.style;
                match *style {
                    None => (),
                    Some(ref previous_style) => {
                        layout_data.restyle_damage =
                            Some(incremental::compute_damage(previous_style.get(),
                                                             computed_values.get()).to_int())
                    }
                }
                *style = Some(computed_values)
            }
        }
    }

    fn cascade_subtree(&self, parent: Option<AbstractNode<LayoutView>>) {
        self.cascade_node(parent);

        for kid in self.children() {
            if kid.is_element() {
                kid.cascade_subtree(Some(*self));
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::cell::UnsafeCell;
use crate::mem::MaybeUninit;

pub struct Mutex {
    inner: UnsafeCell<libc::pthread_mutex_t>,
}

#[inline]
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
    m.inner.get()
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

#[allow(dead_code)] // sys isn't exported yet
impl Mutex {
    pub const fn new() -> Mutex {
        // Might be moved to a different address, so it is better to avoid
        // initialization of potentially opaque OS data before it landed.
        // Be very careful using this newly constructed `Mutex`, reentrant
        // locking is undefined behavior until `init` is called!
        Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
    }
    #[inline]
    pub unsafe fn init(&mut self) {
        // Issue #33770
        //
        // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
        // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
        // try to re-lock it from the same thread when you already hold a lock
        // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html).
        //
        // In practice, glibc takes advantage of this undefined behavior to
        // implement hardware lock elision, which uses hardware transactional
        // memory to avoid acquiring the lock.
        // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL
        // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521).
        // As a consequence, while a transaction is in
        // progress, the lock appears to be unlocked. This isn't a problem for
        // other threads since the transactional memory will abort if a conflict
        // is detected, however no abort is generated when re-locking from the
        // same thread.
        //
        // Since locking the same mutex twice will result in two aliasing &mut
        // references, we instead create the mutex with type
        // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to
        // re-lock it from the same thread, thus avoiding undefined behavior.
        let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
        let r = libc::pthread_mutexattr_init(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn lock(&self) {
        let r = libc::pthread_mutex_lock(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn unlock(&self) {
        let r = libc::pthread_mutex_unlock(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        libc::pthread_mutex_trylock(self.inner.get()) == 0
    }
    #[inline]
    #[cfg(not(target_os = "dragonfly"))]
    pub unsafe fn destroy(&self) {
        let r = libc::pthread_mutex_destroy(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    #[cfg(target_os = "dragonfly")]
    pub unsafe fn destroy(&self) {
        let r = libc::pthread_mutex_destroy(self.inner.get());
        // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
        // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
        // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
        // this behaviour no longer occurs.
        debug_assert!(r == 0 || r == libc::EINVAL);
    }
}

pub struct ReentrantMutex {
    inner: UnsafeCell<libc::pthread_mutex_t>,
}

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
    }

    pub unsafe fn init(&self) {
        let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
        let result = libc::pthread_mutexattr_init(attr.as_mut_ptr());
        debug_assert_eq!(result, 0);
        let result =
            libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE);
        debug_assert_eq!(result, 0);
        let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
        debug_assert_eq!(result, 0);
        let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
        debug_assert_eq!(result, 0);
    }

    pub unsafe fn lock(&self) {
        let result = libc::pthread_mutex_lock(self.inner.get());
        debug_assert_eq!(result, 0);
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        libc::pthread_mutex_trylock(self.inner.get()) == 0
    }

    pub unsafe fn unlock(&self) {
        let result = libc::pthread_mutex_unlock(self.inner.get());
        debug_assert_eq!(result, 0);
    }

    pub unsafe fn destroy(&self) {
        let result = libc::pthread_mutex_destroy(self.inner.get());
        debug_assert_eq!(result, 0);
    }
}
<commit_msg>edit Mutex comment<commit_after>use crate::cell::UnsafeCell;
use crate::mem::MaybeUninit;

pub struct Mutex {
    inner: UnsafeCell<libc::pthread_mutex_t>,
}

#[inline]
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
    m.inner.get()
}

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

#[allow(dead_code)] // sys isn't exported yet
impl Mutex {
    pub const fn new() -> Mutex {
        // Might be moved to a different address, so it is better to avoid
        // initialization of potentially opaque OS data before it landed.
        // Be very careful using this newly constructed `Mutex`, reentrant
        // locking is undefined behavior until `init` is called!
        Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
    }
    #[inline]
    pub unsafe fn init(&mut self) {
        // Issue #33770
        //
        // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
        // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
        // try to re-lock it from the same thread when you already hold a lock
        // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html).
        // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL
        // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that
        // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same
        // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in
        // a Mutex where re-locking is UB.
        //
        // In practice, glibc takes advantage of this undefined behavior to
        // implement hardware lock elision, which uses hardware transactional
        // memory to avoid acquiring the lock. While a transaction is in
        // progress, the lock appears to be unlocked. This isn't a problem for
        // other threads since the transactional memory will abort if a conflict
        // is detected, however no abort is generated when re-locking from the
        // same thread.
        //
        // Since locking the same mutex twice will result in two aliasing &mut
        // references, we instead create the mutex with type
        // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to
        // re-lock it from the same thread, thus avoiding undefined behavior.
        let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
        let r = libc::pthread_mutexattr_init(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn lock(&self) {
        let r = libc::pthread_mutex_lock(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn unlock(&self) {
        let r = libc::pthread_mutex_unlock(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        libc::pthread_mutex_trylock(self.inner.get()) == 0
    }
    #[inline]
    #[cfg(not(target_os = "dragonfly"))]
    pub unsafe fn destroy(&self) {
        let r = libc::pthread_mutex_destroy(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    #[cfg(target_os = "dragonfly")]
    pub unsafe fn destroy(&self) {
        let r = libc::pthread_mutex_destroy(self.inner.get());
        // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
        // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
        // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
        // this behaviour no longer occurs.
        debug_assert!(r == 0 || r == libc::EINVAL);
    }
}

pub struct ReentrantMutex {
    inner: UnsafeCell<libc::pthread_mutex_t>,
}

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub const unsafe fn uninitialized() -> ReentrantMutex {
        ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
    }

    pub unsafe fn init(&self) {
        let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
        let result = libc::pthread_mutexattr_init(attr.as_mut_ptr());
        debug_assert_eq!(result, 0);
        let result =
            libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE);
        debug_assert_eq!(result, 0);
        let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
        debug_assert_eq!(result, 0);
        let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
        debug_assert_eq!(result, 0);
    }

    pub unsafe fn lock(&self) {
        let result = libc::pthread_mutex_lock(self.inner.get());
        debug_assert_eq!(result, 0);
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        libc::pthread_mutex_trylock(self.inner.get()) == 0
    }

    pub unsafe fn unlock(&self) {
        let result = libc::pthread_mutex_unlock(self.inner.get());
        debug_assert_eq!(result, 0);
    }

    pub unsafe fn destroy(&self) {
        let result = libc::pthread_mutex_destroy(self.inner.get());
        debug_assert_eq!(result, 0);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::common::types::OptionalPoolAddressArgs;
use crate::common::utils::{create_dir_if_not_exist, current_dir, dir_default_to_current};
use crate::genesis::git::{LAYOUT_FILE, OPERATOR_FILE, OWNER_FILE};
use crate::{
    common::{
        types::{CliError, CliTypedResult, PromptOptions, RngArgs},
        utils::{check_if_file_exists, read_from_file, write_to_user_only_file},
    },
    genesis::git::{from_yaml, to_yaml, GitOptions},
    CliCommand,
};
use aptos_genesis::config::{Layout, OperatorConfiguration, OwnerConfiguration};
use aptos_genesis::keys::PublicIdentity;
use aptos_genesis::{config::HostAndPort, keys::generate_key_objects};
use async_trait::async_trait;
use clap::Parser;
use std::path::{Path, PathBuf};

const PRIVATE_KEYS_FILE: &str = "private-keys.yaml";
pub const PUBLIC_KEYS_FILE: &str = "public-keys.yaml";
const VALIDATOR_FILE: &str = "validator-identity.yaml";
const VFN_FILE: &str = "validator-full-node-identity.yaml";

/// Generate account key, consensus key, and network key for a validator
#[derive(Parser)]
pub struct GenerateKeys {
    /// Output directory for the key files
    #[clap(long, parse(from_os_str))]
    pub(crate) output_dir: Option<PathBuf>,

    #[clap(flatten)]
    pub(crate) pool_address_args: OptionalPoolAddressArgs,
    #[clap(flatten)]
    pub(crate) prompt_options: PromptOptions,
    #[clap(flatten)]
    pub rng_args: RngArgs,
}

#[async_trait]
impl CliCommand<Vec<PathBuf>> for GenerateKeys {
    fn command_name(&self) -> &'static str {
        "GenerateKeys"
    }

    async fn execute(self) -> CliTypedResult<Vec<PathBuf>> {
        let output_dir = dir_default_to_current(self.output_dir.clone())?;

        let private_keys_file = output_dir.join(PRIVATE_KEYS_FILE);
        let public_keys_file = output_dir.join(PUBLIC_KEYS_FILE);
        let validator_file = output_dir.join(VALIDATOR_FILE);
        let vfn_file = output_dir.join(VFN_FILE);
        check_if_file_exists(private_keys_file.as_path(), self.prompt_options)?;
        check_if_file_exists(public_keys_file.as_path(), self.prompt_options)?;
        check_if_file_exists(validator_file.as_path(), self.prompt_options)?;
        check_if_file_exists(vfn_file.as_path(), self.prompt_options)?;

        let mut key_generator = self.rng_args.key_generator()?;
        let (mut validator_blob, mut vfn_blob, private_identity, public_identity) =
            generate_key_objects(&mut key_generator)?;

        // Allow for the owner to be different than the operator
        if let Some(pool_address) = self.pool_address_args.pool_address {
            validator_blob.account_address = Some(pool_address);
            vfn_blob.account_address = Some(pool_address);
        }

        // Create the directory if it doesn't exist
        create_dir_if_not_exist(output_dir.as_path())?;

        write_to_user_only_file(
            private_keys_file.as_path(),
            PRIVATE_KEYS_FILE,
            to_yaml(&private_identity)?.as_bytes(),
        )?;
        write_to_user_only_file(
            public_keys_file.as_path(),
            PUBLIC_KEYS_FILE,
            to_yaml(&public_identity)?.as_bytes(),
        )?;
        write_to_user_only_file(
            validator_file.as_path(),
            VALIDATOR_FILE,
            to_yaml(&validator_blob)?.as_bytes(),
        )?;
        write_to_user_only_file(vfn_file.as_path(), VFN_FILE, to_yaml(&vfn_blob)?.as_bytes())?;
        Ok(vec![
            public_keys_file,
            private_keys_file,
            validator_file,
            vfn_file,
        ])
    }
}

/// Set validator configuration for a single validator in the git repository
#[derive(Parser)]
pub struct SetValidatorConfiguration {
    /// Name of validator
    #[clap(long)]
    pub(crate) username: String,

    /// Host and port pair for the validator e.g. 127.0.0.1:6180 or aptoslabs.com:6180
    #[clap(long)]
    pub(crate) validator_host: HostAndPort,

    /// Host and port pair for the fullnode e.g. 127.0.0.1:6180 or aptoslabs.com:6180
    #[clap(long)]
    pub(crate) full_node_host: Option<HostAndPort>,

    /// Stake amount for stake distribution
    #[clap(long, default_value_t = 1)]
    pub(crate) stake_amount: u64,

    /// Path to private identity generated from GenerateKeys
    #[clap(long, parse(from_os_str))]
    pub(crate) owner_public_identity_file: Option<PathBuf>,

    /// Path to operator public identity, defaults to owner identity
    #[clap(long, parse(from_os_str))]
    pub(crate) operator_public_identity_file: Option<PathBuf>,

    /// Path to voter public identity, defaults to owner identity
    #[clap(long, parse(from_os_str))]
    pub(crate) voter_public_identity_file: Option<PathBuf>,

    #[clap(flatten)]
    pub(crate) git_options: GitOptions,
}

#[async_trait]
impl CliCommand<()> for SetValidatorConfiguration {
    fn command_name(&self) -> &'static str {
        "SetValidatorConfiguration"
    }

    async fn execute(self) -> CliTypedResult<()> {
        // Load owner
        let owner_keys_file = if let Some(owner_keys_file) = self.owner_public_identity_file {
            owner_keys_file
        } else {
            current_dir()?.join(PUBLIC_KEYS_FILE)
        };
        let owner_identity = read_public_identity_file(owner_keys_file.as_path())?;

        // Load voter
        let voter_identity = if let Some(voter_keys_file) = self.voter_public_identity_file {
            read_public_identity_file(voter_keys_file.as_path())?
        } else {
            owner_identity.clone()
        };

        // Load operator
        let (operator_identity, operator_keys_file) =
            if let Some(operator_keys_file) = self.operator_public_identity_file {
                (
                    read_public_identity_file(operator_keys_file.as_path())?,
                    operator_keys_file,
                )
            } else {
                (owner_identity.clone(), owner_keys_file)
            };

        // Extract the possible optional fields
        let consensus_public_key =
            if let Some(consensus_public_key) = operator_identity.consensus_public_key {
                consensus_public_key
            } else {
                return Err(CliError::CommandArgumentError(format!(
                    "Failed to read consensus public key from public identity file {}",
                    operator_keys_file.display()
                )));
            };

        let validator_network_public_key = if let Some(validator_network_public_key) =
            operator_identity.validator_network_public_key
        {
            validator_network_public_key
        } else {
            return Err(CliError::CommandArgumentError(format!(
                "Failed to read validator network public key from public identity file {}",
                operator_keys_file.display()
            )));
        };

        let consensus_proof_of_possession = if let Some(consensus_proof_of_possession) =
            operator_identity.consensus_proof_of_possession
        {
            consensus_proof_of_possession
        } else {
            return Err(CliError::CommandArgumentError(format!(
                "Failed to read consensus proof of possession from public identity file {}",
                operator_keys_file.display()
            )));
        };

        // Only add the public key if there is a full node
        let full_node_network_public_key = if self.full_node_host.is_some() {
            operator_identity.validator_network_public_key
        } else {
            None
        };

        // Build operator configuration file
        let operator_config = OperatorConfiguration {
            operator_account_address: operator_identity.account_address,
            operator_account_public_key: operator_identity.account_public_key.clone(),
            consensus_public_key,
            consensus_proof_of_possession,
            validator_network_public_key,
            validator_host: self.validator_host,
            full_node_network_public_key,
            full_node_host: self.full_node_host,
        };

        let owner_config = OwnerConfiguration {
            owner_account_address: owner_identity.account_address,
            owner_account_public_key: owner_identity.account_public_key,
            voter_account_address: voter_identity.account_address,
            voter_account_public_key: voter_identity.account_public_key,
            operator_account_address: operator_identity.account_address,
            operator_account_public_key: operator_identity.account_public_key,
            stake_amount: self.stake_amount,
        };

        let directory = PathBuf::from(&self.username);
        let operator_file = directory.join(OPERATOR_FILE);
        let owner_file = directory.join(OWNER_FILE);

        let git_client = self.git_options.get_client()?;
        git_client.put(operator_file.as_path(), &operator_config)?;
        git_client.put(owner_file.as_path(), &owner_config)
    }
}

pub fn read_public_identity_file(public_identity_file: &Path) -> CliTypedResult<PublicIdentity> {
    let bytes = read_from_file(public_identity_file)?;
    from_yaml(&String::from_utf8(bytes).map_err(CliError::from)?)
}

/// Generate a Layout template file with empty values
///
/// This will generate a layout template file for genesis with some default values.  To start a
/// new chain, these defaults should be carefully thought through and chosen.
#[derive(Parser)]
pub struct GenerateLayoutTemplate {
    /// Path of the output layout template
    #[clap(long, parse(from_os_str), default_value = LAYOUT_FILE)]
    pub(crate) output_file: PathBuf,

    #[clap(flatten)]
    pub(crate) prompt_options: PromptOptions,
}

#[async_trait]
impl CliCommand<()> for GenerateLayoutTemplate {
    fn command_name(&self) -> &'static str {
        "GenerateLayoutTemplate"
    }

    async fn execute(self) -> CliTypedResult<()> {
        check_if_file_exists(self.output_file.as_path(), self.prompt_options)?;
        let layout = Layout::default();

        write_to_user_only_file(
            self.output_file.as_path(),
            &self.output_file.display().to_string(),
            to_yaml(&layout)?.as_bytes(),
        )
    }
}
<commit_msg>[aptos-genesis] Use validator full node identity for vfn<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::common::types::OptionalPoolAddressArgs;
use crate::common::utils::{create_dir_if_not_exist, current_dir, dir_default_to_current};
use crate::genesis::git::{LAYOUT_FILE, OPERATOR_FILE, OWNER_FILE};
use crate::{
    common::{
        types::{CliError, CliTypedResult, PromptOptions, RngArgs},
        utils::{check_if_file_exists, read_from_file, write_to_user_only_file},
    },
    genesis::git::{from_yaml, to_yaml, GitOptions},
    CliCommand,
};
use aptos_genesis::config::{Layout, OperatorConfiguration, OwnerConfiguration};
use aptos_genesis::keys::PublicIdentity;
use aptos_genesis::{config::HostAndPort, keys::generate_key_objects};
use async_trait::async_trait;
use clap::Parser;
use std::path::{Path, PathBuf};

const PRIVATE_KEYS_FILE: &str = "private-keys.yaml";
pub const PUBLIC_KEYS_FILE: &str = "public-keys.yaml";
const VALIDATOR_FILE: &str = "validator-identity.yaml";
const VFN_FILE: &str = "validator-full-node-identity.yaml";

/// Generate account key, consensus key, and network key for a validator
#[derive(Parser)]
pub struct GenerateKeys {
    /// Output directory for the key files
    #[clap(long, parse(from_os_str))]
    pub(crate) output_dir: Option<PathBuf>,

    #[clap(flatten)]
    pub(crate) pool_address_args: OptionalPoolAddressArgs,
    #[clap(flatten)]
    pub(crate) prompt_options: PromptOptions,
    #[clap(flatten)]
    pub rng_args: RngArgs,
}

#[async_trait]
impl CliCommand<Vec<PathBuf>> for GenerateKeys {
    fn command_name(&self) -> &'static str {
        "GenerateKeys"
    }

    async fn execute(self) -> CliTypedResult<Vec<PathBuf>> {
        let output_dir = dir_default_to_current(self.output_dir.clone())?;

        let private_keys_file = output_dir.join(PRIVATE_KEYS_FILE);
        let public_keys_file = output_dir.join(PUBLIC_KEYS_FILE);
        let validator_file = output_dir.join(VALIDATOR_FILE);
        let vfn_file = output_dir.join(VFN_FILE);
        check_if_file_exists(private_keys_file.as_path(), self.prompt_options)?;
        check_if_file_exists(public_keys_file.as_path(), self.prompt_options)?;
        check_if_file_exists(validator_file.as_path(), self.prompt_options)?;
        check_if_file_exists(vfn_file.as_path(), self.prompt_options)?;

        let mut key_generator = self.rng_args.key_generator()?;
        let (mut validator_blob, mut vfn_blob, private_identity, public_identity) =
            generate_key_objects(&mut key_generator)?;

        // Allow for the owner to be different than the operator
        if let Some(pool_address) = self.pool_address_args.pool_address {
            validator_blob.account_address = Some(pool_address);
            vfn_blob.account_address = Some(pool_address);
        }

        // Create the directory if it doesn't exist
        create_dir_if_not_exist(output_dir.as_path())?;

        write_to_user_only_file(
            private_keys_file.as_path(),
            PRIVATE_KEYS_FILE,
            to_yaml(&private_identity)?.as_bytes(),
        )?;
        write_to_user_only_file(
            public_keys_file.as_path(),
            PUBLIC_KEYS_FILE,
            to_yaml(&public_identity)?.as_bytes(),
        )?;
        write_to_user_only_file(
            validator_file.as_path(),
            VALIDATOR_FILE,
            to_yaml(&validator_blob)?.as_bytes(),
        )?;
        write_to_user_only_file(vfn_file.as_path(), VFN_FILE, to_yaml(&vfn_blob)?.as_bytes())?;
        Ok(vec![
            public_keys_file,
            private_keys_file,
            validator_file,
            vfn_file,
        ])
    }
}

/// Set validator configuration for a single validator in the git repository
#[derive(Parser)]
pub struct SetValidatorConfiguration {
    /// Name of validator
    #[clap(long)]
    pub(crate) username: String,

    /// Host and port pair for the validator e.g. 127.0.0.1:6180 or aptoslabs.com:6180
    #[clap(long)]
    pub(crate) validator_host: HostAndPort,

    /// Host and port pair for the fullnode e.g. 127.0.0.1:6180 or aptoslabs.com:6180
    #[clap(long)]
    pub(crate) full_node_host: Option<HostAndPort>,

    /// Stake amount for stake distribution
    #[clap(long, default_value_t = 1)]
    pub(crate) stake_amount: u64,

    /// Path to private identity generated from GenerateKeys
    #[clap(long, parse(from_os_str))]
    pub(crate) owner_public_identity_file: Option<PathBuf>,

    /// Path to operator public identity, defaults to owner identity
    #[clap(long, parse(from_os_str))]
    pub(crate) operator_public_identity_file: Option<PathBuf>,

    /// Path to voter public identity, defaults to owner identity
    #[clap(long, parse(from_os_str))]
    pub(crate) voter_public_identity_file: Option<PathBuf>,

    #[clap(flatten)]
    pub(crate) git_options: GitOptions,
}

#[async_trait]
impl CliCommand<()> for SetValidatorConfiguration {
    fn command_name(&self) -> &'static str {
        "SetValidatorConfiguration"
    }

    async fn execute(self) -> CliTypedResult<()> {
        // Load owner
        let owner_keys_file = if let Some(owner_keys_file) = self.owner_public_identity_file {
            owner_keys_file
        } else {
            current_dir()?.join(PUBLIC_KEYS_FILE)
        };
        let owner_identity = read_public_identity_file(owner_keys_file.as_path())?;

        // Load voter
        let voter_identity = if let Some(voter_keys_file) = self.voter_public_identity_file {
            read_public_identity_file(voter_keys_file.as_path())?
        } else {
            owner_identity.clone()
        };

        // Load operator
        let (operator_identity, operator_keys_file) =
            if let Some(operator_keys_file) = self.operator_public_identity_file {
                (
                    read_public_identity_file(operator_keys_file.as_path())?,
                    operator_keys_file,
                )
            } else {
                (owner_identity.clone(), owner_keys_file)
            };

        // Extract the possible optional fields
        let consensus_public_key =
            if let Some(consensus_public_key) = operator_identity.consensus_public_key {
                consensus_public_key
            } else {
                return Err(CliError::CommandArgumentError(format!(
                    "Failed to read consensus public key from public identity file {}",
                    operator_keys_file.display()
                )));
            };

        let validator_network_public_key = if let Some(validator_network_public_key) =
            operator_identity.validator_network_public_key
        {
            validator_network_public_key
        } else {
            return Err(CliError::CommandArgumentError(format!(
                "Failed to read validator network public key from public identity file {}",
                operator_keys_file.display()
            )));
        };

        let consensus_proof_of_possession = if let Some(consensus_proof_of_possession) =
            operator_identity.consensus_proof_of_possession
        {
            consensus_proof_of_possession
        } else {
            return Err(CliError::CommandArgumentError(format!(
                "Failed to read consensus proof of possession from public identity file {}",
                operator_keys_file.display()
            )));
        };

        // Only add the public key if there is a full node
        let full_node_network_public_key = if self.full_node_host.is_some() {
            operator_identity.full_node_network_public_key
        } else {
            None
        };

        // Build operator configuration file
        let operator_config = OperatorConfiguration {
            operator_account_address: operator_identity.account_address,
            operator_account_public_key: operator_identity.account_public_key.clone(),
            consensus_public_key,
            consensus_proof_of_possession,
            validator_network_public_key,
            validator_host: self.validator_host,
            full_node_network_public_key,
            full_node_host: self.full_node_host,
        };

        let owner_config = OwnerConfiguration {
            owner_account_address: owner_identity.account_address,
            owner_account_public_key: owner_identity.account_public_key,
            voter_account_address: voter_identity.account_address,
            voter_account_public_key: voter_identity.account_public_key,
            operator_account_address: operator_identity.account_address,
            operator_account_public_key: operator_identity.account_public_key,
            stake_amount: self.stake_amount,
        };

        let directory = PathBuf::from(&self.username);
        let operator_file = directory.join(OPERATOR_FILE);
        let owner_file = directory.join(OWNER_FILE);

        let git_client = self.git_options.get_client()?;
        git_client.put(operator_file.as_path(), &operator_config)?;
        git_client.put(owner_file.as_path(), &owner_config)
    }
}

pub fn read_public_identity_file(public_identity_file: &Path) -> CliTypedResult<PublicIdentity> {
    let bytes = read_from_file(public_identity_file)?;
    from_yaml(&String::from_utf8(bytes).map_err(CliError::from)?)
}

/// Generate a Layout template file with empty values
///
/// This will generate a layout template file for genesis with some default values.  To start a
/// new chain, these defaults should be carefully thought through and chosen.
#[derive(Parser)]
pub struct GenerateLayoutTemplate {
    /// Path of the output layout template
    #[clap(long, parse(from_os_str), default_value = LAYOUT_FILE)]
    pub(crate) output_file: PathBuf,

    #[clap(flatten)]
    pub(crate) prompt_options: PromptOptions,
}

#[async_trait]
impl CliCommand<()> for GenerateLayoutTemplate {
    fn command_name(&self) -> &'static str {
        "GenerateLayoutTemplate"
    }

    async fn execute(self) -> CliTypedResult<()> {
        check_if_file_exists(self.output_file.as_path(), self.prompt_options)?;
        let layout = Layout::default();

        write_to_user_only_file(
            self.output_file.as_path(),
            &self.output_file.display().to_string(),
            to_yaml(&layout)?.as_bytes(),
        )
    }
}
<|endoftext|>


<commit_before>//! This modules handles hygiene information.
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
use std::sync::Arc;

use base_db::CrateId;
use either::Either;
use mbe::Origin;
use syntax::{ast, AstNode};

use crate::{
    db::AstDatabase,
    name::{AsName, Name},
    ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
};

#[derive(Clone, Debug)]
pub struct Hygiene {
    frames: Option<Arc<HygieneFrames>>,
}

impl Hygiene {
    pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
        Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) }
    }

    pub fn new_unhygienic() -> Hygiene {
        Hygiene { frames: None }
    }

    // FIXME: this should just return name
    pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
        if let Some(frames) = &self.frames {
            if name_ref.text() == "$crate" {
                if let Some(krate) = frames.root_crate(&name_ref) {
                    return Either::Right(krate);
                }
            }
        }

        Either::Left(name_ref.as_name())
    }

    pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
        let frames = self.frames.as_ref()?;

        let mut token = path.syntax().first_token()?;
        let mut current = frames.0.first();

        while let Some((frame, data)) =
            current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
        {
            let (mapped, origin) = data;
            if origin == Origin::Def {
                return if frame.local_inner { frame.krate } else { None };
            }
            current = frames.get(frame.call_site?);
            token = mapped.value;
        }
        None
    }
}

#[derive(Clone, Debug, Copy)]
struct HygieneFrameId(usize);

#[derive(Clone, Debug, Default)]
struct HygieneFrames(Vec<HygieneFrame>);

#[derive(Clone, Debug)]
struct HygieneFrame {
    expansion: Option<ExpansionInfo>,

    // Indicate this is a local inner macro
    local_inner: bool,
    krate: Option<CrateId>,

    call_site: Option<HygieneFrameId>,
    def_site: Option<HygieneFrameId>,
}

impl HygieneFrames {
    fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self {
        let mut frames = HygieneFrames::default();
        frames.add(db, file_id);
        frames
    }

    fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<HygieneFrameId> {
        let (krate, local_inner) = match file_id.0 {
            HirFileIdRepr::FileId(_) => (None, false),
            HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
                MacroCallId::EagerMacro(_id) => (None, false),
                MacroCallId::LazyMacro(id) => {
                    let loc = db.lookup_intern_macro(id);
                    match loc.def.kind {
                        MacroDefKind::Declarative => (Some(loc.def.krate), loc.def.local_inner),
                        MacroDefKind::BuiltIn(_) => (Some(loc.def.krate), false),
                        MacroDefKind::BuiltInDerive(_) => (None, false),
                        MacroDefKind::BuiltInEager(_) => (None, false),
                        MacroDefKind::ProcMacro(_) => (None, false),
                    }
                }
            },
        };

        let expansion = file_id.expansion_info(db);
        let expansion = match expansion {
            None => {
                let idx = self.0.len();
                self.0.push(HygieneFrame {
                    expansion: None,
                    local_inner,
                    krate,
                    call_site: None,
                    def_site: None,
                });
                return Some(HygieneFrameId(idx));
            }
            Some(it) => it,
        };

        let def_site = expansion.def.clone();
        let call_site = expansion.arg.file_id;

        let idx = self.0.len();
        self.0.push(HygieneFrame {
            expansion: Some(expansion),
            local_inner,
            krate,
            call_site: None,
            def_site: None,
        });

        self.0[idx].call_site = self.add(db, call_site);
        self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id));

        Some(HygieneFrameId(idx))
    }

    fn get(&self, id: HygieneFrameId) -> Option<&HygieneFrame> {
        self.0.get(id.0)
    }

    fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> {
        let mut token = name_ref.syntax().first_token()?;
        let first = self.0.first()?;
        let mut result = first.krate;
        let mut current = Some(first);

        while let Some((frame, (mapped, origin))) =
            current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
        {
            result = frame.krate;

            let site = match origin {
                Origin::Def => frame.def_site,
                Origin::Call => frame.call_site,
            };

            let site = match site {
                None => break,
                Some(it) => it,
            };

            current = self.get(site);
            token = mapped.value;
        }

        result
    }
}
<commit_msg>Use arena instead of vec<commit_after>//! This modules handles hygiene information.
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
use std::sync::Arc;

use arena::{Arena, Idx};
use base_db::CrateId;
use either::Either;
use mbe::Origin;
use syntax::{ast, AstNode};

use crate::{
    db::AstDatabase,
    name::{AsName, Name},
    ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
};

#[derive(Clone, Debug)]
pub struct Hygiene {
    frames: Option<Arc<HygieneFrames>>,
}

impl Hygiene {
    pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
        Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) }
    }

    pub fn new_unhygienic() -> Hygiene {
        Hygiene { frames: None }
    }

    // FIXME: this should just return name
    pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
        if let Some(frames) = &self.frames {
            if name_ref.text() == "$crate" {
                if let Some(krate) = frames.root_crate(&name_ref) {
                    return Either::Right(krate);
                }
            }
        }

        Either::Left(name_ref.as_name())
    }

    pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
        let frames = self.frames.as_ref()?;

        let mut token = path.syntax().first_token()?;
        let mut current = frames.first();

        while let Some((frame, data)) =
            current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
        {
            let (mapped, origin) = data;
            if origin == Origin::Def {
                return if frame.local_inner { frame.krate } else { None };
            }
            current = Some(&frames.0[frame.call_site?]);
            token = mapped.value;
        }
        None
    }
}

#[derive(Default, Debug)]
struct HygieneFrames(Arena<HygieneFrame>);

#[derive(Clone, Debug)]
struct HygieneFrame {
    expansion: Option<ExpansionInfo>,

    // Indicate this is a local inner macro
    local_inner: bool,
    krate: Option<CrateId>,

    call_site: Option<Idx<HygieneFrame>>,
    def_site: Option<Idx<HygieneFrame>>,
}

impl HygieneFrames {
    fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self {
        let mut frames = HygieneFrames::default();
        frames.add(db, file_id);
        frames
    }

    fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<Idx<HygieneFrame>> {
        let (krate, local_inner) = match file_id.0 {
            HirFileIdRepr::FileId(_) => (None, false),
            HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
                MacroCallId::EagerMacro(_id) => (None, false),
                MacroCallId::LazyMacro(id) => {
                    let loc = db.lookup_intern_macro(id);
                    match loc.def.kind {
                        MacroDefKind::Declarative => (Some(loc.def.krate), loc.def.local_inner),
                        MacroDefKind::BuiltIn(_) => (Some(loc.def.krate), false),
                        MacroDefKind::BuiltInDerive(_) => (None, false),
                        MacroDefKind::BuiltInEager(_) => (None, false),
                        MacroDefKind::ProcMacro(_) => (None, false),
                    }
                }
            },
        };

        let expansion = file_id.expansion_info(db);
        let expansion = match expansion {
            None => {
                return Some(self.0.alloc(HygieneFrame {
                    expansion: None,
                    local_inner,
                    krate,
                    call_site: None,
                    def_site: None,
                }));
            }
            Some(it) => it,
        };

        let def_site = expansion.def.clone();
        let call_site = expansion.arg.file_id;
        let idx = self.0.alloc(HygieneFrame {
            expansion: Some(expansion),
            local_inner,
            krate,
            call_site: None,
            def_site: None,
        });

        self.0[idx].call_site = self.add(db, call_site);
        self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id));

        Some(idx)
    }

    fn first(&self) -> Option<&HygieneFrame> {
        self.0.iter().next().map(|it| it.1)
    }

    fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> {
        let mut token = name_ref.syntax().first_token()?;
        let first = self.first()?;
        let mut result = first.krate;
        let mut current = Some(first);

        while let Some((frame, (mapped, origin))) =
            current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
        {
            result = frame.krate;

            let site = match origin {
                Origin::Def => frame.def_site,
                Origin::Call => frame.call_site,
            };

            let site = match site {
                None => break,
                Some(it) => it,
            };

            current = Some(&self.0[site]);
            token = mapped.value;
        }

        result
    }
}
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::parse::token;

pub use expr::ExprBuilder;
pub use ident::ToIdent;
pub use name::ToName;
pub use path::PathBuilder;

//////////////////////////////////////////////////////////////////////////////

pub trait ToInternedString {
    fn to_interned_string(&self) -> token::InternedString;
}

impl ToInternedString for token::InternedString {
    fn to_interned_string(&self) -> token::InternedString {
        self.clone()
    }
}

impl<'a> ToInternedString for &'a str {
    fn to_interned_string(&self) -> token::InternedString {
        token::intern_and_get_ident(self)
    }
}

impl ToInternedString for ast::Ident {
    fn to_interned_string(&self) -> token::InternedString {
        token::get_ident(*self)
    }
}

impl ToInternedString for ast::Name {
    fn to_interned_string(&self) -> token::InternedString {
        token::get_name(*self)
    }
}

impl<'a, T> ToInternedString for &'a T where T: ToInternedString {
    fn to_interned_string(&self) -> token::InternedString {
        (**self).to_interned_string()
    }
}
<commit_msg>token::get_{ident,name} was removed from libsyntax<commit_after>use syntax::ast;
use syntax::parse::token;

pub use expr::ExprBuilder;
pub use ident::ToIdent;
pub use name::ToName;
pub use path::PathBuilder;

//////////////////////////////////////////////////////////////////////////////

pub trait ToInternedString {
    fn to_interned_string(&self) -> token::InternedString;
}

impl ToInternedString for token::InternedString {
    fn to_interned_string(&self) -> token::InternedString {
        self.clone()
    }
}

impl<'a> ToInternedString for &'a str {
    fn to_interned_string(&self) -> token::InternedString {
        token::intern_and_get_ident(self)
    }
}

impl ToInternedString for ast::Ident {
    fn to_interned_string(&self) -> token::InternedString {
        self.name.as_str()
    }
}

impl ToInternedString for ast::Name {
    fn to_interned_string(&self) -> token::InternedString {
        self.as_str()
    }
}

impl<'a, T> ToInternedString for &'a T where T: ToInternedString {
    fn to_interned_string(&self) -> token::InternedString {
        (**self).to_interned_string()
    }
}
<|endoftext|>