<commit_before>//! Features and intervals for gene annotations.

use std::collections::HashMap;

use bio::io::Strand;

use self::error::FeatureError;

pub mod error {

    use std::error::Error;
    use std::fmt;

    #[derive(Debug)]
    pub enum FeatureError {
        InvalidCoords,
    }

    impl Error for FeatureError {
        fn description(&self) -> &str {
            match *self {
                FeatureError::InvalidCoords => "interval start coordinate larger than its end coordinate"
            }
        }

        fn cause(&self) -> Option<&Error> {
            None
        }
    }

    impl fmt::Display for FeatureError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "FeatureError: {}", self.description())
        }
    }
}

/// Interface for features with start and end coordinates.
///
/// The intervals represented here are zero-based, half open intervals.
/// Coordinates start from zero and start coordinates must be less than
/// or equal to the end coordinates.
pub trait Interval: Sized {

    /// Start coordinate of the interval.
    fn start(&self) -> u64;

    /// End coordinate of the interval.
    fn end(&self) -> u64;

    /// Name of the interval.
    fn name(&self) -> Option<&str>;

    /// Name setter that returns the implementor itself.
    ///
    /// This function is expected to mutate the implementing type.
    fn with_name<T: Into<String>>(self, name: T) -> Self;

    /// Coordinate setter that returns the implementor itself.
    ///
    /// This function is expected to mutate the implementing type.
    fn with_coords(self, start: u64, end: u64) -> Self;

    /// Performs various validation of the interval.
    fn validate(self) -> Result<Self, FeatureError> {
        self.validate_coords()
    }

    /// The number of bases covered by the interval.
    fn span(&self) -> u64 {
        self.end() - self.start()
    }

    /// Whether two intervals have an intersection or not.
    fn overlaps(&self, other: &Self) -> bool {
        (other.start() <= self.start() && self.start() <= other.end()) ||
        (other.start() <= self.end() && self.end() <= other.end())
    }

    /// Whether one interval completely contains the other.
    fn envelops(&self, other: &Self) -> bool {
        self.start() <= other.start() && self.end() >= other.end()
    }

    /// Whether two intervals cover a contiguous region without any overlaps.
    fn adjacent(&self, other: &Self) -> bool {
        self.end() == other.start() || self.start() == other.end()
    }

    /// Performs validation of the interval coordinates.
    ///
    /// If the validation fails, an error message is returned within
    /// the `Result` type.
    fn validate_coords(self) -> Result<Self, FeatureError> {
        if self.start() > self.end() {
            return Err(FeatureError::InvalidCoords)
        }

        Ok(self)
    }
}

/// Macro for default function implementations of interval types.
macro_rules! impl_interval {
    ($struct_ty:ty) => (

        impl Interval for $struct_ty {

            /// Name of the interval.
            fn name(&self) -> Option<&str> {
                self.name.as_ref().map(|n| n.as_str())
            }

            /// Start coordinate of the interval.
            fn start(&self) -> u64 {
                self.start
            }

            /// End coordinate of the interval.
            fn end(&self) -> u64 {
                self.end
            }

            fn with_name<T>(mut self, name: T) -> $struct_ty
                where T: Into<String>
            {
                self.name = Some(name.into());
                self
            }

            fn with_coords(mut self, start: u64, end: u64) -> $struct_ty {
                self.start = start;
                self.end = end;
                self
            }
        }

    );
}

/// Default implementation of the `Interval` trait.
///
/// This struct also provides static methods for creating exons, transcripts, and genes.
#[derive(Debug, Default)]
pub struct Feature {
    start: u64,
    end: u64,
    name: Option<String>,
}

impl Feature {

    /// Creates a gene interval with default values.
    ///
    /// A gene interval is a container for transcript intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// let gene = Feature::gene();
    ///
    /// assert_eq!(gene.transcript().len(), 0);
    /// assert_eq!(gene.start(), 0);
    /// assert_eq!(gene.end(), 0);
    /// assert_eq!(gene.name(), None);
    /// ```
    pub fn gene() -> Gene {
        Gene::default()
    }

    /// Creates a transcript interval with default values.
    ///
    /// A transcript interval is a container for exon intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// use bio::io::Strand;
    ///
    /// let transcript = Feature::transcript();
    ///
    /// assert_eq!(transcript.exons().len(), 0);
    /// assert_eq!(transcript.strand(), &Strand::Unknown)
    /// assert_eq!(transcript.start(), 0);
    /// assert_eq!(transcript.end(), 0);
    /// assert_eq!(transcript.name(), None);
    /// ```
    pub fn transcript() -> Transcript {
        Transcript::default()
    }

    /// Creates an exon interval with default values.
    ///
    /// # Examples
    ///
    /// ```
    /// let exon = Feature::exon();
    ///
    /// assert_eq!(exon.start(), 0);
    /// assert_eq!(exon.end(), 0);
    /// assert_eq!(exon.name(), None);
    /// ```
    pub fn exon() -> Exon {
        Exon::default()
    }

    pub fn with_coords(mut self, start: u64, end: u64) -> Feature {
        self.start = start;
        self.end = end;
        self
    }
}

impl_interval!(Feature);

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

    #[test]
    fn default() {
        let fx = Feature::default();
        assert_eq!(fx.start(), 0);
        assert_eq!(fx.end(), 0);
        assert!(fx.validate().is_ok());
    }

    #[test]
    fn with_name() {
        let fx1 = Feature::default()
            .with_name("fx1");
        assert_eq!(fx1.start(), 0);
        assert_eq!(fx1.end(), 0);
        assert_eq!(fx1.name(), Some("fx1"));

        let fx2 = Feature::default()
            .with_name("fx2".to_owned());
        assert_eq!(fx2.start(), 0);
        assert_eq!(fx2.end(), 0);
        assert_eq!(fx2.name(), Some("fx2"));
    }

    #[test]
    fn with_coords() {
        let fx = Feature::default()
            .with_coords(1, 3);
        assert_eq!(fx.start(), 1);
        assert_eq!(fx.end(), 3);
        assert_eq!(fx.name(), None);
    }

    #[test]
    fn with_multiples() {
        let fx = Feature::default()
            .with_name("fx")
            .with_coords(20, 30);
        assert_eq!(fx.start(), 20);
        assert_eq!(fx.end(), 30);
        assert_eq!(fx.name(), Some("fx"));
    }
}

/// Gene annotation.
#[derive(Debug, Default)]
pub struct Gene {
    name: Option<String>,
    start: u64,
    end: u64,
    transcripts: HashMap<String, Transcript>,
}

impl Gene {

    pub fn transcripts(&self) -> &HashMap<String, Transcript> {
        &self.transcripts
    }
}

impl_interval!(Gene);

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

    #[test]
    fn default() {
        let gene = Feature::gene();
        assert_eq!(gene.start(), 0);
        assert_eq!(gene.end(), 0);
        assert_eq!(gene.name(), None);
        assert_eq!(gene.transcripts().len(), 0);
    }
}

/// Transcript annotation.
#[derive(Debug)]
pub struct Transcript {
    name: Option<String>,
    strand: Strand,
    start: u64,
    end: u64,
    cds_start: Option<u64>,
    cds_end: Option<u64>,
    exons: Vec<Exon>,
}

impl Default for Transcript {

    fn default() -> Transcript {
        Transcript {
            name: None,
            strand: Strand::Unknown,
            start: 0,
            end: 0,
            cds_start: None,
            cds_end: None,
            exons: Vec::new(),
        }
    }
}

impl Transcript {

    pub fn strand(&self) -> &Strand {
        &self.strand
    }

    pub fn cds_start(&self) -> Option<u64> {
        self.cds_start
    }

    pub fn cds_end(&self) -> Option<u64> {
        self.cds_end
    }

    pub fn exons(&self) -> &Vec<Exon> {
        &self.exons
    }
}

impl_interval!(Transcript);

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

    #[test]
    fn default() {
        let trx = Feature::transcript();
        assert_eq!(trx.start(), 0);
        assert_eq!(trx.end(), 0);
        assert_eq!(trx.name(), None);
        assert_eq!(trx.strand(), &Strand::Unknown);
        assert_eq!(trx.cds_start(), None);
        assert_eq!(trx.cds_end(), None);
        assert_eq!(trx.exons().len(), 0);
    }
}

/// Exon annotation.
#[derive(Debug, Default)]
pub struct Exon {
    name: Option<String>,
    start: u64,
    end: u64,
}

impl_interval!(Exon);

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

    #[test]
    fn default() {
        let exon = Feature::exon();
        assert_eq!(exon.start(), 0);
        assert_eq!(exon.end(), 0);
        assert_eq!(exon.name(), None);
    }
}
<commit_msg>Update Transcript impl<commit_after>//! Features and intervals for gene annotations.

use std::collections::HashMap;

use bio::io::Strand;

use self::error::FeatureError;

pub mod error {

    use std::error::Error;
    use std::fmt;

    #[derive(Debug)]
    pub enum FeatureError {
        InvalidCoords,
    }

    impl Error for FeatureError {
        fn description(&self) -> &str {
            match *self {
                FeatureError::InvalidCoords => "interval start coordinate larger than its end coordinate"
            }
        }

        fn cause(&self) -> Option<&Error> {
            None
        }
    }

    impl fmt::Display for FeatureError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "FeatureError: {}", self.description())
        }
    }
}

/// Interface for features with start and end coordinates.
///
/// The intervals represented here are zero-based, half open intervals.
/// Coordinates start from zero and start coordinates must be less than
/// or equal to the end coordinates.
pub trait Interval: Sized {

    /// Start coordinate of the interval.
    fn start(&self) -> u64;

    /// End coordinate of the interval.
    fn end(&self) -> u64;

    /// Name of the interval.
    fn name(&self) -> Option<&str>;

    /// Name setter that returns the implementor itself.
    ///
    /// This function is expected to mutate the implementing type.
    fn with_name<T: Into<String>>(self, name: T) -> Self;

    /// Coordinate setter that returns the implementor itself.
    ///
    /// This function is expected to mutate the implementing type.
    fn with_coords(self, start: u64, end: u64) -> Self;

    /// Performs various validation of the interval.
    fn validate(self) -> Result<Self, FeatureError> {
        self.validate_coords()
    }

    /// The number of bases covered by the interval.
    fn span(&self) -> u64 {
        self.end() - self.start()
    }

    /// Whether two intervals have an intersection or not.
    fn overlaps(&self, other: &Self) -> bool {
        (other.start() <= self.start() && self.start() <= other.end()) ||
        (other.start() <= self.end() && self.end() <= other.end())
    }

    /// Whether one interval completely contains the other.
    fn envelops(&self, other: &Self) -> bool {
        self.start() <= other.start() && self.end() >= other.end()
    }

    /// Whether two intervals cover a contiguous region without any overlaps.
    fn adjacent(&self, other: &Self) -> bool {
        self.end() == other.start() || self.start() == other.end()
    }

    /// Performs validation of the interval coordinates.
    ///
    /// If the validation fails, an error message is returned within
    /// the `Result` type.
    fn validate_coords(self) -> Result<Self, FeatureError> {
        if self.start() > self.end() {
            return Err(FeatureError::InvalidCoords)
        }

        Ok(self)
    }
}

/// Macro for default function implementations of interval types.
macro_rules! impl_interval {
    ($struct_ty:ty) => (

        impl Interval for $struct_ty {

            /// Name of the interval.
            fn name(&self) -> Option<&str> {
                self.name.as_ref().map(|n| n.as_str())
            }

            /// Start coordinate of the interval.
            fn start(&self) -> u64 {
                self.start
            }

            /// End coordinate of the interval.
            fn end(&self) -> u64 {
                self.end
            }

            fn with_name<T>(mut self, name: T) -> $struct_ty
                where T: Into<String>
            {
                self.name = Some(name.into());
                self
            }

            fn with_coords(mut self, start: u64, end: u64) -> $struct_ty {
                self.start = start;
                self.end = end;
                self
            }
        }

    );
}

/// Default implementation of the `Interval` trait.
///
/// This struct also provides static methods for creating exons, transcripts, and genes.
#[derive(Debug, Default)]
pub struct Feature {
    start: u64,
    end: u64,
    name: Option<String>,
}

impl Feature {

    /// Creates a gene interval with default values.
    ///
    /// A gene interval is a container for transcript intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// let gene = Feature::gene();
    ///
    /// assert_eq!(gene.transcript().len(), 0);
    /// assert_eq!(gene.start(), 0);
    /// assert_eq!(gene.end(), 0);
    /// assert_eq!(gene.name(), None);
    /// ```
    pub fn gene() -> Gene {
        Gene::default()
    }

    /// Creates a transcript interval with default values.
    ///
    /// A transcript interval is a container for exon intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// use bio::io::Strand;
    ///
    /// let transcript = Feature::transcript();
    ///
    /// assert_eq!(transcript.exons().len(), 0);
    /// assert_eq!(transcript.strand(), &Strand::Unknown)
    /// assert_eq!(transcript.start(), 0);
    /// assert_eq!(transcript.end(), 0);
    /// assert_eq!(transcript.name(), None);
    /// ```
    pub fn transcript() -> Transcript {
        Transcript::default()
    }

    /// Creates an exon interval with default values.
    ///
    /// # Examples
    ///
    /// ```
    /// let exon = Feature::exon();
    ///
    /// assert_eq!(exon.start(), 0);
    /// assert_eq!(exon.end(), 0);
    /// assert_eq!(exon.name(), None);
    /// ```
    pub fn exon() -> Exon {
        Exon::default()
    }

    pub fn with_coords(mut self, start: u64, end: u64) -> Feature {
        self.start = start;
        self.end = end;
        self
    }
}

impl_interval!(Feature);

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

    #[test]
    fn default() {
        let fx = Feature::default();
        assert_eq!(fx.start(), 0);
        assert_eq!(fx.end(), 0);
        assert!(fx.validate().is_ok());
    }

    #[test]
    fn with_name() {
        let fx1 = Feature::default()
            .with_name("fx1");
        assert_eq!(fx1.start(), 0);
        assert_eq!(fx1.end(), 0);
        assert_eq!(fx1.name(), Some("fx1"));

        let fx2 = Feature::default()
            .with_name("fx2".to_owned());
        assert_eq!(fx2.start(), 0);
        assert_eq!(fx2.end(), 0);
        assert_eq!(fx2.name(), Some("fx2"));
    }

    #[test]
    fn with_coords() {
        let fx = Feature::default()
            .with_coords(1, 3);
        assert_eq!(fx.start(), 1);
        assert_eq!(fx.end(), 3);
        assert_eq!(fx.name(), None);
    }

    #[test]
    fn with_multiples() {
        let fx = Feature::default()
            .with_name("fx")
            .with_coords(20, 30);
        assert_eq!(fx.start(), 20);
        assert_eq!(fx.end(), 30);
        assert_eq!(fx.name(), Some("fx"));
    }
}

/// Gene annotation.
#[derive(Debug, Default)]
pub struct Gene {
    name: Option<String>,
    start: u64,
    end: u64,
    transcripts: HashMap<String, Transcript>,
}

impl Gene {

    pub fn transcripts(&self) -> &HashMap<String, Transcript> {
        &self.transcripts
    }
}

impl_interval!(Gene);

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

    #[test]
    fn default() {
        let gene = Feature::gene();
        assert_eq!(gene.start(), 0);
        assert_eq!(gene.end(), 0);
        assert_eq!(gene.name(), None);
        assert_eq!(gene.transcripts().len(), 0);
    }
}

/// Transcript annotation.
#[derive(Debug)]
pub struct Transcript {
    name: Option<String>,
    strand: Strand,
    start: u64,
    end: u64,
    cds_start: Option<u64>,
    cds_end: Option<u64>,
    exons: Vec<Exon>,
}

impl Transcript {

    pub fn strand(&self) -> &Strand {
        &self.strand
    }

    pub fn with_strand(mut self, strand: Strand) -> Transcript {
        self.strand = strand;
        self
    }

    pub fn cds_start(&self) -> Option<u64> {
        self.cds_start
    }

    pub fn with_cds_start(mut self, cds_start: u64) -> Transcript {
        self.cds_start = Some(cds_start);
        self
    }

    pub fn cds_end(&self) -> Option<u64> {
        self.cds_end
    }

    pub fn with_cds_end(mut self, cds_end: u64) -> Transcript {
        self.cds_end = Some(cds_end);
        self
    }

    pub fn exons(&self) -> &Vec<Exon> {
        &self.exons
    }
}

impl Default for Transcript {

    fn default() -> Transcript {
        Transcript {
            name: None,
            strand: Strand::Unknown,
            start: 0,
            end: 0,
            cds_start: None,
            cds_end: None,
            exons: Vec::new(),
        }
    }
}

impl_interval!(Transcript);

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

    #[test]
    fn default() {
        let trx = Feature::transcript();
        assert_eq!(trx.start(), 0);
        assert_eq!(trx.end(), 0);
        assert_eq!(trx.name(), None);
        assert_eq!(trx.strand(), &Strand::Unknown);
        assert_eq!(trx.cds_start(), None);
        assert_eq!(trx.cds_end(), None);
        assert_eq!(trx.exons().len(), 0);
    }

    #[test]
    fn with_strand() {
        let trx = Feature::transcript()
            .with_strand(Strand::Forward);
        assert_eq!(trx.strand(), &Strand::Forward);
    }

    #[test]
    fn with_cds_start() {
        let trx = Feature::transcript()
            .with_cds_start(20);
        assert_eq!(trx.cds_start(), Some(20));
    }

    #[test]
    fn with_cds_end() {
        let trx = Feature::transcript()
            .with_cds_end(40);
        assert_eq!(trx.cds_end(), Some(40))
    }
}

/// Exon annotation.
#[derive(Debug, Default)]
pub struct Exon {
    name: Option<String>,
    start: u64,
    end: u64,
}

impl_interval!(Exon);

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

    #[test]
    fn default() {
        let exon = Feature::exon();
        assert_eq!(exon.start(), 0);
        assert_eq!(exon.end(), 0);
        assert_eq!(exon.name(), None);
    }
}
<|endoftext|>


<commit_before>//! Features and intervals for gene annotations.

use std::collections::HashMap;

use bio::io::Strand;

use self::error::FeatureError;

pub mod error {

    use std::error::Error;
    use std::fmt;

    #[derive(Debug)]
    pub enum FeatureError {
        InvalidCoords,
    }

    impl Error for FeatureError {
        fn description(&self) -> &str {
            match *self {
                FeatureError::InvalidCoords => "interval start coordinate larger than its end coordinate"
            }
        }

        fn cause(&self) -> Option<&Error> {
            None
        }
    }

    impl fmt::Display for FeatureError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "FeatureError: {}", self.description())
        }
    }
}

/// Interface for features with start and end coordinates.
///
/// The intervals represented here are zero-based, half open intervals.
/// Coordinates start from zero and start coordinates must be less than
/// or equal to the end coordinates.
pub trait Interval: Sized {

    /// Start coordinate of the interval.
    fn start(&self) -> u64;

    /// End coordinate of the interval.
    fn end(&self) -> u64;

    /// Performs various validation of the interval.
    fn validate(self) -> Result<Self, FeatureError>;

    /// The number of bases covered by the interval.
    fn span(&self) -> u64 {
        self.end() - self.start()
    }

    /// Whether two intervals have an intersection or not.
    fn overlaps(&self, other: &Self) -> bool {
        (other.start() <= self.start() && self.start() <= other.end()) ||
        (other.start() <= self.end() && self.end() <= other.end())
    }

    /// Whether one interval completely contains the other.
    fn envelops(&self, other: &Self) -> bool {
        self.start() <= other.start() && self.end() >= other.end()
    }

    /// Whether two intervals cover a contiguous region without any overlaps.
    fn adjacent(&self, other: &Self) -> bool {
        self.end() == other.start() || self.start() == other.end()
    }

    /// Performs validation of the interval coordinates.
    ///
    /// If the validation fails, an error message is returned within
    /// the `Result` type.
    fn validate_coords(self) -> Result<Self, FeatureError> {
        if self.start() > self.end() {
            return Err(FeatureError::InvalidCoords)
        }

        Ok(self)
    }
}

/// Default implementation of the `Interval` trait.
///
/// This struct also provides static methods for creating exons, transcripts, and genes.
#[derive(Debug, Default)]
pub struct Feature {
    start: u64,
    end: u64,
}

impl Feature {

    /// Creates a gene interval with default values.
    ///
    /// A gene interval is a container for transcript intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// let gene = Feature::gene();
    ///
    /// assert_eq!(gene.transcript().len(), 0);
    /// assert_eq!(gene.start(), 0);
    /// assert_eq!(gene.end(), 0);
    /// assert_eq!(gene.name(), None);
    /// ```
    pub fn gene() -> Gene {
        Gene::default()
    }

    /// Creates a transcript interval with default values.
    ///
    /// A transcript interval is a container for exon intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// use bio::io::Strand;
    ///
    /// let transcript = Feature::transcript();
    ///
    /// assert_eq!(transcript.exons().len(), 0);
    /// assert_eq!(transcript.strand(), &Strand::Unknown)
    /// assert_eq!(transcript.start(), 0);
    /// assert_eq!(transcript.end(), 0);
    /// assert_eq!(transcript.name(), None);
    /// ```
    pub fn transcript() -> Transcript {
        Transcript::default()
    }

    /// Creates an exon interval with default values.
    ///
    /// # Examples
    ///
    /// ```
    /// let exon = Feature::exon();
    ///
    /// assert_eq!(exon.start(), 0);
    /// assert_eq!(exon.end(), 0);
    /// assert_eq!(exon.name(), None);
    /// ```
    pub fn exon() -> Exon {
        Exon::default()
    }
}

impl Interval for Feature {

    fn start(&self) -> u64 {
        self.start
    }

    fn end(&self) -> u64 {
        self.end
    }

    fn validate(self) -> Result<Feature, FeatureError> {
        self.validate_coords()
    }
}

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

    #[test]
    fn default() {
        let fx = Feature::default();
        assert_eq!(fx.start(), 0);
        assert_eq!(fx.end(), 0);
        assert!(fx.validate().is_ok());
    }
}

/// Gene annotation.
#[derive(Debug, Default)]
pub struct Gene {
    name: Option<String>,
    start: u64,
    end: u64,
    transcripts: HashMap<String, Transcript>,
}

impl Gene {

    pub fn name(&self) -> Option<&str> {
        self.name.as_ref().map(|n| n.as_str())
    }

    pub fn transcripts(&self) -> &HashMap<String, Transcript> {
        &self.transcripts
    }
}

impl Interval for Gene {

    fn start(&self) -> u64 {
        self.start
    }

    fn end(&self) -> u64 {
        self.end
    }

    fn validate(self) -> Result<Gene, FeatureError> {
        self.validate_coords()
    }
}

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

    #[test]
    fn default() {
        let gene = Feature::gene();
        assert_eq!(gene.start(), 0);
        assert_eq!(gene.end(), 0);
        assert_eq!(gene.name(), None);
        assert_eq!(gene.transcripts().len(), 0);
    }
}

/// Transcript annotation.
#[derive(Debug)]
pub struct Transcript {
    name: Option<String>,
    strand: Strand,
    start: u64,
    end: u64,
    cds_start: Option<u64>,
    cds_end: Option<u64>,
    exons: Vec<Exon>,
}

impl Default for Transcript {

    fn default() -> Transcript {
        Transcript {
            name: None,
            strand: Strand::Unknown,
            start: 0,
            end: 0,
            cds_start: None,
            cds_end: None,
            exons: Vec::new(),
        }
    }
}

impl Transcript {

    pub fn name(&self) -> Option<&str> {
        self.name.as_ref().map(|n| n.as_str())
    }

    pub fn strand(&self) -> &Strand {
        &self.strand
    }

    pub fn cds_start(&self) -> Option<u64> {
        self.cds_start
    }

    pub fn cds_end(&self) -> Option<u64> {
        self.cds_end
    }

    pub fn exons(&self) -> &Vec<Exon> {
        &self.exons
    }
}

impl Interval for Transcript {

    fn start(&self) -> u64 {
        self.start
    }

    fn end(&self) -> u64 {
        self.end
    }

    fn validate(self) -> Result<Transcript, FeatureError> {
        self.validate_coords()
    }
}

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

    #[test]
    fn default() {
        let trx = Feature::transcript();
        assert_eq!(trx.start(), 0);
        assert_eq!(trx.end(), 0);
        assert_eq!(trx.name(), None);
        assert_eq!(trx.strand(), &Strand::Unknown);
        assert_eq!(trx.cds_start(), None);
        assert_eq!(trx.cds_end(), None);
        assert_eq!(trx.exons().len(), 0);
    }
}

/// Exon annotation.
#[derive(Debug, Default)]
pub struct Exon {
    name: Option<String>,
    start: u64,
    end: u64,
}

impl Exon {

    pub fn name(&self) -> Option<&str> {
        self.name.as_ref().map(|n| n.as_str())
    }

    pub fn with_name<T>(mut self, n: T) -> Exon
        where T: Into<String>
    {
        self.name = Some(n.into());
        self
    }
}

impl Interval for Exon {

    fn start(&self) -> u64 {
        self.start
    }

    fn end(&self) -> u64 {
        self.end
    }

    fn validate(self) -> Result<Exon, FeatureError> {
        self.validate_coords()
    }
}

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

    #[test]
    fn default() {
        let exon = Feature::exon();
        assert_eq!(exon.start(), 0);
        assert_eq!(exon.end(), 0);
        assert_eq!(exon.name(), None);
    }

    #[test]
    fn with_name() {
        let exon1 = Feature::exon().with_name("ex1");
        assert_eq!(exon1.start(), 0);
        assert_eq!(exon1.end(), 0);
        assert_eq!(exon1.name(), Some("ex1"));

        let exon2 = Feature::exon().with_name("ex2".to_owned());
        assert_eq!(exon2.start(), 0);
        assert_eq!(exon2.end(), 0);
        assert_eq!(exon2.name(), Some("ex2"));
    }
}
<commit_msg>Add impl_interval for implementing trait functions.<commit_after>//! Features and intervals for gene annotations.

use std::collections::HashMap;

use bio::io::Strand;

use self::error::FeatureError;

pub mod error {

    use std::error::Error;
    use std::fmt;

    #[derive(Debug)]
    pub enum FeatureError {
        InvalidCoords,
    }

    impl Error for FeatureError {
        fn description(&self) -> &str {
            match *self {
                FeatureError::InvalidCoords => "interval start coordinate larger than its end coordinate"
            }
        }

        fn cause(&self) -> Option<&Error> {
            None
        }
    }

    impl fmt::Display for FeatureError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "FeatureError: {}", self.description())
        }
    }
}

/// Interface for features with start and end coordinates.
///
/// The intervals represented here are zero-based, half open intervals.
/// Coordinates start from zero and start coordinates must be less than
/// or equal to the end coordinates.
pub trait Interval: Sized {

    /// Start coordinate of the interval.
    fn start(&self) -> u64;

    /// End coordinate of the interval.
    fn end(&self) -> u64;

    /// Name of the interval.
    fn name(&self) -> Option<&str>;

    /// Name setter that returns the struct itself.
    ///
    /// This function is expected to mutate the implementing struct.
    fn with_name<T: Into<String>>(self, name: T) -> Self;

    /// Performs various validation of the interval.
    fn validate(self) -> Result<Self, FeatureError> {
        self.validate_coords()
    }

    /// The number of bases covered by the interval.
    fn span(&self) -> u64 {
        self.end() - self.start()
    }

    /// Whether two intervals have an intersection or not.
    fn overlaps(&self, other: &Self) -> bool {
        (other.start() <= self.start() && self.start() <= other.end()) ||
        (other.start() <= self.end() && self.end() <= other.end())
    }

    /// Whether one interval completely contains the other.
    fn envelops(&self, other: &Self) -> bool {
        self.start() <= other.start() && self.end() >= other.end()
    }

    /// Whether two intervals cover a contiguous region without any overlaps.
    fn adjacent(&self, other: &Self) -> bool {
        self.end() == other.start() || self.start() == other.end()
    }

    /// Performs validation of the interval coordinates.
    ///
    /// If the validation fails, an error message is returned within
    /// the `Result` type.
    fn validate_coords(self) -> Result<Self, FeatureError> {
        if self.start() > self.end() {
            return Err(FeatureError::InvalidCoords)
        }

        Ok(self)
    }
}

/// Macro for default function implementations of interval types.
macro_rules! impl_interval {
    ($struct_ty:ty) => (

        impl Interval for $struct_ty {

            /// Name of the interval.
            fn name(&self) -> Option<&str> {
                self.name.as_ref().map(|n| n.as_str())
            }

            /// Start coordinate of the interval.
            fn start(&self) -> u64 {
                self.start
            }

            /// End coordinate of the interval.
            fn end(&self) -> u64 {
                self.end
            }

            fn with_name<T>(mut self, name: T) -> $struct_ty
                where T: Into<String>
            {
                self.name = Some(name.into());
                self
            }
        }

    );
}

/// Default implementation of the `Interval` trait.
///
/// This struct also provides static methods for creating exons, transcripts, and genes.
#[derive(Debug, Default)]
pub struct Feature {
    start: u64,
    end: u64,
    name: Option<String>,
}

impl Feature {

    /// Creates a gene interval with default values.
    ///
    /// A gene interval is a container for transcript intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// let gene = Feature::gene();
    ///
    /// assert_eq!(gene.transcript().len(), 0);
    /// assert_eq!(gene.start(), 0);
    /// assert_eq!(gene.end(), 0);
    /// assert_eq!(gene.name(), None);
    /// ```
    pub fn gene() -> Gene {
        Gene::default()
    }

    /// Creates a transcript interval with default values.
    ///
    /// A transcript interval is a container for exon intervals.
    ///
    /// # Examples
    ///
    /// ```
    /// use bio::io::Strand;
    ///
    /// let transcript = Feature::transcript();
    ///
    /// assert_eq!(transcript.exons().len(), 0);
    /// assert_eq!(transcript.strand(), &Strand::Unknown)
    /// assert_eq!(transcript.start(), 0);
    /// assert_eq!(transcript.end(), 0);
    /// assert_eq!(transcript.name(), None);
    /// ```
    pub fn transcript() -> Transcript {
        Transcript::default()
    }

    /// Creates an exon interval with default values.
    ///
    /// # Examples
    ///
    /// ```
    /// let exon = Feature::exon();
    ///
    /// assert_eq!(exon.start(), 0);
    /// assert_eq!(exon.end(), 0);
    /// assert_eq!(exon.name(), None);
    /// ```
    pub fn exon() -> Exon {
        Exon::default()
    }

    pub fn with_coords(mut self, start: u64, end: u64) -> Feature {
        self.start = start;
        self.end = end;
        self
    }
}

impl_interval!(Feature);

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

    #[test]
    fn default() {
        let fx = Feature::default();
        assert_eq!(fx.start(), 0);
        assert_eq!(fx.end(), 0);
        assert!(fx.validate().is_ok());
    }
}

/// Gene annotation.
#[derive(Debug, Default)]
pub struct Gene {
    name: Option<String>,
    start: u64,
    end: u64,
    transcripts: HashMap<String, Transcript>,
}

impl Gene {

    pub fn transcripts(&self) -> &HashMap<String, Transcript> {
        &self.transcripts
    }
}

impl_interval!(Gene);

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

    #[test]
    fn default() {
        let gene = Feature::gene();
        assert_eq!(gene.start(), 0);
        assert_eq!(gene.end(), 0);
        assert_eq!(gene.name(), None);
        assert_eq!(gene.transcripts().len(), 0);
    }
}

/// Transcript annotation.
#[derive(Debug)]
pub struct Transcript {
    name: Option<String>,
    strand: Strand,
    start: u64,
    end: u64,
    cds_start: Option<u64>,
    cds_end: Option<u64>,
    exons: Vec<Exon>,
}

impl Default for Transcript {

    fn default() -> Transcript {
        Transcript {
            name: None,
            strand: Strand::Unknown,
            start: 0,
            end: 0,
            cds_start: None,
            cds_end: None,
            exons: Vec::new(),
        }
    }
}

impl Transcript {

    pub fn strand(&self) -> &Strand {
        &self.strand
    }

    pub fn cds_start(&self) -> Option<u64> {
        self.cds_start
    }

    pub fn cds_end(&self) -> Option<u64> {
        self.cds_end
    }

    pub fn exons(&self) -> &Vec<Exon> {
        &self.exons
    }
}

impl_interval!(Transcript);

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

    #[test]
    fn default() {
        let trx = Feature::transcript();
        assert_eq!(trx.start(), 0);
        assert_eq!(trx.end(), 0);
        assert_eq!(trx.name(), None);
        assert_eq!(trx.strand(), &Strand::Unknown);
        assert_eq!(trx.cds_start(), None);
        assert_eq!(trx.cds_end(), None);
        assert_eq!(trx.exons().len(), 0);
    }
}

/// Exon annotation.
#[derive(Debug, Default)]
pub struct Exon {
    name: Option<String>,
    start: u64,
    end: u64,
}

impl_interval!(Exon);

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

    #[test]
    fn default() {
        let exon = Feature::exon();
        assert_eq!(exon.start(), 0);
        assert_eq!(exon.end(), 0);
        assert_eq!(exon.name(), None);
    }

    #[test]
    fn with_name() {
        let exon1 = Feature::exon()
            .with_name("ex1");
        assert_eq!(exon1.start(), 0);
        assert_eq!(exon1.end(), 0);
        assert_eq!(exon1.name(), Some("ex1"));

        let exon2 = Feature::exon()
            .with_name("ex2".to_owned());
        assert_eq!(exon2.start(), 0);
        assert_eq!(exon2.end(), 0);
        assert_eq!(exon2.name(), Some("ex2"));
    }
}
<|endoftext|>


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

//!
//! Protect the server's long-term key with envelope encryption and a key management system.
//!
//! ## Motivation
//!
//! The seed for the server's [long-term key](../key/struct.LongTermKey.html) is subject to
//! contradictory requirements:
//!
//!   1. The seed must be kept secret, but
//!   2. The seed must be available at server start-up to create the
//!      [delegated on-line key](../key/struct.OnlineKey.html)
//!
//! ## Plaintext seed
//!
//! The default option is to store the seed in plaintext as part of the server's configuration.
//! This usually means the seed is present in the clear: on disk, in a repository, or otherwise
//! durably persisted where it can be compromised (accidentally or maliciously).
//!
//! ## Encrypting the seed
//!
//! Envelope encryption protects the seed by encrypting it with a locally generated 256-bit
//! Data Encryption Key (DEK). The DEK itself is then encrypted using a cloud key management
//! system (KMS). The resulting opaque encrypted "blob" (encrypted seed + encrypted DEK) is
//! stored in the Roughenough configuration.
//!
//! At server start-up the KMS is used to decrypt the DEK, which is then used to (in memory)
//! decrypt the seed. The seed is used to generate the
//! [delegated on-line key](../key/struct.OnlineKey.html) after which the seed and DEK are erased
//! from memory.
//!
//! See
//!   * [`EnvelopeEncryption`](struct.EnvelopeEncryption.html) for Roughenough's implementation.
//!   * [Google](https://cloud.google.com/kms/docs/envelope-encryption) or
//!     [Amazon](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping)
//!     for more in-depth explanations of envelope encryption.
//!

mod envelope;

use base64;
use ring;
use std;

use config::ServerConfig;
use error;
use key::KeyProtection;

pub use self::envelope::EnvelopeEncryption;

/// Errors generated by KMS operations
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)]
pub enum KmsError {
    OperationFailed(String),
    InvalidConfiguration(String),
    InvalidData(String),
    InvalidKey(String),
}

impl From<std::io::Error> for KmsError {
    fn from(error: std::io::Error) -> Self {
        KmsError::OperationFailed(format!("{:?}", error))
    }
}

impl From<ring::error::Unspecified> for KmsError {
    fn from(_: ring::error::Unspecified) -> Self {
        KmsError::OperationFailed("unspecified ring cryptographic failure".to_string())
    }
}

impl From<base64::DecodeError> for KmsError {
    fn from(error: base64::DecodeError) -> Self {
        KmsError::OperationFailed(format!("base64: {}", error))
    }
}

// Size of the AEAD nonce in bytes.
const NONCE_SIZE_BYTES: usize = 12;

// Size of the AEAD authentication tag in bytes.
const TAG_SIZE_BYTES: usize = 16;

// Size of the 256-bit Data Encryption Key (DEK) in bytes.
const DEK_SIZE_BYTES: usize = 32;

// Trivial domain separation to guard against KMS key reuse
const AD: &str = "roughenough";

/// An unencrypted (plaintext) 256-bit Data Encryption Key (DEK).
pub type PlaintextDEK = Vec<u8>;

/// A Data Encryption Key (DEK) that has been encrypted (wrapped) by a Key Management System (KMS).
///
/// This is an opaque, implementation-specific value. AEAD tag size, nonce size,
/// provider metadata, and so on will vary between [`KmsProvider`](trait.KmsProvider.html)
/// implementations.
pub type EncryptedDEK = Vec<u8>;

///
/// A key management system that wraps/unwraps a data encryption key (DEK).
///
pub trait KmsProvider {
    /// Make a blocking request to encrypt (wrap) the provided plaintext data encryption key.
    fn encrypt_dek(&self, plaintext_dek: &PlaintextDEK) -> Result<EncryptedDEK, KmsError>;

    /// Make a blocking request to decrypt (unwrap) a previously encrypted data encryption key.
    fn decrypt_dek(&self, encrypted_dek: &EncryptedDEK) -> Result<PlaintextDEK, KmsError>;
}

#[cfg(feature = "awskms")]
mod awskms;

#[cfg(feature = "awskms")]
pub use kms::awskms::inner::AwsKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.key_protection()`:
///
///  * If `config.key_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.key_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "awskms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    use kms::envelope::EnvelopeEncryption;

    match config.key_protection() {
        KeyProtection::Plaintext => Ok(config.seed()),
        KeyProtection::AwsKmsEnvelope(key_id) => {
            info!("Unwrapping seed via AWS KMS key '{}'", key_id);
            let kms = AwsKms::from_arn(key_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "Google KMS not supported".to_string(),
        )),
    }
}

#[cfg(feature = "gcpkms")]
mod gcpkms;

#[cfg(feature = "gcpkms")]
pub use kms::gcpkms::inner::GcpKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.key_protection()`:
///
///  * If `config.key_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.key_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "gcpkms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    use kms::envelope::EnvelopeEncryption;

    match config.key_protection() {
        KeyProtection::Plaintext => Ok(config.seed()),
        KeyProtection::GoogleKmsEnvelope(resource_id) => {
            info!("Unwrapping seed via Google KMS key '{}'", resource_id);
            let kms = GcpKms::from_resource_id(resource_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "AWS KMS not supported".to_string(),
        )),
    }
}

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.key_protection()`:
///
///  * If `config.key_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.key_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
/// ## KMS Disabled
///
/// The KMS feature is *disabled* in this build of Roughenough. The only
/// supported `key_protection` value is `plaintext`. Any other value is an error.
///
#[cfg(all(not(feature = "awskms"), not(feature = "gcpkms")))]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    match config.key_protection() {
        KeyProtection::Plaintext => Ok(config.seed()),
        v => Err(error::Error::InvalidConfiguration(format!(
            "key_protection '{}' requires KMS, but server was not compiled with KMS support",
            v
        ))),
    }
}
<commit_msg>minor cfg tweak<commit_after>// Copyright 2017-2018 int08h LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//!
//! Protect the server's long-term key with envelope encryption and a key management system.
//!
//! ## Motivation
//!
//! The seed for the server's [long-term key](../key/struct.LongTermKey.html) is subject to
//! contradictory requirements:
//!
//!   1. The seed must be kept secret, but
//!   2. The seed must be available at server start-up to create the
//!      [delegated on-line key](../key/struct.OnlineKey.html)
//!
//! ## Plaintext seed
//!
//! The default option is to store the seed in plaintext as part of the server's configuration.
//! This usually means the seed is present in the clear: on disk, in a repository, or otherwise
//! durably persisted where it can be compromised (accidentally or maliciously).
//!
//! ## Encrypting the seed
//!
//! Envelope encryption protects the seed by encrypting it with a locally generated 256-bit
//! Data Encryption Key (DEK). The DEK itself is then encrypted using a cloud key management
//! system (KMS). The resulting opaque encrypted "blob" (encrypted seed + encrypted DEK) is
//! stored in the Roughenough configuration.
//!
//! At server start-up the KMS is used to decrypt the DEK, which is then used to (in memory)
//! decrypt the seed. The seed is used to generate the
//! [delegated on-line key](../key/struct.OnlineKey.html) after which the seed and DEK are erased
//! from memory.
//!
//! See
//!   * [`EnvelopeEncryption`](struct.EnvelopeEncryption.html) for Roughenough's implementation.
//!   * [Google](https://cloud.google.com/kms/docs/envelope-encryption) or
//!     [Amazon](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping)
//!     for more in-depth explanations of envelope encryption.
//!

mod envelope;

use base64;
use ring;
use std;

use config::ServerConfig;
use error;
use key::KeyProtection;

pub use self::envelope::EnvelopeEncryption;

/// Errors generated by KMS operations
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)]
pub enum KmsError {
    OperationFailed(String),
    InvalidConfiguration(String),
    InvalidData(String),
    InvalidKey(String),
}

impl From<std::io::Error> for KmsError {
    fn from(error: std::io::Error) -> Self {
        KmsError::OperationFailed(format!("{:?}", error))
    }
}

impl From<ring::error::Unspecified> for KmsError {
    fn from(_: ring::error::Unspecified) -> Self {
        KmsError::OperationFailed("unspecified ring cryptographic failure".to_string())
    }
}

impl From<base64::DecodeError> for KmsError {
    fn from(error: base64::DecodeError) -> Self {
        KmsError::OperationFailed(format!("base64: {}", error))
    }
}

// Size of the AEAD nonce in bytes.
const NONCE_SIZE_BYTES: usize = 12;

// Size of the AEAD authentication tag in bytes.
const TAG_SIZE_BYTES: usize = 16;

// Size of the 256-bit Data Encryption Key (DEK) in bytes.
const DEK_SIZE_BYTES: usize = 32;

// Trivial domain separation to guard against KMS key reuse
const AD: &str = "roughenough";

/// An unencrypted (plaintext) 256-bit Data Encryption Key (DEK).
pub type PlaintextDEK = Vec<u8>;

/// A Data Encryption Key (DEK) that has been encrypted (wrapped) by a Key Management System (KMS).
///
/// This is an opaque, implementation-specific value. AEAD tag size, nonce size,
/// provider metadata, and so on will vary between [`KmsProvider`](trait.KmsProvider.html)
/// implementations.
pub type EncryptedDEK = Vec<u8>;

///
/// A key management system that wraps/unwraps a data encryption key (DEK).
///
pub trait KmsProvider {
    /// Make a blocking request to encrypt (wrap) the provided plaintext data encryption key.
    fn encrypt_dek(&self, plaintext_dek: &PlaintextDEK) -> Result<EncryptedDEK, KmsError>;

    /// Make a blocking request to decrypt (unwrap) a previously encrypted data encryption key.
    fn decrypt_dek(&self, encrypted_dek: &EncryptedDEK) -> Result<PlaintextDEK, KmsError>;
}

#[cfg(feature = "awskms")]
mod awskms;

#[cfg(feature = "awskms")]
pub use kms::awskms::inner::AwsKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.key_protection()`:
///
///  * If `config.key_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.key_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "awskms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    use kms::envelope::EnvelopeEncryption;

    match config.key_protection() {
        KeyProtection::Plaintext => Ok(config.seed()),
        KeyProtection::AwsKmsEnvelope(key_id) => {
            info!("Unwrapping seed via AWS KMS key '{}'", key_id);
            let kms = AwsKms::from_arn(key_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "Google KMS not supported".to_string(),
        )),
    }
}

#[cfg(feature = "gcpkms")]
mod gcpkms;

#[cfg(feature = "gcpkms")]
pub use kms::gcpkms::inner::GcpKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.key_protection()`:
///
///  * If `config.key_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.key_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "gcpkms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    use kms::envelope::EnvelopeEncryption;

    match config.key_protection() {
        KeyProtection::Plaintext => Ok(config.seed()),
        KeyProtection::GoogleKmsEnvelope(resource_id) => {
            info!("Unwrapping seed via Google KMS key '{}'", resource_id);
            let kms = GcpKms::from_resource_id(resource_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "AWS KMS not supported".to_string(),
        )),
    }
}

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.key_protection()`:
///
///  * If `config.key_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.key_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
/// ## KMS Disabled
///
/// The KMS feature is *disabled* in this build of Roughenough. The only
/// supported `key_protection` value is `plaintext`. Any other value is an error.
///
#[cfg(not(any(feature = "awskms", feature = "gcpkms")))]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    match config.key_protection() {
        KeyProtection::Plaintext => Ok(config.seed()),
        v => Err(error::Error::InvalidConfiguration(format!(
            "key_protection '{}' requires KMS, but server was not compiled with KMS support",
            v
        ))),
    }
}
<|endoftext|>


<commit_before>
import option::none;
import option::some;
import util::orb;

export vbuf;
export buf;
export len;

type vbuf = rustrt::vbuf;

type operator2[T, U, V] = fn(&T, &U) -> V ;

type array[T] = vec[mutable? T];

native "rust" mod rustrt {
    type vbuf;
    fn vec_buf[T](v: vec[T], offset: uint) -> vbuf;
    fn vec_len[T](v: vec[T]) -> uint;

    /**
     * Sometimes we modify the vec internal data via vec_buf and need to
     * update the vec's fill length accordingly.
     */
    fn vec_len_set[T](v: vec[T], n: uint);

    /**
     * The T in vec_alloc[T, U] is the type of the vec to allocate.  The
     * U is the type of an element in the vec.  So to allocate a vec[U] we
     * want to invoke this as vec_alloc[vec[U], U].
     */
    fn vec_alloc[T, U](n_elts: uint) -> vec[U];
    fn vec_alloc_mut[T, U](n_elts: uint) -> vec[mutable U];
    fn refcount[T](v: vec[T]) -> uint;
    fn vec_print_debug_info[T](v: vec[T]);
    fn vec_from_vbuf[T](v: vbuf, n_elts: uint) -> vec[T];
    fn unsafe_vec_to_mut[T](v: vec[T]) -> vec[mutable T];
}

fn alloc[T](n_elts: uint) -> vec[T] {
    ret rustrt::vec_alloc[vec[T], T](n_elts);
}

fn alloc_mut[T](n_elts: uint) -> vec[mutable T] {
    ret rustrt::vec_alloc_mut[vec[mutable T], T](n_elts);
}

fn refcount[T](v: array[T]) -> uint {
    let r = rustrt::refcount[T](v);
    if r == dbg::const_refcount { ret r; } else { ret r - 1u; }
}

fn vec_from_vbuf[T](v: vbuf, n_elts: uint) -> vec[T] {
    ret rustrt::vec_from_vbuf[T](v, n_elts);
}

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty[T]() -> vec[T] { ret alloc[T](0u); }

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty_mut[T]() -> vec[mutable T] { ret alloc_mut[T](0u); }

type init_op[T] = fn(uint) -> T ;

fn init_fn[@T](op: &init_op[T], n_elts: uint) -> vec[T] {
    let v: vec[T] = alloc[T](n_elts);
    let i: uint = 0u;
    while i < n_elts { v += [op(i)]; i += 1u; }
    ret v;
}

fn init_fn_mut[@T](op: &init_op[T], n_elts: uint) -> vec[mutable T] {
    let v: vec[mutable T] = alloc_mut[T](n_elts);
    let i: uint = 0u;
    while i < n_elts { v += [mutable op(i)]; i += 1u; }
    ret v;
}

// init_elt: creates and returns a vector of length n_elts, filled with
// that many copies of element t.
fn init_elt[@T](t: &T, n_elts: uint) -> vec[T] {
    /**
     * FIXME (issue #81): should be:
     *
     * fn elt_op[T](&T x, uint i) -> T { ret x; }
     * let init_op[T] inner = bind elt_op[T](t, _);
     * ret init_fn[T](inner, n_elts);
     */

    let v: vec[T] = alloc[T](n_elts);
    let i: uint = n_elts;
    while i > 0u { i -= 1u; v += [t]; }
    ret v;
}

fn init_elt_mut[@T](t: &T, n_elts: uint) -> vec[mutable T] {
    let v: vec[mutable T] = alloc_mut[T](n_elts);
    let i: uint = n_elts;
    while i > 0u { i -= 1u; v += [mutable t]; }
    ret v;
}

fn buf[T](v: array[T]) -> vbuf { ret rustrt::vec_buf[T](v, 0u); }

fn len[T](v: array[T]) -> uint { ret rustrt::vec_len[T](v); }

fn len_set[T](v: array[T], n: uint) { rustrt::vec_len_set[T](v, n); }

fn buf_off[T](v: array[T], offset: uint) -> vbuf {
    assert (offset < len[T](v));
    ret rustrt::vec_buf[T](v, offset);
}

fn print_debug_info[T](v: array[T]) { rustrt::vec_print_debug_info[T](v); }

// FIXME: typestate precondition (list is non-empty)
// Returns the last element of v.
fn last[@T](v: array[T]) -> option::t[T] {
    let l = len[T](v);
    if l == 0u { ret none[T]; }
    ret some[T](v.(l - 1u));
}

// Returns elements from [start..end) from v.
fn slice[@T](v: array[T], start: uint, end: uint) -> vec[T] {
    assert (start <= end);
    assert (end <= len[T](v));
    let result = alloc[T](end - start);
    let i: uint = start;
    while i < end { result += [v.(i)]; i += 1u; }
    ret result;
}

// FIXME: Should go away eventually.
fn slice_mut[@T](v: array[T], start: uint, end: uint) -> vec[mutable T] {
    assert (start <= end);
    assert (end <= len[T](v));
    let result = alloc_mut[T](end - start);
    let i: uint = start;
    while i < end { result += [mutable v.(i)]; i += 1u; }
    ret result;
}

fn shift[@T](v: &mutable array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    let e = v.(0);
    v = slice[T](v, 1u, ln);
    ret e;
}

fn pop[@T](v: &mutable array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    ln -= 1u;
    let e = v.(ln);
    v = slice[T](v, 0u, ln);
    ret e;
}

fn top[@T](v: &array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    ret v.(ln - 1u);
}

fn push[@T](v: &mutable array[T], t: &T) { v += [t]; }

fn unshift[@T](v: &mutable array[T], t: &T) {
    let rs = alloc[T](len[T](v) + 1u);
    rs += [t];
    rs += v;
    v = rs;
}

fn grow[@T](v: &mutable array[T], n: uint, initval: &T) {
    let i: uint = n;
    while i > 0u { i -= 1u; v += [initval]; }
}

fn grow_set[@T](v: &mutable vec[mutable T], index: uint, initval: &T,
               val: &T) {
    let length = vec::len(v);
    if index >= length { grow(v, index - length + 1u, initval); }
    v.(index) = val;
}

fn grow_init_fn[@T](v: &mutable array[T], n: uint, init_fn: fn() -> T ) {
    let i: uint = n;
    while i > 0u { i -= 1u; v += [init_fn()]; }
}

fn grow_init_fn_set[@T](v: &mutable array[T], index: uint, init_fn: fn() -> T,
                       val: &T) {
    let length = vec::len(v);
    if index >= length { grow_init_fn(v, index - length + 1u, init_fn); }
    v.(index) = val;
}

fn map[@T, @U](f: &fn(&T) -> U , v: &vec[T]) -> vec[U] {
    let rs: vec[U] = alloc[U](len[T](v));
    for ve: T  in v { rs += [f(ve)]; }
    ret rs;
}

fn filter_map[@T, @U](f: &fn(&T) -> option::t[U] , v: &vec[T]) -> vec[U] {
    let rs: vec[U] = [];
    for ve: T  in v { alt f(ve) { some(elt) { rs += [elt]; } none. { } } }
    ret rs;
}

fn map2[@T, @U, @V](f: &operator2[T, U, V], v0: &vec[T], v1: &vec[U])
    -> vec[V] {
    let v0_len = len[T](v0);
    if v0_len != len[U](v1) { fail; }
    let u: vec[V] = alloc[V](v0_len);
    let i = 0u;
    while i < v0_len { u += [f({ v0.(i) }, { v1.(i) })]; i += 1u; }
    ret u;
}

fn find[@T](f: fn(&T) -> bool , v: &vec[T]) -> option::t[T] {
    for elt: T  in v { if f(elt) { ret some[T](elt); } }
    ret none[T];
}

fn position[@T](x: &T, v: &array[T]) -> option::t[uint] {
    let i: uint = 0u;
    while i < len(v) { if x == v.(i) { ret some[uint](i); } i += 1u; }
    ret none[uint];
}

fn position_pred[T](f: fn(&T) -> bool , v: &vec[T]) -> option::t[uint] {
    let i: uint = 0u;
    while i < len(v) { if f(v.(i)) { ret some[uint](i); } i += 1u; }
    ret none[uint];
}

fn member[T](x: &T, v: &array[T]) -> bool {
    for elt: T in v { if x == elt { ret true; } }
    ret false;
}

fn count[T](x: &T, v: &array[T]) -> uint {
    let cnt = 0u;
    for elt: T  in v { if x == elt { cnt += 1u; } }
    ret cnt;
}

fn foldl[@T, @U](p: fn(&U, &T) -> U , z: &U, v: &vec[T]) -> U {
    let sz = len[T](v);
    if sz == 0u {
        ret z;
    } else {
        let rest = slice[T](v, 1u, sz);
        ret p(foldl[T, U](p, z, rest), v.(0));
    }
}

fn unzip[@T, @U](v: &vec[{_0: T, _1: U}]) -> {_0: vec[T], _1: vec[U]} {
    let sz = len(v);
    if sz == 0u {
        ret {_0: alloc[T](0u), _1: alloc[U](0u)};
    } else {
        let rest = slice(v, 1u, sz);
        let tl = unzip[T, U](rest);
        let a = [v.(0)._0];
        let b = [v.(0)._1];
        ret {_0: a + tl._0, _1: b + tl._1};
    }
}

// FIXME make the lengths being equal a constraint
fn zip[@T, @U](v: &vec[T], u: &vec[U]) -> vec[{_0: T, _1: U}] {
    let sz = len(v);
    assert (sz == len(u));
    if sz == 0u {
        ret alloc(0u);
    } else {
        let rest = zip(slice(v, 1u, sz), slice(u, 1u, sz));
        push(rest, {_0: v.(0), _1: u.(0)});
        ret rest;
    }
}

fn or(v: &vec[bool]) -> bool {
    let f = orb;
    ret foldl[bool, bool](f, false, v);
}

fn any[T](f: &fn(&T) -> bool , v: &vec[T]) -> bool {
    for t: T  in v { if f(t) { ret true; } }
    ret false;
}
fn all[T](f: &fn(&T) -> bool , v: &vec[T]) -> bool {
    for t: T  in v { if !f(t) { ret false; } }
    ret true;
}

fn clone[@T](v: &vec[T]) -> vec[T] { ret slice[T](v, 0u, len[T](v)); }

fn plus_option[@T](v: &mutable vec[T], o: &option::t[T]) {
    alt o { none. { } some(x) { v += [x]; } }
}

fn cat_options[@T](v: &vec[option::t[T]]) -> vec[T] {
    let rs: vec[T] = [];
    for o: option::t[T]  in v { alt o { none. { } some(t) { rs += [t]; } } }
    ret rs;
}

// TODO: Remove in favor of built-in "freeze" operation when it's implemented.
fn freeze[@T](v: vec[mutable T]) -> vec[T] {
    let result: vec[T] = [];
    for elem: T  in v { result += [elem]; }
    ret result;
}

// Swaps two elements in a vector
fn swap[@T](v: &vec[mutable T], a: uint, b: uint) {
    let t: T = v.(a);
    v.(a) = v.(b);
    v.(b) = t;
}

// In place vector reversal
fn reverse[@T](v: &vec[mutable T]) {
    let i: uint = 0u;
    let ln = len[T](v);
    while i < ln / 2u { swap(v, i, ln - i - 1u); i += 1u; }
}

// Functional vector reversal. Returns a reversed copy of v.
fn reversed[@T](v: vec[T]) -> vec[T] {
    let rs: vec[T] = [];
    let i = len[T](v);
    if i == 0u { ret rs; } else { i -= 1u; }
    while i != 0u { push[T](rs, v.(i)); i -= 1u; }
    push[T](rs, v.(0));
    ret rs;
}

/// Truncates the vector to length `new_len`.
/// FIXME: This relies on a typechecker bug (covariance vs. invariance).
fn truncate[@T](v: &mutable vec[mutable? T], new_len: uint) {
    v = slice[T](v, 0u, new_len);
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Remove unused functions from std::vec<commit_after>
import option::none;
import option::some;
import util::orb;

export vbuf;
export buf;
export len;

type vbuf = rustrt::vbuf;

type array[T] = vec[mutable? T];

native "rust" mod rustrt {
    type vbuf;
    fn vec_buf[T](v: vec[T], offset: uint) -> vbuf;
    fn vec_len[T](v: vec[T]) -> uint;

    /**
     * Sometimes we modify the vec internal data via vec_buf and need to
     * update the vec's fill length accordingly.
     */
    fn vec_len_set[T](v: vec[T], n: uint);

    /**
     * The T in vec_alloc[T, U] is the type of the vec to allocate.  The
     * U is the type of an element in the vec.  So to allocate a vec[U] we
     * want to invoke this as vec_alloc[vec[U], U].
     */
    fn vec_alloc[T, U](n_elts: uint) -> vec[U];
    fn vec_alloc_mut[T, U](n_elts: uint) -> vec[mutable U];
    fn refcount[T](v: vec[T]) -> uint;
    fn vec_print_debug_info[T](v: vec[T]);
    fn vec_from_vbuf[T](v: vbuf, n_elts: uint) -> vec[T];
    fn unsafe_vec_to_mut[T](v: vec[T]) -> vec[mutable T];
}

fn buf[T](v: array[T]) -> vbuf { ret rustrt::vec_buf[T](v, 0u); }

fn len[T](v: array[T]) -> uint { ret rustrt::vec_len[T](v); }

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>use std::panic::PanicInfo;
use log::LogLocation;
use config::Monitor as MonitorConfig;
use backtrace::Backtrace;
use std::thread::JoinHandle;

pub struct MonitorProvider;
impl MonitorProvider {
  pub fn find_with_config(monitor: &str, config: &MonitorConfig) -> Option<impl Monitor> {
    match monitor {
      "rollbar" => Some(rollbar::Rollbar::from_config(&config)),
      _         => None
    }
  }

  pub fn null_monitor() -> null_monitor::NullMonitor {
    null_monitor::NullMonitor
  }
}

pub trait Monitor: Send + Sync {
  type MonitorType: Monitor;
  type ResponseType;

  fn from_config(config: &MonitorConfig) -> Self::MonitorType;
  fn send(&self, error_message: &String, location: &LogLocation);
  fn send_panic(&self, panic_info: &PanicInfo, backtrace: &Backtrace) -> JoinHandle<Self::ResponseType>;
  fn is_real(&self) -> bool;
}

mod null_monitor {
  use super::{PanicInfo, Backtrace, JoinHandle, LogLocation, Monitor, MonitorConfig};

  pub struct NullMonitor;

  impl Monitor for NullMonitor {
    type MonitorType = NullMonitor;
    type ResponseType = Option<()>;

    fn from_config(_: &MonitorConfig) -> Self::MonitorType {
      NullMonitor
    }

    fn send(&self, _: &String, _: &LogLocation) {
      unimplemented!()
    }

    fn send_panic(&self, _: &PanicInfo, _: &Backtrace) -> JoinHandle<Self::ResponseType> {
      unimplemented!()
    }

    fn is_real(&self) -> bool {
      false
    }
  }
}

mod rollbar {
  use super::{PanicInfo, Backtrace, LogLocation, Monitor, MonitorConfig, JoinHandle};
  use rollbar::*;

  pub struct Rollbar {
    client: Client
  }

  impl Monitor for Rollbar {
    type MonitorType = Rollbar;
    type ResponseType = Option<ResponseStatus>;

    fn from_config(config: &MonitorConfig) -> Self::MonitorType {
      Rollbar {
        client: Client::new(config.access_token.to_owned(), config.environment.to_owned())
      }
    }

    fn send(&self, error_message: &String, location: &LogLocation) {
      self.client.build_report()
        .from_error_message(error_message)
        .with_frame(FrameBuilder::new()
                    .with_line_number(location.line())
                    .with_file_name(location.file())
                    .build())
        .send();
    }

    fn send_panic(&self, panic_info: &PanicInfo, backtrace: &Backtrace) -> JoinHandle<Self::ResponseType> {
      self.client.build_report()
          .from_panic(&panic_info)
          .with_backtrace(&backtrace)
          .send()
    }

    fn is_real(&self) -> bool {
      true
    }
  }
}
<commit_msg>use concrete type instead of impl Trait in return position<commit_after>use std::panic::PanicInfo;
use log::LogLocation;
use config::Monitor as MonitorConfig;
use backtrace::Backtrace;
use std::thread::JoinHandle;

pub struct MonitorProvider;
impl MonitorProvider {
  pub fn find_with_config(monitor: &str, config: &MonitorConfig) -> Option<rollbar::Rollbar> {
    match monitor {
      "rollbar" => Some(rollbar::Rollbar::from_config(&config)),
      _         => None
    }
  }

  pub fn null_monitor() -> null_monitor::NullMonitor {
    null_monitor::NullMonitor
  }
}

pub trait Monitor: Send + Sync {
  type MonitorType: Monitor;
  type ResponseType;

  fn from_config(config: &MonitorConfig) -> Self::MonitorType;
  fn send(&self, error_message: &String, location: &LogLocation);
  fn send_panic(&self, panic_info: &PanicInfo, backtrace: &Backtrace) -> JoinHandle<Self::ResponseType>;
  fn is_real(&self) -> bool;
}

mod null_monitor {
  use super::{PanicInfo, Backtrace, JoinHandle, LogLocation, Monitor, MonitorConfig};

  pub struct NullMonitor;

  impl Monitor for NullMonitor {
    type MonitorType = NullMonitor;
    type ResponseType = Option<()>;

    fn from_config(_: &MonitorConfig) -> Self::MonitorType {
      NullMonitor
    }

    fn send(&self, _: &String, _: &LogLocation) {
      unimplemented!()
    }

    fn send_panic(&self, _: &PanicInfo, _: &Backtrace) -> JoinHandle<Self::ResponseType> {
      unimplemented!()
    }

    fn is_real(&self) -> bool {
      false
    }
  }
}

mod rollbar {
  use super::{PanicInfo, Backtrace, LogLocation, Monitor, MonitorConfig, JoinHandle};
  use rollbar::*;

  pub struct Rollbar {
    client: Client
  }

  impl Monitor for Rollbar {
    type MonitorType = Rollbar;
    type ResponseType = Option<ResponseStatus>;

    fn from_config(config: &MonitorConfig) -> Self::MonitorType {
      Rollbar {
        client: Client::new(config.access_token.to_owned(), config.environment.to_owned())
      }
    }

    fn send(&self, error_message: &String, location: &LogLocation) {
      self.client.build_report()
        .from_error_message(error_message)
        .with_frame(FrameBuilder::new()
                    .with_line_number(location.line())
                    .with_file_name(location.file())
                    .build())
        .send();
    }

    fn send_panic(&self, panic_info: &PanicInfo, backtrace: &Backtrace) -> JoinHandle<Self::ResponseType> {
      self.client.build_report()
          .from_panic(&panic_info)
          .with_backtrace(&backtrace)
          .send()
    }

    fn is_real(&self) -> bool {
      true
    }
  }
}
<|endoftext|>


<commit_before>//! The net module is resonsible for the network related parts,
//! the mDNS registering, mDNS search, communication server and client.
//! It also let's us startup and perform everything in yet one step.
mod connect_from;
mod connect_to;
mod data;

pub mod key_keeper;

use self::connect_to::ConnectToOther;
use super::ctrl;
use async_std::{
    sync::{Arc, Mutex},
    task,
};
use futures_util::TryFutureExt;
use libp2p::{
    mdns::{service::MdnsPacket, MdnsService},
    PeerId,
};
use std::{
    self,
    sync::mpsc::{channel, Sender},
};

#[allow(dead_code)]
#[derive(Clone)]
enum ToThread<T: Send + Clone> {
    Data(T),
    Stop,
}

/// The Net component keeps control about everything from net.
///
/// # Arguments
/// * 'peer_id' - the own peer id (to not talk to much with itself ;-))
/// * 'clients_connected' - All clients that are connected
/// * 'has_ui' - if there is an ui present, that would need update messages///
/// * 'ui_sender' - to send out update ui messages
pub struct Net {
    #[allow(dead_code)]
    peer_id: PeerId,
    clients_connected: Arc<Mutex<Vec<PeerId>>>,
    has_ui: bool,
    ui_sender: Sender<ctrl::UiUpdateMsg>,
}

impl Net {
    pub fn new(peer_id: PeerId, has_ui: bool, ui_sender: Sender<ctrl::UiUpdateMsg>) -> Self {
        Net {
            peer_id,
            clients_connected: Arc::new(Mutex::new(Vec::new())),
            has_ui,
            ui_sender,
        }
    }

    /// Lookup yet is the start of the networking.
    /// It looks for possible mDNS clients and spawns
    pub async fn lookup(&mut self) {
        // threads to connect to them.
        // It uses timeouts, checkups.

        // we are sending SocketAddr ToThread

        let (sender_ssh_client, receiver_client) = channel::<ToThread<PeerId>>();
        let sender_ssh_client_stop = sender_ssh_client.clone();
        // I might not need that one here
        // maybe to attach some more data
        //let borrow_arc = &self.addresses_found.clone();
        let has_tui_mdns_input = self.has_ui.clone();
        let has_tui_stats = self.has_ui.clone();
        let has_tui_new_client = self.has_ui.clone();

        // to controller messages (mostly tui now)
        let ctrl_sender = self.ui_sender.clone();
        let ctrl_sender2 = self.ui_sender.clone();

        // collection of addresses
        let borrow_arc_connected_clients = self.clients_connected.clone();

        let _get_ip_thread = task::spawn(async move {
            //
            //
            info!("Creating new threads to connecting clients started");
            Self::connect_new_clients(
                receiver_client,
                borrow_arc_connected_clients,
                ctrl_sender,
                has_tui_new_client,
            )
            .await;
        });

        // statistics
        let mut count_valid = 0;
        let mut count_no_cast = 0;

        // prepare everything for mdns thread
        let (mdns_send_peer, mdns_receive_peer) = channel::<ToThread<PeerId>>();

        let mdns_send_stop = mdns_send_peer.clone();

        // take input from mdns_discoper_thread
        // (new ssh client discovered).
        //
        // If client good send sender_ssh_client to
        // _get_ip_thread
        let _take_mdns_input_thread = task::spawn(async move {
            info!("Processing mdns input threads created!");
            Self::take_mdns_input(
                mdns_receive_peer,
                sender_ssh_client,
                ctrl_sender2,
                has_tui_mdns_input,
                // change these
                &mut count_no_cast,
                &mut count_valid,
            );
        });

        Self::async_mdns_discover(mdns_send_peer)
            .and_then(|count_response| async move {
                if !has_tui_stats {
                    let output_string = format!(
                        "no response from : {no_resp:>width$}\n\
                         not castable     : {no_cast:>width$}\n",
                        no_resp = count_response,
                        no_cast = count_no_cast,
                        width = 3
                    );
                    info!("{}", output_string);
                }
                Ok(())
            })
            .await
            .unwrap();

        // other thread should not wait for new messages after all
        sender_ssh_client_stop.send(ToThread::Stop).unwrap();
        mdns_send_stop.send(ToThread::Stop).unwrap();
    }

    /// Discovers mdns on the net and should have a whole
    /// process with discovered clients to share data.
    async fn async_mdns_discover(
        _mdns_send_ip: std::sync::mpsc::Sender<ToThread<PeerId>>,
    ) -> Result<usize, std::io::Error> {
        let mut count_no_response: usize = 0;

        let mut service = MdnsService::new()?;
        info!("Started Mdns Service!");

        async move {
            info!("Starting Mdns Looping!");
            loop {
                info!("taking package ...");
                let (srv, packet) = service.next().await;
                match packet {
                    MdnsPacket::Query(query) => {
                        // We detected a libp2p mDNS query on the network. In a real application, you
                        // probably want to answer this query by doing `query.respond(...)`.
                        info!("Detected query from {:?}", query.remote_addr());
                    }
                    MdnsPacket::Response(response) => {
                        // We detected a libp2p mDNS response on the network. Responses are for
                        // everyone and not just for the requester, which makes it possible to
                        // passively listen.
                        for peer in response.discovered_peers() {
                            info!("Discovered peer {:?}", peer.id());
                            // These are the self-reported addresses of the peer we just discovered.
                            for addr in peer.addresses() {
                                println!(" Address = {:?}", addr);
                            }
                            // mdns_send_ip  std::sync::mpsc::Sender<net::ToThread<SocketAddr>>
                            count_no_response += 1;
                        }
                    }
                    MdnsPacket::ServiceDiscovery(query) => {
                        // The last possibility is a service detection query from DNS-SD.
                        // Just like `Query`, in a real application you probably want to call
                        // `query.respond`.
                        info!("Detected service query from {:?}", query.remote_addr());
                    }
                }
                service = srv
            }
        }
        .await;
        Ok(count_no_response)
    }

    fn take_mdns_input(
        mdns_receive_ip: std::sync::mpsc::Receiver<ToThread<PeerId>>,
        sender_ssh_client: std::sync::mpsc::Sender<ToThread<PeerId>>,
        ctrl_sender: std::sync::mpsc::Sender<ctrl::UiUpdateMsg>,
        has_tui: bool,
        count_no_cast: &mut usize,
        count_valid: &mut usize,
    ) {
        loop {
            match mdns_receive_ip.recv() {
                Ok(good) => {
                    match good {
                        ToThread::Data(recv_mesg) => {
                            *count_valid += 1;
                            sender_ssh_client
                                .send(ToThread::Data(recv_mesg.clone()))
                                .unwrap();
                        }
                        ToThread::Stop => {
                            break; //break loop
                        }
                    }
                }
                Err(_) => {
                    *count_no_cast += 1;
                    break; //break loop
                }
            }
        } // loop ended
          // finally stop animation
        if has_tui {
            info!("Stop animation!");
            ctrl_sender
                .send(ctrl::UiUpdateMsg::CollectionUpdate(
                    ctrl::CollectionPathAlive::HostSearch,
                    ctrl::Status::OFF,
                ))
                .unwrap();
        }
    }

    async fn connect_new_clients(
        receiver_client: std::sync::mpsc::Receiver<ToThread<PeerId>>,
        clients_connected: Arc<Mutex<Vec<PeerId>>>,
        ctrl_sender: std::sync::mpsc::Sender<ctrl::UiUpdateMsg>,
        has_tui: bool,
    ) {
        loop {
            // wait for message
            if let Ok(recv_mesg) = receiver_client.recv() {
                match recv_mesg {
                    ToThread::Data(incoming_id) => {
                        //
                        // input address
                        //
                        let to_push_into_arc_clone = incoming_id.clone();
                        if let Some(mut guard) = clients_connected.try_lock() {
                            let ip_addresses = &mut *guard;
                            // search if ip address is already collected
                            if ip_addresses
                                .iter()
                                .all(|incoming_peer| *incoming_peer != incoming_id)
                            {
                                // put into collection to not find again
                                let id_for_processing = to_push_into_arc_clone.clone();
                                ip_addresses.push(to_push_into_arc_clone);

                                let count = ip_addresses.len();
                                if has_tui {
                                    ctrl_sender
                                        .send(ctrl::UiUpdateMsg::NetUpdate((
                                            ctrl::NetMessages::ShowNewHost,
                                            incoming_id.to_string(),
                                        )))
                                        .unwrap();
                                    ctrl_sender
                                        .send(ctrl::UiUpdateMsg::NetUpdate((
                                            ctrl::NetMessages::ShowStats {
                                                show: ctrl::NetStats {
                                                    line: count,
                                                    max: 0, //index,
                                                },
                                            },
                                            String::from(""),
                                        )))
                                        .unwrap();
                                }
                                // create ssh client in new thread
                                let _connect_ip_client_thread = task::spawn(async move {
                                    let connector = ConnectToOther::new(&id_for_processing);
                                    connector.run();
                                });
                            }
                        } else {
                            error!("lock failed!")
                        }
                    }
                    ToThread::Stop => {
                        break; // leave loop and _get_ip_thread
                    }
                }
            } else {
                break; // leave loop and _get_ip_thread
            }
        }
        ()
    }
}

impl Drop for Net {
    fn drop(&mut self) {
        if !self.has_ui {
            println!("Dropping/destroying net");
        }
    }
}
<commit_msg>[cleanup] clean-up and renaming<commit_after>//! The net module is resonsible for the network related parts,
//! the mDNS registering, mDNS search, communication server and client.
//! It also let's us startup and perform everything in yet one step.
mod connect_from;
mod connect_to;
mod data;

pub mod key_keeper;

use self::connect_to::ConnectToOther;
use super::ctrl;
use async_std::{
    sync::{Arc, Mutex},
    task,
};
use futures_util::TryFutureExt;
use libp2p::{
    mdns::{service::MdnsPacket, MdnsService},
    PeerId,
};
use std::{
    self,
    sync::mpsc::{channel, Sender},
};

#[allow(dead_code)]
#[derive(Clone)]
enum ToThread<T: Send + Clone> {
    Data(T),
    Stop,
}

/// The Net component keeps control about everything from net.
///
/// # Arguments
/// * 'peer_id' - the own peer id (to not talk to much with itself ;-))
/// * 'clients_connected' - All clients that are connected
/// * 'has_ui' - if there is an ui present, that would need update messages///
/// * 'ui_sender' - to send out update ui messages
pub struct Net {
    #[allow(dead_code)]
    peer_id: PeerId,
    clients_connected: Arc<Mutex<Vec<PeerId>>>,
    has_ui: bool,
    ui_sender: Sender<ctrl::UiUpdateMsg>,
}

impl Net {
    pub fn new(peer_id: PeerId, has_ui: bool, ui_sender: Sender<ctrl::UiUpdateMsg>) -> Self {
        Net {
            peer_id,
            clients_connected: Arc::new(Mutex::new(Vec::new())),
            has_ui,
            ui_sender,
        }
    }

    /// Lookup yet is the start of the networking.
    /// It looks for possible mDNS clients and spawns
    pub async fn lookup(&mut self) {
        // threads to connect to them.
        // It uses timeouts, checkups.

        // we are sending SocketAddr ToThread

        let (sender_ssh_client, receiver_client) = channel::<ToThread<PeerId>>();
        let sender_ssh_client_stop = sender_ssh_client.clone();
        // I might not need that one here
        // maybe to attach some more data
        //let borrow_arc = &self.addresses_found.clone();
        let has_ui_mdns_input = self.has_ui.clone();
        let has_ui_stats = self.has_ui.clone();
        let has_ui_new_client = self.has_ui.clone();

        // to controller messages (mostly tui now)
        let ui_update_sender = self.ui_sender.clone();

        // collection of addresses
        let borrow_arc_connected_clients = self.clients_connected.clone();

        let _get_ip_thread = task::spawn(async move {
            //
            //
            info!("Creating new threads to connecting clients started");
            Self::connect_new_clients(
                receiver_client,
                borrow_arc_connected_clients,
                ui_update_sender,
                has_ui_new_client,
            )
            .await;
        });

        // statistics
        let mut count_valid = 0;
        let mut count_no_cast = 0;

        // prepare everything for mdns thread
        let (mdns_send_peer, mdns_receive_peer) = channel::<ToThread<PeerId>>();

        let mdns_send_stop = mdns_send_peer.clone();

        let ctrl_sender = self.ui_sender.clone();
        // take input from mdns_discoper_thread
        // (new ssh client discovered).
        //
        // If client good send sender_ssh_client to
        // _get_ip_thread
        let _take_mdns_input_thread = task::spawn(async move {
            info!("Processing mdns input threads created!");
            Self::take_mdns_input(
                mdns_receive_peer,
                sender_ssh_client,
                ctrl_sender,
                has_ui_mdns_input,
                // change these
                &mut count_no_cast,
                &mut count_valid,
            );
        });

        Self::async_mdns_discover(mdns_send_peer)
            .and_then(|count_response| async move {
                if !has_ui_stats {
                    let output_string = format!(
                        "no response from : {no_resp:>width$}\n\
                         not castable     : {no_cast:>width$}\n",
                        no_resp = count_response,
                        no_cast = count_no_cast,
                        width = 3
                    );
                    info!("{}", output_string);
                }
                Ok(())
            })
            .await
            .unwrap();

        // other thread should not wait for new messages after all
        sender_ssh_client_stop.send(ToThread::Stop).unwrap();
        mdns_send_stop.send(ToThread::Stop).unwrap();
    }

    /// Discovers mdns on the net and should have a whole
    /// process with discovered clients to share data.
    async fn async_mdns_discover(
        _mdns_send_ip: std::sync::mpsc::Sender<ToThread<PeerId>>,
    ) -> Result<usize, std::io::Error> {
        let mut count_no_response: usize = 0;

        let mut service = MdnsService::new()?;
        info!("Started Mdns Service!");

        async move {
            info!("Starting Mdns Looping!");
            loop {
                info!("taking package ...");
                let (srv, packet) = service.next().await;
                match packet {
                    MdnsPacket::Query(query) => {
                        // We detected a libp2p mDNS query on the network. In a real application, you
                        // probably want to answer this query by doing `query.respond(...)`.
                        info!("Detected query from {:?}", query.remote_addr());
                    }
                    MdnsPacket::Response(response) => {
                        // We detected a libp2p mDNS response on the network. Responses are for
                        // everyone and not just for the requester, which makes it possible to
                        // passively listen.
                        for peer in response.discovered_peers() {
                            info!("Discovered peer {:?}", peer.id());
                            // These are the self-reported addresses of the peer we just discovered.
                            for addr in peer.addresses() {
                                println!(" Address = {:?}", addr);
                            }
                            // mdns_send_ip  std::sync::mpsc::Sender<net::ToThread<SocketAddr>>
                            count_no_response += 1;
                        }
                    }
                    MdnsPacket::ServiceDiscovery(query) => {
                        // The last possibility is a service detection query from DNS-SD.
                        // Just like `Query`, in a real application you probably want to call
                        // `query.respond`.
                        info!("Detected service query from {:?}", query.remote_addr());
                    }
                }
                service = srv
            }
        }
        .await;
        Ok(count_no_response)
    }

    fn take_mdns_input(
        mdns_receive_ip: std::sync::mpsc::Receiver<ToThread<PeerId>>,
        sender_ssh_client: std::sync::mpsc::Sender<ToThread<PeerId>>,
        ctrl_sender: std::sync::mpsc::Sender<ctrl::UiUpdateMsg>,
        has_tui: bool,
        count_no_cast: &mut usize,
        count_valid: &mut usize,
    ) {
        loop {
            match mdns_receive_ip.recv() {
                Ok(good) => {
                    match good {
                        ToThread::Data(recv_mesg) => {
                            *count_valid += 1;
                            sender_ssh_client
                                .send(ToThread::Data(recv_mesg.clone()))
                                .unwrap();
                        }
                        ToThread::Stop => {
                            break; //break loop
                        }
                    }
                }
                Err(_) => {
                    *count_no_cast += 1;
                    break; //break loop
                }
            }
        } // loop ended
          // finally stop animation
        if has_tui {
            info!("Stop animation!");
            ctrl_sender
                .send(ctrl::UiUpdateMsg::CollectionUpdate(
                    ctrl::CollectionPathAlive::HostSearch,
                    ctrl::Status::OFF,
                ))
                .unwrap();
        }
    }

    async fn connect_new_clients(
        receiver_client: std::sync::mpsc::Receiver<ToThread<PeerId>>,
        clients_connected: Arc<Mutex<Vec<PeerId>>>,
        ctrl_sender: std::sync::mpsc::Sender<ctrl::UiUpdateMsg>,
        has_tui: bool,
    ) {
        loop {
            // wait for message
            if let Ok(recv_mesg) = receiver_client.recv() {
                match recv_mesg {
                    ToThread::Data(incoming_id) => {
                        //
                        // input address
                        //
                        let to_push_into_arc_clone = incoming_id.clone();
                        if let Some(mut guard) = clients_connected.try_lock() {
                            let ip_addresses = &mut *guard;
                            // search if ip address is already collected
                            if ip_addresses
                                .iter()
                                .all(|incoming_peer| *incoming_peer != incoming_id)
                            {
                                // put into collection to not find again
                                let id_for_processing = to_push_into_arc_clone.clone();
                                ip_addresses.push(to_push_into_arc_clone);

                                let count = ip_addresses.len();
                                if has_tui {
                                    ctrl_sender
                                        .send(ctrl::UiUpdateMsg::NetUpdate((
                                            ctrl::NetMessages::ShowNewHost,
                                            incoming_id.to_string(),
                                        )))
                                        .unwrap();
                                    ctrl_sender
                                        .send(ctrl::UiUpdateMsg::NetUpdate((
                                            ctrl::NetMessages::ShowStats {
                                                show: ctrl::NetStats {
                                                    line: count,
                                                    max: 0, //index,
                                                },
                                            },
                                            String::from(""),
                                        )))
                                        .unwrap();
                                }
                                // create ssh client in new thread
                                let _connect_ip_client_thread = task::spawn(async move {
                                    let connector = ConnectToOther::new(&id_for_processing);
                                    connector.run();
                                });
                            }
                        } else {
                            error!("lock failed!")
                        }
                    }
                    ToThread::Stop => {
                        break; // leave loop and _get_ip_thread
                    }
                }
            } else {
                break; // leave loop and _get_ip_thread
            }
        }
        ()
    }
}

impl Drop for Net {
    fn drop(&mut self) {
        if !self.has_ui {
            println!("Dropping/destroying net");
        }
    }
}
<|endoftext|>


<commit_before>use std::str::from_utf8;
use std::io::{IoError, IoResult, InvalidInput};
use std::io::net::ip::IpAddr;
use std::io::net::get_host_addresses;
use libc::{c_int, size_t, c_char};

extern {
    pub fn gethostname(name: *mut c_char, size: size_t) -> c_int;
}

pub fn get_host_ip() -> IoResult<IpAddr> {
    let host = try!(get_host_name());
    println!("NAME {}", host);
    let addr = try!(get_host_addresses(host.as_slice()));
    println!("ADDR {}", addr);
    return Ok(addr[0]);
}

pub fn get_host_name() -> IoResult<String> {
    let mut buf: Vec<u8> = Vec::with_capacity(256);
    let nbytes = unsafe {
        buf.set_len(256);
        gethostname(
            buf.as_mut_slice().as_mut_ptr() as *mut i8,
            256)
    };
    if nbytes != 0 {
        return Err(IoError::last_error());
    }
    return buf.as_slice().splitn(1, |x| *x == 0u8)
           .next().and_then(from_utf8).map(|x| x.to_string())
           .ok_or(IoError {
                kind: InvalidInput,
                desc: "Got invalid hostname from OS",
                detail: None,
            });
}
<commit_msg>Remove unuseful debug<commit_after>use std::str::from_utf8;
use std::io::{IoError, IoResult, InvalidInput};
use std::io::net::ip::IpAddr;
use std::io::net::get_host_addresses;
use libc::{c_int, size_t, c_char};

extern {
    pub fn gethostname(name: *mut c_char, size: size_t) -> c_int;
}

pub fn get_host_ip() -> IoResult<IpAddr> {
    let host = try!(get_host_name());
    let addr = try!(get_host_addresses(host.as_slice()));
    return Ok(addr[0]);
}

pub fn get_host_name() -> IoResult<String> {
    let mut buf: Vec<u8> = Vec::with_capacity(256);
    let nbytes = unsafe {
        buf.set_len(256);
        gethostname(
            buf.as_mut_slice().as_mut_ptr() as *mut i8,
            256)
    };
    if nbytes != 0 {
        return Err(IoError::last_error());
    }
    return buf.as_slice().splitn(1, |x| *x == 0u8)
           .next().and_then(from_utf8).map(|x| x.to_string())
           .ok_or(IoError {
                kind: InvalidInput,
                desc: "Got invalid hostname from OS",
                detail: None,
            });
}
<|endoftext|>