<commit_before>use crate::drawing_ctx::DrawingCtx;
use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use crate::property_bag::PropertyBag;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, Primitive};

/// The `feFlood` filter primitive.
pub struct FeFlood {
    base: Primitive,
}

impl Default for FeFlood {
    /// Constructs a new `Flood` with empty properties.
    #[inline]
    fn default() -> FeFlood {
        FeFlood {
            base: Primitive::new::<Self>(),
        }
    }
}

impl NodeTrait for FeFlood {
    impl_node_as_filter_effect!();

    #[inline]
    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)
    }
}

impl FilterEffect for FeFlood {
    fn render(
        &self,
        node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let bounds = self.base.get_bounds(ctx).into_irect(draw_ctx);

        let cascaded = CascadedValues::new_from_node(node);
        let values = cascaded.get();

        let color = match values.flood_color.0 {
            cssparser::Color::CurrentColor => values.color.0,
            cssparser::Color::RGBA(rgba) => rgba,
        };
        let opacity = values.flood_opacity.0;

        let surface = ctx.source_graphic().flood(
            bounds,
            color,
            opacity,
        )?;

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput { surface, bounds },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<commit_msg>flood: shorten code<commit_after>use crate::drawing_ctx::DrawingCtx;
use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use crate::property_bag::PropertyBag;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, Primitive};

/// The `feFlood` filter primitive.
pub struct FeFlood {
    base: Primitive,
}

impl Default for FeFlood {
    /// Constructs a new `Flood` with empty properties.
    #[inline]
    fn default() -> FeFlood {
        FeFlood {
            base: Primitive::new::<Self>(),
        }
    }
}

impl NodeTrait for FeFlood {
    impl_node_as_filter_effect!();

    #[inline]
    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)
    }
}

impl FilterEffect for FeFlood {
    fn render(
        &self,
        node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let bounds = self.base.get_bounds(ctx).into_irect(draw_ctx);

        let cascaded = CascadedValues::new_from_node(node);
        let values = cascaded.get();

        let color = match values.flood_color.0 {
            cssparser::Color::CurrentColor => values.color.0,
            cssparser::Color::RGBA(rgba) => rgba,
        };
        let opacity = values.flood_opacity.0;

        let surface = ctx.source_graphic().flood(bounds, color, opacity)?;

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput { surface, bounds },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<|endoftext|>


<commit_before>//! Data types for addresses to nodes in DICOM networks.
//!
//! This module provides the definitions for [`FullAeAddr`] and [`AeAddr`],
//! which enable consumers to couple a socket address with an expected
//! application entity (AE) title.
//!
//! The syntax is `«ae_title»@«network_address»:«port»`,
//! which works not only with IPv4 and IPv6 addresses,
//! but also with domain names.
use std::{
    net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
    str::FromStr,
};

use snafu::{ensure, AsErrorSource, ResultExt, Snafu};

/// A specification for a full address to the target SCP:
/// an application entity title, plus a generic  address,
/// typically a socket address.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{address}`,
/// where the socket address is parsed according to
/// the expectations of the parameter type `T`.
///
/// For the version of the struct without a mandatory AE title,
/// see [`AeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::FullAeAddr;
/// # use std::net::SocketAddr;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # // socket address can be a string
/// let addr: FullAeAddr<String> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), "SCP-STORAGE");
/// assert_eq!(addr.socket_addr(), "127.0.0.1:104");
/// # // or anything else which can be parsed into a socket address
/// let addr: FullAeAddr<SocketAddr> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), "SCP-STORAGE");
/// assert_eq!(addr.socket_addr(), &SocketAddr::from(([127, 0, 0, 1], 104)));
/// assert_eq!(&addr.to_string(), "SCP-STORAGE@127.0.0.1:104");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct FullAeAddr<T> {
    ae_title: String,
    socket_addr: T,
}

impl<T> FullAeAddr<T> {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion.
    pub fn ae_title(&self) -> &str {
        &self.ae_title
    }

    /// Retrieve the network address portion.
    pub fn socket_addr(&self) -> &T {
        &self.socket_addr
    }

    /// Convert the full address into its constituent parts.
    pub fn into_parts(self) -> (String, T) {
        (self.ae_title, self.socket_addr)
    }
}

impl<T> From<(String, T)> for FullAeAddr<T> {
    fn from((ae_title, socket_addr): (String, T)) -> Self {
        Self::new(ae_title, socket_addr)
    }
}

/// A error which occurred when parsing an AE address.
#[derive(Debug, Clone, Eq, PartialEq, Snafu)]
pub enum ParseAeAddressError<E>
where
    E: std::fmt::Debug + AsErrorSource,
{
    /// Missing `@` in full AE address
    MissingPart,

    /// Could not parse network socket address
    ParseSocketAddress { source: E },
}

impl<T> FromStr for FullAeAddr<T>
where
    T: FromStr,
    T::Err: std::fmt::Debug + AsErrorSource,
{
    type Err = ParseAeAddressError<<T as FromStr>::Err>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // !!! there should be a way to escape the `@`
        if let Some((ae_title, addr)) = s.split_once('@') {
            ensure!(!ae_title.is_empty(), MissingPartSnafu);
            Ok(FullAeAddr {
                ae_title: ae_title.to_string(),
                socket_addr: addr.parse().context(ParseSocketAddressSnafu)?,
            })
        } else {
            Err(ParseAeAddressError::MissingPart)
        }
    }
}

impl<T> ToSocketAddrs for FullAeAddr<T>
where
    T: ToSocketAddrs,
{
    type Iter = T::Iter;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl<T> std::fmt::Display for FullAeAddr<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.ae_title.replace("@", "\\@"))?;
        f.write_str("@")?;
        std::fmt::Display::fmt(&self.socket_addr, f)
    }
}

/// A specification for an address to the target SCP:
/// a generic network socket address
/// which may also include an application entity title.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{address}`,
/// where the socket address is parsed according to
/// the expectations of the parameter type `T`.
///
/// For the version of the struct in which the AE title part is mandatory,
/// see [`FullAeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::{AeAddr, FullAeAddr};
/// # use std::net::{SocketAddr, SocketAddrV4};
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let addr: AeAddr<SocketAddrV4> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), Some("SCP-STORAGE"));
/// assert_eq!(addr.socket_addr(), &SocketAddrV4::new([127, 0, 0, 1].into(), 104));
/// assert_eq!(&addr.to_string(), "SCP-STORAGE@127.0.0.1:104");
///
/// // AE title can be missing
/// let addr: AeAddr<String> = "192.168.1.99:1045".parse()?;
/// assert_eq!(addr.ae_title(), None);
/// // but can be provided later
/// let full_addr: FullAeAddr<_> = addr.with_ae_title("SCP-QUERY");
/// assert_eq!(full_addr.ae_title(), "SCP-QUERY");
/// assert_eq!(&full_addr.to_string(), "SCP-QUERY@192.168.1.99:1045");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct AeAddr<T> {
    ae_title: Option<String>,
    socket_addr: T,
}

impl<T> AeAddr<T> {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
        AeAddr {
            ae_title: Some(ae_title.into()),
            socket_addr,
        }
    }

    /// Create an address with a missing AE title.
    pub fn new_socket_addr(socket_addr: T) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion, if present.
    pub fn ae_title(&self) -> Option<&str> {
        self.ae_title.as_deref()
    }

    /// Retrieve the socket address portion.
    pub fn socket_addr(&self) -> &T {
        &self.socket_addr
    }

    /// Create a new address with the full application entity target,
    /// discarding any potentially existing AE title.
    pub fn with_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr: self.socket_addr,
        }
    }

    /// Create a new address with the full application entity target,
    /// using the given AE title if it is missing.
    pub fn with_default_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
        FullAeAddr {
            ae_title: self.ae_title.unwrap_or_else(|| ae_title.into()),
            socket_addr: self.socket_addr,
        }
    }

    /// Convert the address into its constituent parts.
    pub fn into_parts(self) -> (Option<String>, T) {
        (self.ae_title, self.socket_addr)
    }
}

/// This conversion provides a socket address without an AE title.
impl From<SocketAddr> for AeAddr<SocketAddr> {
    fn from(socket_addr: SocketAddr) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

/// This conversion provides an IPv4 socket address without an AE title.
impl From<SocketAddrV4> for AeAddr<SocketAddrV4> {
    fn from(socket_addr: SocketAddrV4) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

/// This conversion provides an IPv6 socket address without an AE title.
impl From<SocketAddrV6> for AeAddr<SocketAddrV6> {
    fn from(socket_addr: SocketAddrV6) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

impl<T> From<FullAeAddr<T>> for AeAddr<T> {
    fn from(full: FullAeAddr<T>) -> Self {
        AeAddr {
            ae_title: Some(full.ae_title),
            socket_addr: full.socket_addr,
        }
    }
}

impl<T> FromStr for AeAddr<T>
where
    T: FromStr,
{
    type Err = <T as FromStr>::Err;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // !!! there should be a way to escape the `@`
        if let Some((ae_title, address)) = s.split_once('@') {
            Ok(AeAddr {
                ae_title: Some(ae_title)
                    .filter(|s| !s.is_empty())
                    .map(|s| s.to_string()),
                socket_addr: address.parse()?,
            })
        } else {
            Ok(AeAddr {
                ae_title: None,
                socket_addr: s.parse()?,
            })
        }
    }
}

impl<T> ToSocketAddrs for AeAddr<T>
where
    T: ToSocketAddrs,
{
    type Iter = T::Iter;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl<T> std::fmt::Display for AeAddr<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let socket_addr = self.socket_addr.to_string();
        if let Some(ae_title) = &self.ae_title {
            f.write_str(&ae_title.replace("@", "\\@"))?;
            f.write_str("@")?;
        } else if socket_addr.contains("@") {
            // if formatted socket address contains a `@`,
            // we need to start the output with `@`
            // so that the start of the socket address
            // is not interpreted as an AE title
            f.write_str("@")?;
        }

        std::fmt::Display::fmt(&socket_addr, f)
    }
}

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

    #[test]
    fn ae_addr_parse() {
        // socket address can be a string
        let addr: FullAeAddr<String> = "SCP-STORAGE@127.0.0.1:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "SCP-STORAGE");
        assert_eq!(addr.socket_addr(), "127.0.0.1:104");

        // or anything else which can be parsed into a socket address
        let addr: FullAeAddr<SocketAddr> = "SCP_STORAGE@127.0.0.1:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "SCP_STORAGE");
        assert_eq!(addr.socket_addr(), &SocketAddr::from(([127, 0, 0, 1], 104)));
        assert_eq!(&addr.to_string(), "SCP_STORAGE@127.0.0.1:104");

        // IPv4 socket address
        let addr: FullAeAddr<SocketAddrV4> = "MAMMOSTORE@10.0.0.11:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "MAMMOSTORE");
        assert_eq!(
            addr.socket_addr(),
            &SocketAddrV4::new([10, 0, 0, 11].into(), 104)
        );
        assert_eq!(&addr.to_string(), "MAMMOSTORE@10.0.0.11:104");
    }

    /// test addresses without an AE title
    #[test]
    fn ae_addr_parse_no_ae() {
        // should fail
        let res = FullAeAddr::<String>::from_str("pacs.hospital.example.com:104");
        assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));
        // should also fail (AE title can't be empty)
        let res = FullAeAddr::<String>::from_str("@pacs.hospital.example.com:104");
        assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));

        // should return an ae addr with no AE title
        let addr: AeAddr<String> = "pacs.hospital.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), None);
        assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
        // should also return an ae addr with no AE title
        let addr: AeAddr<String> = "@pacs.hospital.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), None);
        assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
    }

    #[test]
    fn ae_addr_parse_weird_scenarios() {
        // can parse addresses with multiple @'s
        let addr: FullAeAddr<String> = "ABC@DICOM@pacs.archive.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "ABC");
        assert_eq!(addr.socket_addr(), "DICOM@pacs.archive.example.com:104");
        assert_eq!(&addr.to_string(), "ABC@DICOM@pacs.archive.example.com:104");
    }
}
<commit_msg>[ul] Add TryFrom impl for AeAddr<String><commit_after>//! Data types for addresses to nodes in DICOM networks.
//!
//! This module provides the definitions for [`FullAeAddr`] and [`AeAddr`],
//! which enable consumers to couple a socket address with an expected
//! application entity (AE) title.
//!
//! The syntax is `«ae_title»@«network_address»:«port»`,
//! which works not only with IPv4 and IPv6 addresses,
//! but also with domain names.
use std::{
    convert::TryFrom,
    net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
    str::FromStr,
};

use snafu::{ensure, AsErrorSource, ResultExt, Snafu};

/// A specification for a full address to the target SCP:
/// an application entity title, plus a generic  address,
/// typically a socket address.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{address}`,
/// where the socket address is parsed according to
/// the expectations of the parameter type `T`.
///
/// For the version of the struct without a mandatory AE title,
/// see [`AeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::FullAeAddr;
/// # use std::net::SocketAddr;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # // socket address can be a string
/// let addr: FullAeAddr<String> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), "SCP-STORAGE");
/// assert_eq!(addr.socket_addr(), "127.0.0.1:104");
/// # // or anything else which can be parsed into a socket address
/// let addr: FullAeAddr<SocketAddr> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), "SCP-STORAGE");
/// assert_eq!(addr.socket_addr(), &SocketAddr::from(([127, 0, 0, 1], 104)));
/// assert_eq!(&addr.to_string(), "SCP-STORAGE@127.0.0.1:104");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct FullAeAddr<T> {
    ae_title: String,
    socket_addr: T,
}

impl<T> FullAeAddr<T> {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion.
    pub fn ae_title(&self) -> &str {
        &self.ae_title
    }

    /// Retrieve the network address portion.
    pub fn socket_addr(&self) -> &T {
        &self.socket_addr
    }

    /// Convert the full address into its constituent parts.
    pub fn into_parts(self) -> (String, T) {
        (self.ae_title, self.socket_addr)
    }
}

impl<T> From<(String, T)> for FullAeAddr<T> {
    fn from((ae_title, socket_addr): (String, T)) -> Self {
        Self::new(ae_title, socket_addr)
    }
}

/// A error which occurred when parsing an AE address.
#[derive(Debug, Clone, Eq, PartialEq, Snafu)]
pub enum ParseAeAddressError<E>
where
    E: std::fmt::Debug + AsErrorSource,
{
    /// Missing `@` in full AE address
    MissingPart,

    /// Could not parse network socket address
    ParseSocketAddress { source: E },
}

impl<T> FromStr for FullAeAddr<T>
where
    T: FromStr,
    T::Err: std::fmt::Debug + AsErrorSource,
{
    type Err = ParseAeAddressError<<T as FromStr>::Err>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // !!! there should be a way to escape the `@`
        if let Some((ae_title, addr)) = s.split_once('@') {
            ensure!(!ae_title.is_empty(), MissingPartSnafu);
            Ok(FullAeAddr {
                ae_title: ae_title.to_string(),
                socket_addr: addr.parse().context(ParseSocketAddressSnafu)?,
            })
        } else {
            Err(ParseAeAddressError::MissingPart)
        }
    }
}

impl<T> ToSocketAddrs for FullAeAddr<T>
where
    T: ToSocketAddrs,
{
    type Iter = T::Iter;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl<T> std::fmt::Display for FullAeAddr<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.ae_title.replace("@", "\\@"))?;
        f.write_str("@")?;
        std::fmt::Display::fmt(&self.socket_addr, f)
    }
}

/// A specification for an address to the target SCP:
/// a generic network socket address
/// which may also include an application entity title.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{address}`,
/// where the socket address is parsed according to
/// the expectations of the parameter type `T`.
///
/// For the version of the struct in which the AE title part is mandatory,
/// see [`FullAeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::{AeAddr, FullAeAddr};
/// # use std::net::{SocketAddr, SocketAddrV4};
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let addr: AeAddr<SocketAddrV4> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), Some("SCP-STORAGE"));
/// assert_eq!(addr.socket_addr(), &SocketAddrV4::new([127, 0, 0, 1].into(), 104));
/// assert_eq!(&addr.to_string(), "SCP-STORAGE@127.0.0.1:104");
///
/// // AE title can be missing
/// let addr: AeAddr<String> = "192.168.1.99:1045".parse()?;
/// assert_eq!(addr.ae_title(), None);
/// // but can be provided later
/// let full_addr: FullAeAddr<_> = addr.with_ae_title("SCP-QUERY");
/// assert_eq!(full_addr.ae_title(), "SCP-QUERY");
/// assert_eq!(&full_addr.to_string(), "SCP-QUERY@192.168.1.99:1045");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct AeAddr<T> {
    ae_title: Option<String>,
    socket_addr: T,
}

impl<T> AeAddr<T> {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
        AeAddr {
            ae_title: Some(ae_title.into()),
            socket_addr,
        }
    }

    /// Create an address with a missing AE title.
    pub fn new_socket_addr(socket_addr: T) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion, if present.
    pub fn ae_title(&self) -> Option<&str> {
        self.ae_title.as_deref()
    }

    /// Retrieve the socket address portion.
    pub fn socket_addr(&self) -> &T {
        &self.socket_addr
    }

    /// Create a new address with the full application entity target,
    /// discarding any potentially existing AE title.
    pub fn with_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr: self.socket_addr,
        }
    }

    /// Create a new address with the full application entity target,
    /// using the given AE title if it is missing.
    pub fn with_default_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
        FullAeAddr {
            ae_title: self.ae_title.unwrap_or_else(|| ae_title.into()),
            socket_addr: self.socket_addr,
        }
    }

    /// Convert the address into its constituent parts.
    pub fn into_parts(self) -> (Option<String>, T) {
        (self.ae_title, self.socket_addr)
    }
}

/// This conversion provides a socket address without an AE title.
impl From<SocketAddr> for AeAddr<SocketAddr> {
    fn from(socket_addr: SocketAddr) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

/// This conversion provides an IPv4 socket address without an AE title.
impl From<SocketAddrV4> for AeAddr<SocketAddrV4> {
    fn from(socket_addr: SocketAddrV4) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

/// This conversion provides an IPv6 socket address without an AE title.
impl From<SocketAddrV6> for AeAddr<SocketAddrV6> {
    fn from(socket_addr: SocketAddrV6) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

impl<T> From<FullAeAddr<T>> for AeAddr<T> {
    fn from(full: FullAeAddr<T>) -> Self {
        AeAddr {
            ae_title: Some(full.ae_title),
            socket_addr: full.socket_addr,
        }
    }
}

impl<T> FromStr for AeAddr<T>
where
    T: FromStr,
{
    type Err = <T as FromStr>::Err;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // !!! there should be a way to escape the `@`
        if let Some((ae_title, address)) = s.split_once('@') {
            Ok(AeAddr {
                ae_title: Some(ae_title)
                    .filter(|s| !s.is_empty())
                    .map(|s| s.to_string()),
                socket_addr: address.parse()?,
            })
        } else {
            Ok(AeAddr {
                ae_title: None,
                socket_addr: s.parse()?,
            })
        }
    }
}

impl<'a> TryFrom<&'a str> for AeAddr<String> {
    type Error = <AeAddr<String> as FromStr>::Err;

    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
        s.parse()
    }
}

impl<T> ToSocketAddrs for AeAddr<T>
where
    T: ToSocketAddrs,
{
    type Iter = T::Iter;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl<T> std::fmt::Display for AeAddr<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let socket_addr = self.socket_addr.to_string();
        if let Some(ae_title) = &self.ae_title {
            f.write_str(&ae_title.replace("@", "\\@"))?;
            f.write_str("@")?;
        } else if socket_addr.contains("@") {
            // if formatted socket address contains a `@`,
            // we need to start the output with `@`
            // so that the start of the socket address
            // is not interpreted as an AE title
            f.write_str("@")?;
        }

        std::fmt::Display::fmt(&socket_addr, f)
    }
}

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

    #[test]
    fn ae_addr_parse() {
        // socket address can be a string
        let addr: FullAeAddr<String> = "SCP-STORAGE@127.0.0.1:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "SCP-STORAGE");
        assert_eq!(addr.socket_addr(), "127.0.0.1:104");

        // or anything else which can be parsed into a socket address
        let addr: FullAeAddr<SocketAddr> = "SCP_STORAGE@127.0.0.1:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "SCP_STORAGE");
        assert_eq!(addr.socket_addr(), &SocketAddr::from(([127, 0, 0, 1], 104)));
        assert_eq!(&addr.to_string(), "SCP_STORAGE@127.0.0.1:104");

        // IPv4 socket address
        let addr: FullAeAddr<SocketAddrV4> = "MAMMOSTORE@10.0.0.11:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "MAMMOSTORE");
        assert_eq!(
            addr.socket_addr(),
            &SocketAddrV4::new([10, 0, 0, 11].into(), 104)
        );
        assert_eq!(&addr.to_string(), "MAMMOSTORE@10.0.0.11:104");
    }

    /// test addresses without an AE title
    #[test]
    fn ae_addr_parse_no_ae() {
        // should fail
        let res = FullAeAddr::<String>::from_str("pacs.hospital.example.com:104");
        assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));
        // should also fail (AE title can't be empty)
        let res = FullAeAddr::<String>::from_str("@pacs.hospital.example.com:104");
        assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));

        // should return an ae addr with no AE title
        let addr: AeAddr<String> = "pacs.hospital.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), None);
        assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
        // should also return an ae addr with no AE title
        let addr: AeAddr<String> = "@pacs.hospital.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), None);
        assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
    }

    #[test]
    fn ae_addr_parse_weird_scenarios() {
        // can parse addresses with multiple @'s
        let addr: FullAeAddr<String> = "ABC@DICOM@pacs.archive.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "ABC");
        assert_eq!(addr.socket_addr(), "DICOM@pacs.archive.example.com:104");
        assert_eq!(&addr.to_string(), "ABC@DICOM@pacs.archive.example.com:104");
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 Inderjit Gill

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use std::collections::HashMap;

use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter, EnumString};

use crate::iname::Iname;
use crate::packable::{Mule, Packable};
use crate::result::Result;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, EnumString, Display, EnumIter)]
pub enum Keyword {
    #[strum(serialize = "UnreachableKeywordStart")]
    KeywordStart = 127,
    #[strum(serialize = "false")]
    False,
    #[strum(serialize = "true")]
    True,

    // mathematical special forms
    //
    #[strum(serialize = "+")]
    Plus,
    #[strum(serialize = "-")]
    Minus,
    #[strum(serialize = "*")]
    Mult,
    #[strum(serialize = "/")]
    Divide,
    #[strum(serialize = "=")]
    Equal,
    #[strum(serialize = ">")]
    Gt,
    #[strum(serialize = "<")]
    Lt,

    // built-in keywords/special-forms
    //
    #[strum(serialize = "++")]
    VectorAppend,
    #[strum(serialize = "sqrt")]
    Sqrt,
    #[strum(serialize = "mod")]
    Mod,
    #[strum(serialize = "and")]
    And,
    #[strum(serialize = "or")]
    Or,
    #[strum(serialize = "not")]
    Not,
    #[strum(serialize = "define")]
    Define,
    #[strum(serialize = "fn")]
    Fn,
    #[strum(serialize = "if")]
    If,
    #[strum(serialize = "each")]
    Each,
    #[strum(serialize = "loop")]
    Loop,
    #[strum(serialize = "fence")]
    Fence,
    #[strum(serialize = "on-matrix-stack")]
    OnMatrixStack,
    #[strum(serialize = "setq")]
    Setq,
    #[strum(serialize = "address-of")]
    AddressOf,
    #[strum(serialize = "fn-call")]
    FnCall,
    #[strum(serialize = "quote")]
    Quote,

    // pre-defined globals
    //
    #[strum(serialize = "#vars")]
    HashVars,
    #[strum(serialize = "canvas/width")]
    CanvasWidth,
    #[strum(serialize = "canvas/height")]
    CanvasHeight,
    #[strum(serialize = "math/PI")]
    MathPi,
    #[strum(serialize = "math/TAU")]
    MathTau,

    // colour formats
    //
    #[strum(serialize = "RGB")]
    Rgb,
    #[strum(serialize = "HSL")]
    Hsl,
    #[strum(serialize = "HSLuv")]
    Hsluv,
    #[strum(serialize = "LAB")]
    Lab,
    #[strum(serialize = "HSV")]
    Hsv,

    // pre-defined colours
    //
    #[strum(serialize = "white")]
    White,
    #[strum(serialize = "black")]
    Black,
    #[strum(serialize = "red")]
    Red,
    #[strum(serialize = "green")]
    Green,
    #[strum(serialize = "blue")]
    Blue,
    #[strum(serialize = "yellow")]
    Yellow,
    #[strum(serialize = "magenta")]
    Magenta,
    #[strum(serialize = "cyan")]
    Cyan,

    // procedural colours
    //
    #[strum(serialize = "chrome")]
    Chrome,
    #[strum(serialize = "hotline-miami")]
    HotlineMiami,
    #[strum(serialize = "knight-rider")]
    KnightRider,
    #[strum(serialize = "mars")]
    Mars,
    #[strum(serialize = "rainbow")]
    Rainbow,
    #[strum(serialize = "robocop")]
    Robocop,
    #[strum(serialize = "transformers")]
    Transformers,
    #[strum(serialize = "col/procedural-fn-presets")] // globally mapped in preamble
    ColProceduralFnPresets,

    // brush types
    //
    #[strum(serialize = "brush/flat")]
    BrushFlat,
    #[strum(serialize = "brush/a")]
    BrushA,
    #[strum(serialize = "brush/b")]
    BrushB,
    #[strum(serialize = "brush/c")]
    BrushC,
    #[strum(serialize = "brush/d")]
    BrushD,
    #[strum(serialize = "brush/e")]
    BrushE,
    #[strum(serialize = "brush/f")]
    BrushF,
    #[strum(serialize = "brush/g")]
    BrushG,
    #[strum(serialize = "brush/*")] // globally mapped in preamble
    BrushAll,

    // interpolation
    //
    #[strum(serialize = "linear")]
    Linear,
    #[strum(serialize = "ease/quick")]
    EaseQuick,
    #[strum(serialize = "ease/slow-in")]
    EaseSlowIn,
    #[strum(serialize = "ease/slow-in-out")]
    EaseSlowInOut,
    #[strum(serialize = "ease/quadratic-in")]
    EaseQuadraticIn,
    #[strum(serialize = "ease/quadratic-out")]
    EaseQuadraticOut,
    #[strum(serialize = "ease/quadratic-in-out")]
    EaseQuadraticInOut,
    #[strum(serialize = "ease/cubic-in")]
    EaseCubicIn,
    #[strum(serialize = "ease/cubic-out")]
    EaseCubicOut,
    #[strum(serialize = "ease/cubic-in-out")]
    EaseCubicInOut,
    #[strum(serialize = "ease/quartic-in")]
    EaseQuarticIn,
    #[strum(serialize = "ease/quartic-out")]
    EaseQuarticOut,
    #[strum(serialize = "ease/quartic-in-out")]
    EaseQuarticInOut,
    #[strum(serialize = "ease/quintic-in")]
    EaseQuinticIn,
    #[strum(serialize = "ease/quintic-out")]
    EaseQuinticOut,
    #[strum(serialize = "ease/quintic-in-out")]
    EaseQuinticInOut,
    #[strum(serialize = "ease/sin-in")]
    EaseSinIn,
    #[strum(serialize = "ease/sin-out")]
    EaseSinOut,
    #[strum(serialize = "ease/sin-in-out")]
    EaseSinInOut,
    #[strum(serialize = "ease/circular-in")]
    EaseCircularIn,
    #[strum(serialize = "ease/circular-out")]
    EaseCircularOut,
    #[strum(serialize = "ease/circular-in-out")]
    EaseCircularInOut,
    #[strum(serialize = "ease/exponential-in")]
    EaseExponentialIn,
    #[strum(serialize = "ease/exponential-out")]
    EaseExponentialOut,
    #[strum(serialize = "ease/exponential-in-out")]
    EaseExponentialInOut,
    #[strum(serialize = "ease/elastic-in")]
    EaseElasticIn,
    #[strum(serialize = "ease/elastic-out")]
    EaseElasticOut,
    #[strum(serialize = "ease/elastic-in-out")]
    EaseElasticInOut,
    #[strum(serialize = "ease/back-in")]
    EaseBackIn,
    #[strum(serialize = "ease/back-out")]
    EaseBackOut,
    #[strum(serialize = "ease/back-in-out")]
    EaseBackInOut,
    #[strum(serialize = "ease/bounce-in")]
    EaseBounceIn,
    #[strum(serialize = "ease/bounce-out")]
    EaseBounceOut,
    #[strum(serialize = "ease/bounce-in-out")]
    EaseBounceInOut,

    #[strum(serialize = "ease/*")] // globally mapped in preamble
    EaseAll,

    // common parameter labels
    //
    #[strum(serialize = "a")]
    A,
    #[strum(serialize = "b")]
    B,
    #[strum(serialize = "c")]
    C,
    #[strum(serialize = "d")]
    D,
    #[strum(serialize = "g")]
    G,
    #[strum(serialize = "h")]
    H,
    #[strum(serialize = "l")]
    L,
    #[strum(serialize = "n")]
    N,
    #[strum(serialize = "r")]
    R,
    #[strum(serialize = "s")]
    S,
    #[strum(serialize = "t")]
    T,
    #[strum(serialize = "v")]
    V,
    #[strum(serialize = "x")]
    X,
    #[strum(serialize = "y")]
    Y,
    #[strum(serialize = "z")]
    Z,
    #[strum(serialize = "alpha")]
    Alpha,
    #[strum(serialize = "amplitude")]
    Amplitude,
    #[strum(serialize = "angle")]
    Angle,
    #[strum(serialize = "angle-end")]
    AngleEnd,
    #[strum(serialize = "angle-start")]
    AngleStart,
    #[strum(serialize = "brush")]
    Brush,
    #[strum(serialize = "brush-subtype")]
    BrushSubtype,
    #[strum(serialize = "by")]
    By,
    #[strum(serialize = "clamping")]
    Clamping,
    #[strum(serialize = "colour")]
    Colour,
    #[strum(serialize = "colour-volatility")]
    ColourVolatility,
    #[strum(serialize = "colours")]
    Colours,
    #[strum(serialize = "coords")]
    Coords,
    #[strum(serialize = "copies")]
    Copies,
    #[strum(serialize = "copy")]
    Copy,
    #[strum(serialize = "direction")]
    Direction,
    #[strum(serialize = "distance")]
    Distance,
    #[strum(serialize = "format")]
    Format,
    #[strum(serialize = "frequency")]
    Frequency,
    #[strum(serialize = "from")]
    From,
    #[strum(serialize = "from-colour")]
    FromColour,
    #[strum(serialize = "height")]
    Height,
    #[strum(serialize = "inc")]
    Inc,
    #[strum(serialize = "inner-height")]
    InnerHeight,
    #[strum(serialize = "inner-width")]
    InnerWidth,
    #[strum(serialize = "iterations")]
    Iterations,
    #[strum(serialize = "line-width")]
    LineWidth,
    #[strum(serialize = "line-width-end")]
    LineWidthEnd,
    #[strum(serialize = "line-width-mapping")]
    LineWidthMapping,
    #[strum(serialize = "line-width-start")]
    LineWidthStart,
    #[strum(serialize = "mapping")]
    Mapping,
    #[strum(serialize = "max")]
    Max,
    #[strum(serialize = "min")]
    Min,
    #[strum(serialize = "num")]
    Num,
    #[strum(serialize = "overlap")]
    Overlap,
    #[strum(serialize = "point")]
    Point,
    #[strum(serialize = "position")]
    Position,
    #[strum(serialize = "preset")]
    Preset,
    #[strum(serialize = "radius")]
    Radius,
    #[strum(serialize = "scalar")]
    Scalar,
    #[strum(serialize = "scalars")]
    Scalars,
    #[strum(serialize = "seed")]
    Seed,
    #[strum(serialize = "shuffle-seed")]
    ShuffleSeed,
    #[strum(serialize = "steps")]
    Steps,
    #[strum(serialize = "stroke-line-width-end")]
    StrokeLineWidthEnd,
    #[strum(serialize = "stroke-line-width-start")]
    StrokeLineWidthStart,
    #[strum(serialize = "stroke-noise")]
    StrokeNoise,
    #[strum(serialize = "stroke-tessellation")]
    StrokeTessellation,
    #[strum(serialize = "t-end")]
    TEnd,
    #[strum(serialize = "t-start")]
    TStart,
    #[strum(serialize = "tessellation")]
    Tessellation,
    #[strum(serialize = "transform-position")]
    TransformPosition,
    #[strum(serialize = "to")]
    To,
    #[strum(serialize = "to-colour")]
    ToColour,
    #[strum(serialize = "upto")]
    Upto,
    #[strum(serialize = "value")]
    Value,
    #[strum(serialize = "vec1")]
    Vec1,
    #[strum(serialize = "vec2")]
    Vec2,
    #[strum(serialize = "vector")]
    Vector,
    #[strum(serialize = "volatility")]
    Volatility,
    #[strum(serialize = "width")]
    Width,
    #[strum(serialize = "worldspace")]
    WorldSpace,

    #[strum(serialize = "UnreachableKeywordEnd")]
    KeywordEnd,
}

impl Packable for Keyword {
    fn pack(&self, cursor: &mut String) -> Result<()> {
        Mule::pack_label(cursor, &self.to_string());

        Ok(())
    }

    fn unpack(cursor: &str) -> Result<(Self, &str)> {
        let ns = Mule::next_space(cursor);
        let sub = &cursor[0..ns];
        let res = sub.parse::<Keyword>()?;

        Ok((res, &cursor[ns..]))
    }
}

pub fn name_to_keyword_hash() -> HashMap<Iname, Keyword> {
    let mut hm: HashMap<Iname, Keyword> = HashMap::new();

    for kw in Keyword::iter() {
        hm.insert(Iname::from(kw), kw);
    }

    hm
}

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

    #[test]
    fn test_keyword_enums() {
        assert_eq!(Keyword::False as i32, 128);
        assert_eq!(Keyword::True as i32, 129);
        assert_eq!(Keyword::Width as i32, 298);
    }

    #[test]
    fn test_keyword_pack() {
        let mut res: String = "".to_string();
        Keyword::Volatility.pack(&mut res).unwrap();
        assert_eq!("volatility", res);
    }

    #[test]
    fn test_keyword_unpack() {
        let (res, _rem) = Keyword::unpack("volatility").unwrap();
        assert_eq!(res, Keyword::Volatility);
    }
}
<commit_msg>fixed failing keyword test<commit_after>// Copyright (C) 2019 Inderjit Gill

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use std::collections::HashMap;

use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter, EnumString};

use crate::iname::Iname;
use crate::packable::{Mule, Packable};
use crate::result::Result;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, EnumString, Display, EnumIter)]
pub enum Keyword {
    #[strum(serialize = "UnreachableKeywordStart")]
    KeywordStart = 127,
    #[strum(serialize = "false")]
    False,
    #[strum(serialize = "true")]
    True,

    // mathematical special forms
    //
    #[strum(serialize = "+")]
    Plus,
    #[strum(serialize = "-")]
    Minus,
    #[strum(serialize = "*")]
    Mult,
    #[strum(serialize = "/")]
    Divide,
    #[strum(serialize = "=")]
    Equal,
    #[strum(serialize = ">")]
    Gt,
    #[strum(serialize = "<")]
    Lt,

    // built-in keywords/special-forms
    //
    #[strum(serialize = "++")]
    VectorAppend,
    #[strum(serialize = "sqrt")]
    Sqrt,
    #[strum(serialize = "mod")]
    Mod,
    #[strum(serialize = "and")]
    And,
    #[strum(serialize = "or")]
    Or,
    #[strum(serialize = "not")]
    Not,
    #[strum(serialize = "define")]
    Define,
    #[strum(serialize = "fn")]
    Fn,
    #[strum(serialize = "if")]
    If,
    #[strum(serialize = "each")]
    Each,
    #[strum(serialize = "loop")]
    Loop,
    #[strum(serialize = "fence")]
    Fence,
    #[strum(serialize = "on-matrix-stack")]
    OnMatrixStack,
    #[strum(serialize = "setq")]
    Setq,
    #[strum(serialize = "address-of")]
    AddressOf,
    #[strum(serialize = "fn-call")]
    FnCall,
    #[strum(serialize = "quote")]
    Quote,

    // pre-defined globals
    //
    #[strum(serialize = "#vars")]
    HashVars,
    #[strum(serialize = "canvas/width")]
    CanvasWidth,
    #[strum(serialize = "canvas/height")]
    CanvasHeight,
    #[strum(serialize = "math/PI")]
    MathPi,
    #[strum(serialize = "math/TAU")]
    MathTau,

    // colour formats
    //
    #[strum(serialize = "RGB")]
    Rgb,
    #[strum(serialize = "HSL")]
    Hsl,
    #[strum(serialize = "HSLuv")]
    Hsluv,
    #[strum(serialize = "LAB")]
    Lab,
    #[strum(serialize = "HSV")]
    Hsv,

    // pre-defined colours
    //
    #[strum(serialize = "white")]
    White,
    #[strum(serialize = "black")]
    Black,
    #[strum(serialize = "red")]
    Red,
    #[strum(serialize = "green")]
    Green,
    #[strum(serialize = "blue")]
    Blue,
    #[strum(serialize = "yellow")]
    Yellow,
    #[strum(serialize = "magenta")]
    Magenta,
    #[strum(serialize = "cyan")]
    Cyan,

    // procedural colours
    //
    #[strum(serialize = "chrome")]
    Chrome,
    #[strum(serialize = "hotline-miami")]
    HotlineMiami,
    #[strum(serialize = "knight-rider")]
    KnightRider,
    #[strum(serialize = "mars")]
    Mars,
    #[strum(serialize = "rainbow")]
    Rainbow,
    #[strum(serialize = "robocop")]
    Robocop,
    #[strum(serialize = "transformers")]
    Transformers,
    #[strum(serialize = "col/procedural-fn-presets")] // globally mapped in preamble
    ColProceduralFnPresets,

    // brush types
    //
    #[strum(serialize = "brush/flat")]
    BrushFlat,
    #[strum(serialize = "brush/a")]
    BrushA,
    #[strum(serialize = "brush/b")]
    BrushB,
    #[strum(serialize = "brush/c")]
    BrushC,
    #[strum(serialize = "brush/d")]
    BrushD,
    #[strum(serialize = "brush/e")]
    BrushE,
    #[strum(serialize = "brush/f")]
    BrushF,
    #[strum(serialize = "brush/g")]
    BrushG,
    #[strum(serialize = "brush/*")] // globally mapped in preamble
    BrushAll,

    // interpolation
    //
    #[strum(serialize = "linear")]
    Linear,
    #[strum(serialize = "ease/quick")]
    EaseQuick,
    #[strum(serialize = "ease/slow-in")]
    EaseSlowIn,
    #[strum(serialize = "ease/slow-in-out")]
    EaseSlowInOut,
    #[strum(serialize = "ease/quadratic-in")]
    EaseQuadraticIn,
    #[strum(serialize = "ease/quadratic-out")]
    EaseQuadraticOut,
    #[strum(serialize = "ease/quadratic-in-out")]
    EaseQuadraticInOut,
    #[strum(serialize = "ease/cubic-in")]
    EaseCubicIn,
    #[strum(serialize = "ease/cubic-out")]
    EaseCubicOut,
    #[strum(serialize = "ease/cubic-in-out")]
    EaseCubicInOut,
    #[strum(serialize = "ease/quartic-in")]
    EaseQuarticIn,
    #[strum(serialize = "ease/quartic-out")]
    EaseQuarticOut,
    #[strum(serialize = "ease/quartic-in-out")]
    EaseQuarticInOut,
    #[strum(serialize = "ease/quintic-in")]
    EaseQuinticIn,
    #[strum(serialize = "ease/quintic-out")]
    EaseQuinticOut,
    #[strum(serialize = "ease/quintic-in-out")]
    EaseQuinticInOut,
    #[strum(serialize = "ease/sin-in")]
    EaseSinIn,
    #[strum(serialize = "ease/sin-out")]
    EaseSinOut,
    #[strum(serialize = "ease/sin-in-out")]
    EaseSinInOut,
    #[strum(serialize = "ease/circular-in")]
    EaseCircularIn,
    #[strum(serialize = "ease/circular-out")]
    EaseCircularOut,
    #[strum(serialize = "ease/circular-in-out")]
    EaseCircularInOut,
    #[strum(serialize = "ease/exponential-in")]
    EaseExponentialIn,
    #[strum(serialize = "ease/exponential-out")]
    EaseExponentialOut,
    #[strum(serialize = "ease/exponential-in-out")]
    EaseExponentialInOut,
    #[strum(serialize = "ease/elastic-in")]
    EaseElasticIn,
    #[strum(serialize = "ease/elastic-out")]
    EaseElasticOut,
    #[strum(serialize = "ease/elastic-in-out")]
    EaseElasticInOut,
    #[strum(serialize = "ease/back-in")]
    EaseBackIn,
    #[strum(serialize = "ease/back-out")]
    EaseBackOut,
    #[strum(serialize = "ease/back-in-out")]
    EaseBackInOut,
    #[strum(serialize = "ease/bounce-in")]
    EaseBounceIn,
    #[strum(serialize = "ease/bounce-out")]
    EaseBounceOut,
    #[strum(serialize = "ease/bounce-in-out")]
    EaseBounceInOut,

    #[strum(serialize = "ease/*")] // globally mapped in preamble
    EaseAll,

    // common parameter labels
    //
    #[strum(serialize = "a")]
    A,
    #[strum(serialize = "b")]
    B,
    #[strum(serialize = "c")]
    C,
    #[strum(serialize = "d")]
    D,
    #[strum(serialize = "g")]
    G,
    #[strum(serialize = "h")]
    H,
    #[strum(serialize = "l")]
    L,
    #[strum(serialize = "n")]
    N,
    #[strum(serialize = "r")]
    R,
    #[strum(serialize = "s")]
    S,
    #[strum(serialize = "t")]
    T,
    #[strum(serialize = "v")]
    V,
    #[strum(serialize = "x")]
    X,
    #[strum(serialize = "y")]
    Y,
    #[strum(serialize = "z")]
    Z,
    #[strum(serialize = "alpha")]
    Alpha,
    #[strum(serialize = "amplitude")]
    Amplitude,
    #[strum(serialize = "angle")]
    Angle,
    #[strum(serialize = "angle-end")]
    AngleEnd,
    #[strum(serialize = "angle-start")]
    AngleStart,
    #[strum(serialize = "brush")]
    Brush,
    #[strum(serialize = "brush-subtype")]
    BrushSubtype,
    #[strum(serialize = "by")]
    By,
    #[strum(serialize = "clamping")]
    Clamping,
    #[strum(serialize = "colour")]
    Colour,
    #[strum(serialize = "colour-volatility")]
    ColourVolatility,
    #[strum(serialize = "colours")]
    Colours,
    #[strum(serialize = "coords")]
    Coords,
    #[strum(serialize = "copies")]
    Copies,
    #[strum(serialize = "copy")]
    Copy,
    #[strum(serialize = "direction")]
    Direction,
    #[strum(serialize = "distance")]
    Distance,
    #[strum(serialize = "format")]
    Format,
    #[strum(serialize = "frequency")]
    Frequency,
    #[strum(serialize = "from")]
    From,
    #[strum(serialize = "from-colour")]
    FromColour,
    #[strum(serialize = "height")]
    Height,
    #[strum(serialize = "inc")]
    Inc,
    #[strum(serialize = "inner-height")]
    InnerHeight,
    #[strum(serialize = "inner-width")]
    InnerWidth,
    #[strum(serialize = "iterations")]
    Iterations,
    #[strum(serialize = "line-width")]
    LineWidth,
    #[strum(serialize = "line-width-end")]
    LineWidthEnd,
    #[strum(serialize = "line-width-mapping")]
    LineWidthMapping,
    #[strum(serialize = "line-width-start")]
    LineWidthStart,
    #[strum(serialize = "mapping")]
    Mapping,
    #[strum(serialize = "max")]
    Max,
    #[strum(serialize = "min")]
    Min,
    #[strum(serialize = "num")]
    Num,
    #[strum(serialize = "overlap")]
    Overlap,
    #[strum(serialize = "point")]
    Point,
    #[strum(serialize = "position")]
    Position,
    #[strum(serialize = "preset")]
    Preset,
    #[strum(serialize = "radius")]
    Radius,
    #[strum(serialize = "scalar")]
    Scalar,
    #[strum(serialize = "scalars")]
    Scalars,
    #[strum(serialize = "seed")]
    Seed,
    #[strum(serialize = "shuffle-seed")]
    ShuffleSeed,
    #[strum(serialize = "steps")]
    Steps,
    #[strum(serialize = "stroke-line-width-end")]
    StrokeLineWidthEnd,
    #[strum(serialize = "stroke-line-width-start")]
    StrokeLineWidthStart,
    #[strum(serialize = "stroke-noise")]
    StrokeNoise,
    #[strum(serialize = "stroke-tessellation")]
    StrokeTessellation,
    #[strum(serialize = "t-end")]
    TEnd,
    #[strum(serialize = "t-start")]
    TStart,
    #[strum(serialize = "tessellation")]
    Tessellation,
    #[strum(serialize = "transform-position")]
    TransformPosition,
    #[strum(serialize = "to")]
    To,
    #[strum(serialize = "to-colour")]
    ToColour,
    #[strum(serialize = "upto")]
    Upto,
    #[strum(serialize = "value")]
    Value,
    #[strum(serialize = "vec1")]
    Vec1,
    #[strum(serialize = "vec2")]
    Vec2,
    #[strum(serialize = "vector")]
    Vector,
    #[strum(serialize = "volatility")]
    Volatility,
    #[strum(serialize = "width")]
    Width,
    #[strum(serialize = "worldspace")]
    WorldSpace,

    #[strum(serialize = "UnreachableKeywordEnd")]
    KeywordEnd,
}

impl Packable for Keyword {
    fn pack(&self, cursor: &mut String) -> Result<()> {
        Mule::pack_label(cursor, &self.to_string());

        Ok(())
    }

    fn unpack(cursor: &str) -> Result<(Self, &str)> {
        let ns = Mule::next_space(cursor);
        let sub = &cursor[0..ns];
        let res = sub.parse::<Keyword>()?;

        Ok((res, &cursor[ns..]))
    }
}

pub fn name_to_keyword_hash() -> HashMap<Iname, Keyword> {
    let mut hm: HashMap<Iname, Keyword> = HashMap::new();

    for kw in Keyword::iter() {
        hm.insert(Iname::from(kw), kw);
    }

    hm
}

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

    #[test]
    fn test_keyword_enums() {
        assert_eq!(Keyword::False as i32, 128);
        assert_eq!(Keyword::True as i32, 129);
        assert_eq!(Keyword::Width as i32, 299);
    }

    #[test]
    fn test_keyword_pack() {
        let mut res: String = "".to_string();
        Keyword::Volatility.pack(&mut res).unwrap();
        assert_eq!("volatility", res);
    }

    #[test]
    fn test_keyword_unpack() {
        let (res, _rem) = Keyword::unpack("volatility").unwrap();
        assert_eq!(res, Keyword::Volatility);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

use kbdi::*;
use clap::{clap_app, crate_version};

fn main() {
    env_logger::init();
    let _guard = sentry::init(include_str!("../../dsn.txt"));

    let matches = clap_app!(kbdi =>
        (@setting SubcommandRequiredElseHelp)
        (version: crate_version!())
        (author: "Brendan Molloy <brendan@bbqsrc.net>")
        (about: "Configure Windows registry values for keyboards")
        (@subcommand keyboard_install =>
            (about: "Installs a keyboard layout to the registry")
            (@arg TAG: -t +takes_value +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
            (@arg LAYOUT: -n +takes_value +required "Layout name (eg: Skolt Sami (Norway))")
            (@arg GUID: -g +takes_value +required "Product code GUID (eg: {42c3de12-28...})")
            (@arg DLL: -d +takes_value +required "Name of keyboard DLL (eg: kbdfoo01.dll)")
            (@arg LANG: -l +takes_value "Native language name, if required (eg: Norsk)")
            (@arg enable: -e "Enable keyboard immediately after installing")
        )
        (@subcommand keyboard_uninstall =>
            (about: "Uninstalls a keyboard layout from the registry")
            (@arg GUID: +required "Product code GUID (eg: {42c3de12-28...})")
        )
        (@subcommand keyboard_enable =>
            (about: "Enables a keyboard for a user")
            (@arg TAG: -t +takes_value +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
            (@arg GUID: -g +takes_value +required "Product code GUID (eg: {42c3de12-28...})")
            (@arg LANG: -l +takes_value "Native language name, if required (eg: Norsk)")
        )
        (@subcommand registry_regen =>
            (about: "Regenerate the keyboard registry")
        )
        (@subcommand language_enable =>
            (about: "Enable a language with provided tag")
            (@arg TAG: +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
        )
        (@subcommand language_query =>
            (about: "Get data about language tag")
            (@arg TAG: +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
        )
        (@subcommand language_list =>
            (about: "Lists all languages enabled for the current user")
        )
        (@subcommand keyboard_list =>
            (about: "Lists all keyboards installed on the system")
        )
        (@subcommand keyboard_enabled =>
            (about: "Lists all enabled keyboards for the user")
        )
        (@subcommand clean =>
            (about: "Remove empty languages and invalid keyboards")
        )
    ).get_matches();

    match matches.subcommand() {
        ("keyboard_install", Some(matches)) => {
            let lang_name = matches.value_of("LANG");
            let layout_dll = matches.value_of("DLL").unwrap();
            let layout_name = matches.value_of("LAYOUT").unwrap();
            let tag = matches.value_of("TAG").unwrap();
            let guid = matches.value_of("GUID").unwrap();
            let wants_enable = matches.is_present("enable");

            // println!("Enabling language...");
            // enable_language(tag).unwrap();

            println!("Installing keyboard...");
            match keyboard::install(tag, layout_name, guid, layout_dll, lang_name) {
                Ok(_) => (),
                Err(err) => {
                    match err {
                        keyboard::Error::AlreadyExists => {
                            println!("Keyboard already installed.");
                        },
                        _ => panic!(err)
                    }
                }
            }
            if wants_enable {
                println!("Enabling keyboard...");
                keyboard::enable(tag, guid, lang_name).unwrap();
            }
        },
        ("keyboard_uninstall", Some(matches)) => {
            let guid = matches.value_of("GUID").unwrap();
            keyboard::uninstall(guid).unwrap();
        },
        ("keyboard_enable", Some(matches)) => {
            let lang_name = matches.value_of("LANG");
            let tag = matches.value_of("TAG").unwrap();
            let guid = matches.value_of("GUID").unwrap();

            keyboard::enable(tag, guid, lang_name).unwrap();
        },
        ("registry_regen", _) => {
            keyboard::regenerate_registry();
        },
        ("language_enable", Some(matches)) => {
            let tag = matches.value_of("TAG").unwrap();
            enable_language(tag).unwrap();
        },
        ("language_query", Some(matches)) => {
            let tag = matches.value_of("TAG").unwrap();
            println!("{}", query_language(tag));
        },
        ("language_list", _) => {
            let languages = enabled_languages().unwrap().join(" ");
            println!("{}", &languages);
        },
        ("keyboard_list", _) => {
            for k in keyboard::installed().iter() {
                println!("{}", k);
            }
        },
        ("keyboard_enabled", _) => {
            for k in enabled_keyboards().iter() {
                println!("{:?}", k);
            }
        },
        ("clean", _) => {
            clean().unwrap();
        },
        _ => {}
    };
}
<commit_msg>switch to env var for sentry<commit_after>#[macro_use]
extern crate log;

use kbdi::*;
use clap::{clap_app, crate_version};

fn main() {
    env_logger::init();

    let _guard = option_env!("SENTRY_DSN").map(|var| sentry::init(var));

    let matches = clap_app!(kbdi =>
        (@setting SubcommandRequiredElseHelp)
        (version: crate_version!())
        (author: "Brendan Molloy <brendan@bbqsrc.net>")
        (about: "Configure Windows registry values for keyboards")
        (@subcommand keyboard_install =>
            (about: "Installs a keyboard layout to the registry")
            (@arg TAG: -t +takes_value +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
            (@arg LAYOUT: -n +takes_value +required "Layout name (eg: Skolt Sami (Norway))")
            (@arg GUID: -g +takes_value +required "Product code GUID (eg: {42c3de12-28...})")
            (@arg DLL: -d +takes_value +required "Name of keyboard DLL (eg: kbdfoo01.dll)")
            (@arg LANG: -l +takes_value "Native language name, if required (eg: Norsk)")
            (@arg enable: -e "Enable keyboard immediately after installing")
        )
        (@subcommand keyboard_uninstall =>
            (about: "Uninstalls a keyboard layout from the registry")
            (@arg GUID: +required "Product code GUID (eg: {42c3de12-28...})")
        )
        (@subcommand keyboard_enable =>
            (about: "Enables a keyboard for a user")
            (@arg TAG: -t +takes_value +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
            (@arg GUID: -g +takes_value +required "Product code GUID (eg: {42c3de12-28...})")
            (@arg LANG: -l +takes_value "Native language name, if required (eg: Norsk)")
        )
        (@subcommand registry_regen =>
            (about: "Regenerate the keyboard registry")
        )
        (@subcommand language_enable =>
            (about: "Enable a language with provided tag")
            (@arg TAG: +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
        )
        (@subcommand language_query =>
            (about: "Get data about language tag")
            (@arg TAG: +required "Language tag in BCP 47 format (eg: sma-Latn-NO)")
        )
        (@subcommand language_list =>
            (about: "Lists all languages enabled for the current user")
        )
        (@subcommand keyboard_list =>
            (about: "Lists all keyboards installed on the system")
        )
        (@subcommand keyboard_enabled =>
            (about: "Lists all enabled keyboards for the user")
        )
        (@subcommand clean =>
            (about: "Remove empty languages and invalid keyboards")
        )
    ).get_matches();

    match matches.subcommand() {
        ("keyboard_install", Some(matches)) => {
            let lang_name = matches.value_of("LANG");
            let layout_dll = matches.value_of("DLL").unwrap();
            let layout_name = matches.value_of("LAYOUT").unwrap();
            let tag = matches.value_of("TAG").unwrap();
            let guid = matches.value_of("GUID").unwrap();
            let wants_enable = matches.is_present("enable");

            // println!("Enabling language...");
            // enable_language(tag).unwrap();

            println!("Installing keyboard...");
            match keyboard::install(tag, layout_name, guid, layout_dll, lang_name) {
                Ok(_) => (),
                Err(err) => {
                    match err {
                        keyboard::Error::AlreadyExists => {
                            println!("Keyboard already installed.");
                        },
                        _ => panic!(err)
                    }
                }
            }
            if wants_enable {
                println!("Enabling keyboard...");
                keyboard::enable(tag, guid, lang_name).unwrap();
            }
        },
        ("keyboard_uninstall", Some(matches)) => {
            let guid = matches.value_of("GUID").unwrap();
            keyboard::uninstall(guid).unwrap();
        },
        ("keyboard_enable", Some(matches)) => {
            let lang_name = matches.value_of("LANG");
            let tag = matches.value_of("TAG").unwrap();
            let guid = matches.value_of("GUID").unwrap();

            keyboard::enable(tag, guid, lang_name).unwrap();
        },
        ("registry_regen", _) => {
            keyboard::regenerate_registry();
        },
        ("language_enable", Some(matches)) => {
            let tag = matches.value_of("TAG").unwrap();
            enable_language(tag).unwrap();
        },
        ("language_query", Some(matches)) => {
            let tag = matches.value_of("TAG").unwrap();
            println!("{}", query_language(tag));
        },
        ("language_list", _) => {
            let languages = enabled_languages().unwrap().join(" ");
            println!("{}", &languages);
        },
        ("keyboard_list", _) => {
            for k in keyboard::installed().iter() {
                println!("{}", k);
            }
        },
        ("keyboard_enabled", _) => {
            for k in enabled_keyboards().iter() {
                println!("{:?}", k);
            }
        },
        ("clean", _) => {
            clean().unwrap();
        },
        _ => {}
    };
}
<|endoftext|>


<commit_before>use connection::Connection;
use libproto::blockchain::{Block, Status};
use libproto::router::{MsgType, RoutingKey, SubModules};
use libproto::{Message, OperateType, SyncRequest, SyncResponse};
use protobuf::RepeatedField;
use rand::{thread_rng, Rng, ThreadRng};
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::convert::{Into, TryFrom, TryInto};
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
use std::u8;
use Source;

const SYNC_STEP: u64 = 200;
const SYNC_TIME_OUT: u64 = 60;

/// Get messages and determine if need to synchronize or broadcast the current node status
pub struct Synchronizer {
    tx_pub: mpsc::Sender<(String, Vec<u8>)>,
    con: Arc<Connection>,
    current_status: Status,
    global_status: Status,
    sync_end_height: u64, //current_status <= sync_end_status
    is_synchronizing: bool,
    latest_status_lists: BTreeMap<u64, VecDeque<u32>>,
    block_lists: BTreeMap<u64, Block>,
    rand: ThreadRng,
    remote_sync_time_out: Instant,
    /// local sync error
    local_sync_count: u8,
}

unsafe impl Sync for Synchronizer {}
unsafe impl Send for Synchronizer {}

impl Synchronizer {
    pub fn new(tx_pub: mpsc::Sender<(String, Vec<u8>)>, con: Arc<Connection>) -> Self {
        Synchronizer {
            tx_pub: tx_pub,
            con: con,
            current_status: Status::new(),
            global_status: Status::new(),
            latest_status_lists: BTreeMap::new(),
            sync_end_height: 0,
            is_synchronizing: true,
            block_lists: BTreeMap::new(),
            rand: thread_rng(),
            remote_sync_time_out: (Instant::now() - Duration::from_secs(SYNC_TIME_OUT)),
            local_sync_count: 0,
        }
    }

    /// After receiving the `Chain >> Status`, it is processed as follows:
    /// 1. The chain height suddenly becomes lower than the original,
    ///    which means that the library is deleted, that is,
    ///    the synchronization is restarted from the received height.
    /// 2. The height of the chain is greater than or equal to the original height,
    ///    less than or equal to the height that the network has synchronized
    ///        - It is equal to the original height more than 2 times, indicating that
    ///          the data in the chain or executor is lost, and the block information is
    ///          sent again from the buffer. If no buffer exists, the data is requested again from other nodes.
    ///        - When the maximum height of data has been synchronized, a synchronization
    ///          request is sent to other nodes to confirm that the current node has successfully
    ///          completed the data synchronization work.
    /// 3. The height is greater than the maximum height that has been synchronized, ie it is the latest data itself.
    pub fn update_current_status(&mut self, latest_status: Status) {
        debug!(
            "sync: update_current_status: current height = {}, \
             before height = {}, \
             sync_end_height = {}",
            latest_status.get_height(),
            self.current_status.get_height(),
            self.sync_end_height
        );
        let old_height = self.current_status.get_height();
        let new_height = latest_status.get_height();

        if new_height == old_height && self.local_sync_count < u8::MAX {
            // Chain height does not increase
            self.local_sync_count += 1;
        }

        self.latest_status_lists = self
            .latest_status_lists
            .split_off(&(latest_status.get_height() + 1));
        self.current_status = latest_status;
        self.broadcast_status();
        self.prune_block_list_cache(new_height + 1);

        if new_height < old_height {
            // Chain error, may be a problem with the database, such as the database was deleted
            self.is_synchronizing = true;
            let start_height = new_height + 1;

            if self.block_lists.contains_key(&start_height) && !self.block_lists.is_empty() {
                self.submit_blocks();
            } else {
                self.start_sync_req(start_height, 0);
            }
        } else if new_height <= self.sync_end_height {
            // In synchronization, or loss of sync data, need to resend
            debug!(
                "Syncing: update_current_status: height = {}, block_lists len = {}",
                new_height,
                self.block_lists.len()
            );

            if !self.block_lists.is_empty() && new_height < self.sync_end_height {
                if self.local_sync_count >= 3 {
                    // Chain height does not increase, must loss data, send cache to executor and chain
                    self.submit_blocks();
                    self.local_sync_count = 0;
                }
            } else {
                // If the block height is equal to the maximum height that has already been synchronized,
                // perform the synchronization operation first to see if it is the latest in the chain
                self.start_sync_req(new_height + 1, 0);
            }

            self.remote_sync_time_out = Instant::now();
            self.is_synchronizing = true;
        } else {
            // Greater than the last sync height, expressed as the latest height
            self.sync_end_height = self.current_status.get_height();
            self.is_synchronizing = false;
        }
    }

    pub fn update_global_status(&mut self, status: &Status, origin: u32) {
        debug!(
            "sync: update_global_status: current height = {}, from node = {}, height = {}",
            self.current_status.get_height(),
            origin,
            status.get_height()
        );
        let current_height = self.current_status.get_height();
        let old_global_status = self.global_status.clone();
        if self.global_status.get_height() < status.get_height() {
            self.global_status = status.clone();
        }

        if status.get_height() < current_height + 1 {
            // The current node is the latest height and does not need to be synchronized
            self.is_synchronizing = false;
        } else if status.get_height() == current_height + 1 {
            // A node on the chain blocks out, synchronizing the latest block
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            } else if self.global_status.get_height() == old_global_status.get_height() {
                if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT {
                    self.remote_sync_time_out = Instant::now();
                    self.start_sync_req(current_height + 1, status.get_height());
                    self.is_synchronizing = true;
                }
            } else {
                self.is_synchronizing = false;
            }
        } else {
            // The node is far behind the data on the chain and initiates a synchronization request
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT
                || !self.is_synchronizing
            {
                self.remote_sync_time_out = Instant::now();
                self.start_sync_req(current_height + 1, status.get_height());
            } else if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            }

            self.is_synchronizing = true;
        }
    }

    pub fn is_synchronizing(&self) -> bool {
        self.is_synchronizing
    }

    pub fn process_sync(&mut self, mut blocks: SyncResponse) {
        let blocks = blocks.take_blocks();
        debug!("sync: process_sync: blocks len = {}", blocks.len());

        let mut heights = vec![];
        for block in blocks.into_iter() {
            heights.push(block.get_header().get_height());
            self.block_lists
                .insert(block.get_header().get_height(), block);
        }

        debug!("sync: process_sync: heights = {:?}", heights);
        self.submit_blocks();
    }

    pub fn receive(&mut self, _from: Source, payload: (String, Vec<u8>)) {
        let (key, body) = payload;
        let mut msg = Message::try_from(&body).unwrap();
        let origin = msg.get_origin();
        match RoutingKey::from(&key) {
            routing_key!(Chain >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_current_status(status);
                };
            }
            routing_key!(Synchronizer >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_global_status(&status, origin);
                };
            }
            routing_key!(Synchronizer >> SyncResponse) => {
                if let Some(blocks) = msg.take_sync_response() {
                    self.process_sync(blocks);
                };
            }
            _ => {
                error!("receive: unexpected data key = {:?}", key);
            }
        }
    }

    // Initiate a sync request
    fn start_sync_req(&mut self, start_height: u64, end_height: u64) {
        debug!(
            "sync: start_sync_req: start_height = {}, end_height = {},current height = {}",
            start_height,
            end_height,
            self.current_status.get_height()
        );
        let mut origin = 0;
        let mut end_height = end_height;
        let mut is_send = false;

        if let Some((height, origins)) = self
            .latest_status_lists
            .iter()
            .rfind(|&(_, origins)| origins.len() >= (2 / (3 * self.con.peers_pair.read().len())))
        {
            debug!(
                "sync: start_sync_req: height = {}, origins = {:?}",
                height, origins
            );
            if let Some(origins) = self.latest_status_lists.get(height) {
                if *height > self.current_status.get_height() {
                    origin = origins[self.rand.gen_range(0, origins.len())];
                    if end_height == 0 {
                        end_height = *height + 1;
                    }
                    is_send = true;
                }
            }
        }

        if is_send {
            self.sync_strategy(start_height, end_height, origin);
        }
    }

    fn sync_strategy(&self, start_height: u64, end_height: u64, origin: u32) {
        //current height = 155,start_height = 156, end height = 160, to origin = 1
        debug!(
            "sync: sync_strategy: current height = {}, \
             start_height = {}, \
             end height = {}, \
             to origin = {}",
            self.current_status.get_height(),
            start_height,
            end_height,
            origin
        );
        if start_height >= self.current_status.get_height() && start_height <= end_height {
            let mut start_height = start_height;
            let mut step_sum = SYNC_STEP;
            let mut heights = vec![];

            while start_height <= end_height {
                if step_sum == 0 {
                    step_sum = SYNC_STEP;
                    self.send_sync_req(heights, origin);
                    heights = vec![];
                }
                heights.push(start_height);

                step_sum -= 1;
                start_height += 1;
            }
            self.send_sync_req(heights, origin);
        }
    }

    fn send_sync_req(&self, heights: Vec<u64>, origin: u32) {
        if !heights.is_empty() {
            debug!(
                "sync: send_sync_req:current height = {}, \
                 heights = {:?}, \
                 origin {:?}, \
                 chain.sync: OperateType {:?}",
                self.current_status.get_height(),
                heights,
                origin,
                OperateType::Single
            );
            let mut sync_req = SyncRequest::new();
            sync_req.set_heights(heights);
            let msg = Message::init(OperateType::Single, origin, sync_req.into());
            self.con
                .broadcast(routing_key!(Synchronizer >> SyncRequest).into(), msg);
        }
    }

    fn broadcast_status(&mut self) {
        debug!(
            "sync: broadcast status {:?}, {:?} to other nodes",
            self.current_status.get_height(),
            self.current_status.get_hash()
        );
        let msg: Message = self.current_status.clone().into();
        self.con
            .broadcast(routing_key!(Synchronizer >> Status).into(), msg);
    }

    // Submit synchronization information
    fn submit_blocks(&mut self) {
        let mut height = self.current_status.get_height() + 1;
        debug!(
            "sync: submit_blocks:submit_height = {},\
             current height = {},\
             sync_end_height = {},\
             block_lists = {}",
            height,
            self.current_status.get_height(),
            self.sync_end_height,
            self.block_lists.len()
        );
        let mut blocks = vec![];
        let end_height = height + SYNC_STEP;

        loop {
            if height <= end_height {
                if let Some(block) = self.block_lists.get(&height) {
                    blocks.push(block.to_owned());
                } else {
                    break;
                }
            } else {
                break;
            }
            height += 1;
        }

        match blocks.last() {
            Some(block) => {
                if let Some(header) = block.header.as_ref() {
                    let height = header.get_height();

                    if height > self.sync_end_height {
                        self.sync_end_height = height;
                    }
                }
            }
            None => {}
        }

        if self.block_lists.contains_key(&::std::u64::MAX) {
            blocks.push(self.block_lists.remove(&::std::u64::MAX).unwrap());
        }

        self.pub_blocks(blocks);
    }

    fn pub_blocks(&mut self, blocks: Vec<Block>) {
        if !blocks.is_empty() {
            debug!(
                "sync: pub_blocks: current height = {}, \
                 sync_end_height = {}, \
                 len = {}, ",
                self.current_status.get_height(),
                self.sync_end_height,
                blocks.len()
            );
            let mut sync_res = SyncResponse::new();
            sync_res.set_blocks(RepeatedField::from_vec(blocks));
            let msg: Message = sync_res.into();
            self.tx_pub.send((
                routing_key!(Net >> SyncResponse).into(),
                msg.try_into().unwrap(),
            ));
        }
    }

    fn add_latest_sync_lists(&mut self, height: u64, origin: u32) {
        debug!(
            "sync: add_sync_lists: current height = {}, \
             from node = {}, \
             height = {}",
            self.current_status.get_height(),
            origin,
            height
        );
        if self
            .latest_status_lists
            .entry(height)
            .or_insert_with(VecDeque::new)
            .iter()
            .fold(HashSet::new(), |mut set, item| {
                set.insert(item);
                set
            })
            .insert(&origin)
        {
            self.latest_status_lists
                .entry(height)
                .or_insert_with(VecDeque::new)
                .push_back(origin);
        }
    }

    /// Prune block on btreemap
    fn prune_block_list_cache(&mut self, height: u64) {
        self.block_lists = self.block_lists.split_off(&height);
    }
}
<commit_msg>fix permit sync from target that less then 2/3 num's status info<commit_after>use connection::Connection;
use libproto::blockchain::{Block, Status};
use libproto::router::{MsgType, RoutingKey, SubModules};
use libproto::{Message, OperateType, SyncRequest, SyncResponse};
use protobuf::RepeatedField;
use rand::{thread_rng, Rng, ThreadRng};
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::convert::{Into, TryFrom, TryInto};
use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant};
use std::u8;
use Source;

const SYNC_STEP: u64 = 200;
const SYNC_TIME_OUT: u64 = 60;

/// Get messages and determine if need to synchronize or broadcast the current node status
pub struct Synchronizer {
    tx_pub: mpsc::Sender<(String, Vec<u8>)>,
    con: Arc<Connection>,
    current_status: Status,
    global_status: Status,
    sync_end_height: u64, //current_status <= sync_end_status
    is_synchronizing: bool,
    latest_status_lists: BTreeMap<u64, VecDeque<u32>>,
    block_lists: BTreeMap<u64, Block>,
    rand: ThreadRng,
    remote_sync_time_out: Instant,
    /// local sync error
    local_sync_count: u8,
}

unsafe impl Sync for Synchronizer {}
unsafe impl Send for Synchronizer {}

impl Synchronizer {
    pub fn new(tx_pub: mpsc::Sender<(String, Vec<u8>)>, con: Arc<Connection>) -> Self {
        Synchronizer {
            tx_pub: tx_pub,
            con: con,
            current_status: Status::new(),
            global_status: Status::new(),
            latest_status_lists: BTreeMap::new(),
            sync_end_height: 0,
            is_synchronizing: true,
            block_lists: BTreeMap::new(),
            rand: thread_rng(),
            remote_sync_time_out: (Instant::now() - Duration::from_secs(SYNC_TIME_OUT)),
            local_sync_count: 0,
        }
    }

    /// After receiving the `Chain >> Status`, it is processed as follows:
    /// 1. The chain height suddenly becomes lower than the original,
    ///    which means that the library is deleted, that is,
    ///    the synchronization is restarted from the received height.
    /// 2. The height of the chain is greater than or equal to the original height,
    ///    less than or equal to the height that the network has synchronized
    ///        - It is equal to the original height more than 2 times, indicating that
    ///          the data in the chain or executor is lost, and the block information is
    ///          sent again from the buffer. If no buffer exists, the data is requested again from other nodes.
    ///        - When the maximum height of data has been synchronized, a synchronization
    ///          request is sent to other nodes to confirm that the current node has successfully
    ///          completed the data synchronization work.
    /// 3. The height is greater than the maximum height that has been synchronized, ie it is the latest data itself.
    pub fn update_current_status(&mut self, latest_status: Status) {
        debug!(
            "sync: update_current_status: current height = {}, \
             before height = {}, \
             sync_end_height = {}",
            latest_status.get_height(),
            self.current_status.get_height(),
            self.sync_end_height
        );
        let old_height = self.current_status.get_height();
        let new_height = latest_status.get_height();

        if new_height == old_height && self.local_sync_count < u8::MAX {
            // Chain height does not increase
            self.local_sync_count += 1;
        }

        self.latest_status_lists = self
            .latest_status_lists
            .split_off(&(latest_status.get_height() + 1));
        self.current_status = latest_status;
        self.broadcast_status();
        self.prune_block_list_cache(new_height + 1);

        if new_height < old_height {
            // Chain error, may be a problem with the database, such as the database was deleted
            self.is_synchronizing = true;
            let start_height = new_height + 1;

            if self.block_lists.contains_key(&start_height) && !self.block_lists.is_empty() {
                self.submit_blocks();
            } else {
                self.start_sync_req(start_height, 0);
            }
        } else if new_height <= self.sync_end_height {
            // In synchronization, or loss of sync data, need to resend
            debug!(
                "Syncing: update_current_status: height = {}, block_lists len = {}",
                new_height,
                self.block_lists.len()
            );

            if !self.block_lists.is_empty() && new_height < self.sync_end_height {
                if self.local_sync_count >= 3 {
                    // Chain height does not increase, must loss data, send cache to executor and chain
                    self.submit_blocks();
                    self.local_sync_count = 0;
                }
            } else {
                // If the block height is equal to the maximum height that has already been synchronized,
                // perform the synchronization operation first to see if it is the latest in the chain
                self.start_sync_req(new_height + 1, 0);
            }

            self.remote_sync_time_out = Instant::now();
            self.is_synchronizing = true;
        } else {
            // Greater than the last sync height, expressed as the latest height
            self.sync_end_height = self.current_status.get_height();
            self.is_synchronizing = false;
        }
    }

    pub fn update_global_status(&mut self, status: &Status, origin: u32) {
        debug!(
            "sync: update_global_status: current height = {}, from node = {}, height = {}",
            self.current_status.get_height(),
            origin,
            status.get_height()
        );
        let current_height = self.current_status.get_height();
        let old_global_status = self.global_status.clone();
        if self.global_status.get_height() < status.get_height() {
            self.global_status = status.clone();
        }

        if status.get_height() < current_height + 1 {
            // The current node is the latest height and does not need to be synchronized
            self.is_synchronizing = false;
        } else if status.get_height() == current_height + 1 {
            // A node on the chain blocks out, synchronizing the latest block
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            } else if self.global_status.get_height() == old_global_status.get_height() {
                if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT {
                    self.remote_sync_time_out = Instant::now();
                    self.start_sync_req(current_height + 1, status.get_height());
                    self.is_synchronizing = true;
                }
            } else {
                self.is_synchronizing = false;
            }
        } else {
            // The node is far behind the data on the chain and initiates a synchronization request
            self.add_latest_sync_lists(status.get_height(), origin);

            if self.remote_sync_time_out.elapsed().as_secs() > SYNC_TIME_OUT
                || !self.is_synchronizing
            {
                self.remote_sync_time_out = Instant::now();
                self.start_sync_req(current_height + 1, status.get_height());
            } else if self.global_status.get_height() > old_global_status.get_height()
                && self.is_synchronizing
            {
                self.start_sync_req(status.get_height(), status.get_height());
            }

            self.is_synchronizing = true;
        }
    }

    pub fn is_synchronizing(&self) -> bool {
        self.is_synchronizing
    }

    pub fn process_sync(&mut self, mut blocks: SyncResponse) {
        let blocks = blocks.take_blocks();
        debug!("sync: process_sync: blocks len = {}", blocks.len());

        let mut heights = vec![];
        for block in blocks.into_iter() {
            heights.push(block.get_header().get_height());
            self.block_lists
                .insert(block.get_header().get_height(), block);
        }

        debug!("sync: process_sync: heights = {:?}", heights);
        self.submit_blocks();
    }

    pub fn receive(&mut self, _from: Source, payload: (String, Vec<u8>)) {
        let (key, body) = payload;
        let mut msg = Message::try_from(&body).unwrap();
        let origin = msg.get_origin();
        match RoutingKey::from(&key) {
            routing_key!(Chain >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_current_status(status);
                };
            }
            routing_key!(Synchronizer >> Status) => {
                if let Some(status) = msg.take_status() {
                    self.update_global_status(&status, origin);
                };
            }
            routing_key!(Synchronizer >> SyncResponse) => {
                if let Some(blocks) = msg.take_sync_response() {
                    self.process_sync(blocks);
                };
            }
            _ => {
                error!("receive: unexpected data key = {:?}", key);
            }
        }
    }

    // Initiate a sync request
    fn start_sync_req(&mut self, start_height: u64, end_height: u64) {
        debug!(
            "sync: start_sync_req: start_height = {}, end_height = {},current height = {}",
            start_height,
            end_height,
            self.current_status.get_height()
        );
        let mut origin = 0;
        let mut end_height = end_height;
        let mut is_send = false;

        if let Some((height, origins)) = self
            .latest_status_lists
            .iter()
            .rfind(|&(_, origins)| origins.len() > 0)
        {
            debug!(
                "sync: start_sync_req: height = {}, origins = {:?}",
                height, origins
            );
            if let Some(origins) = self.latest_status_lists.get(height) {
                if *height > self.current_status.get_height() {
                    origin = origins[self.rand.gen_range(0, origins.len())];
                    if end_height == 0 {
                        end_height = *height + 1;
                    }
                    is_send = true;
                }
            }
        }

        if is_send {
            self.sync_strategy(start_height, end_height, origin);
        }
    }

    fn sync_strategy(&self, start_height: u64, end_height: u64, origin: u32) {
        //current height = 155,start_height = 156, end height = 160, to origin = 1
        debug!(
            "sync: sync_strategy: current height = {}, \
             start_height = {}, \
             end height = {}, \
             to origin = {}",
            self.current_status.get_height(),
            start_height,
            end_height,
            origin
        );
        if start_height >= self.current_status.get_height() && start_height <= end_height {
            let mut start_height = start_height;
            let mut step_sum = SYNC_STEP;
            let mut heights = vec![];

            while start_height <= end_height {
                if step_sum == 0 {
                    step_sum = SYNC_STEP;
                    self.send_sync_req(heights, origin);
                    heights = vec![];
                }
                heights.push(start_height);

                step_sum -= 1;
                start_height += 1;
            }
            self.send_sync_req(heights, origin);
        }
    }

    fn send_sync_req(&self, heights: Vec<u64>, origin: u32) {
        if !heights.is_empty() {
            debug!(
                "sync: send_sync_req:current height = {}, \
                 heights = {:?}, \
                 origin {:?}, \
                 chain.sync: OperateType {:?}",
                self.current_status.get_height(),
                heights,
                origin,
                OperateType::Single
            );
            let mut sync_req = SyncRequest::new();
            sync_req.set_heights(heights);
            let msg = Message::init(OperateType::Single, origin, sync_req.into());
            self.con
                .broadcast(routing_key!(Synchronizer >> SyncRequest).into(), msg);
        }
    }

    fn broadcast_status(&mut self) {
        debug!(
            "sync: broadcast status {:?}, {:?} to other nodes",
            self.current_status.get_height(),
            self.current_status.get_hash()
        );
        let msg: Message = self.current_status.clone().into();
        self.con
            .broadcast(routing_key!(Synchronizer >> Status).into(), msg);
    }

    // Submit synchronization information
    fn submit_blocks(&mut self) {
        let mut height = self.current_status.get_height() + 1;
        debug!(
            "sync: submit_blocks:submit_height = {},\
             current height = {},\
             sync_end_height = {},\
             block_lists = {}",
            height,
            self.current_status.get_height(),
            self.sync_end_height,
            self.block_lists.len()
        );
        let mut blocks = vec![];
        let end_height = height + SYNC_STEP;

        loop {
            if height <= end_height {
                if let Some(block) = self.block_lists.get(&height) {
                    blocks.push(block.to_owned());
                } else {
                    break;
                }
            } else {
                break;
            }
            height += 1;
        }

        match blocks.last() {
            Some(block) => {
                if let Some(header) = block.header.as_ref() {
                    let height = header.get_height();

                    if height > self.sync_end_height {
                        self.sync_end_height = height;
                    }
                }
            }
            None => {}
        }

        if self.block_lists.contains_key(&::std::u64::MAX) {
            blocks.push(self.block_lists.remove(&::std::u64::MAX).unwrap());
        }

        self.pub_blocks(blocks);
    }

    fn pub_blocks(&mut self, blocks: Vec<Block>) {
        if !blocks.is_empty() {
            debug!(
                "sync: pub_blocks: current height = {}, \
                 sync_end_height = {}, \
                 len = {}, ",
                self.current_status.get_height(),
                self.sync_end_height,
                blocks.len()
            );
            let mut sync_res = SyncResponse::new();
            sync_res.set_blocks(RepeatedField::from_vec(blocks));
            let msg: Message = sync_res.into();
            self.tx_pub.send((
                routing_key!(Net >> SyncResponse).into(),
                msg.try_into().unwrap(),
            ));
        }
    }

    fn add_latest_sync_lists(&mut self, height: u64, origin: u32) {
        debug!(
            "sync: add_sync_lists: current height = {}, \
             from node = {}, \
             height = {}",
            self.current_status.get_height(),
            origin,
            height
        );
        if self
            .latest_status_lists
            .entry(height)
            .or_insert_with(VecDeque::new)
            .iter()
            .fold(HashSet::new(), |mut set, item| {
                set.insert(item);
                set
            })
            .insert(&origin)
        {
            self.latest_status_lists
                .entry(height)
                .or_insert_with(VecDeque::new)
                .push_back(origin);
        }
    }

    /// Prune block on btreemap
    fn prune_block_list_cache(&mut self, height: u64) {
        self.block_lists = self.block_lists.split_off(&height);
    }
}
<|endoftext|>


<commit_before>use parser::HttpParser;

#[derive(Copy)]
pub enum CallbackDecision {
    Nothing,
    SkipBody,
}

pub type CallbackResult = Result<CallbackDecision, String>;

pub trait HttpParserCallback {
    #[allow(unused_variables)]
    fn on_message_begin(&mut self, parser : &mut HttpParser) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_url(&mut self, parser : &mut HttpParser, data : &[u8],) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_status(&mut self, parser : &mut HttpParser, data : &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_header_field(&mut self, parser : &mut HttpParser, data : &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_header_value(&mut self, parser : &mut HttpParser, data : &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_headers_complete(&mut self, parser : &mut HttpParser) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_body(&mut self, parser : &mut HttpParser, data : &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_message_complete(&mut self, parser : &mut HttpParser) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }
}
<commit_msg>adjust format in callback<commit_after>use parser::HttpParser;

#[derive(Copy)]
pub enum CallbackDecision {
    Nothing,
    SkipBody,
}

pub type CallbackResult = Result<CallbackDecision, String>;

pub trait HttpParserCallback {
    #[allow(unused_variables)]
    fn on_message_begin(&mut self, parser: &mut HttpParser) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_url(&mut self, parser: &mut HttpParser, data: &[u8],) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_status(&mut self, parser: &mut HttpParser, data: &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_header_field(&mut self, parser: &mut HttpParser, data: &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_header_value(&mut self, parser: &mut HttpParser, data: &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_headers_complete(&mut self, parser: &mut HttpParser) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_body(&mut self, parser: &mut HttpParser, data: &[u8]) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }

    #[allow(unused_variables)]
    fn on_message_complete(&mut self, parser: &mut HttpParser) -> CallbackResult {
        Ok(CallbackDecision::Nothing)
    }
}
<|endoftext|>


<commit_before><commit_msg>Allow trailing plus on type parameter with default<commit_after><|endoftext|>