<commit_before>use super::*;
use serde::{Deserialize, Serialize};

/// Defines the class of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum GeoClass {
    /// Lines (segments) class
    Lin,

    /// Triangles class
    Tri,

    /// Quadrilaterals class
    Qua,

    /// Tetrahedra class
    Tet,

    /// Hexahedra class
    Hex,
}

/// Defines the kind of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum GeoKind {
    /// Line (segment) with 2 nodes (linear functions)
    Lin2 = 1_002,

    /// Line (segment) with 3 nodes (quadratic functions)
    Lin3 = 1_003,

    /// Line (segment) with 4 nodes (cubic functions)
    Lin4 = 1_004,

    /// Line (segment) with 5 nodes (quartic functions)
    Lin5 = 1_005,

    /// Triangle with 3 nodes (linear edges)
    Tri3 = 2_003,

    /// Triangle with 6 nodes (quadratic edges)
    Tri6 = 2_006,

    /// Triangle with 10 nodes (cubic edges; interior node)
    Tri10 = 2_010,

    /// Triangle with 15 nodes (quartic edges; interior nodes)
    Tri15 = 2_015,

    /// Quadrilateral with 4 nodes (linear edges)
    Qua4 = 2_004,

    /// Quadrilateral with 8 nodes (quadratic edges)
    Qua8 = 2_008,

    /// Quadrilateral with 9 nodes (quadratic edges; interior node)
    Qua9 = 2_009,

    /// Quadrilateral with 12 nodes (cubic edges)
    Qua12 = 2_012,

    /// Quadrilateral with 16 nodes (cubic edges; interior nodes)
    Qua16 = 2_016,

    /// Quadrilateral with 17 nodes (quartic edges; interior node)
    Qua17 = 2_017,

    /// Tetrahedron with 4 nodes (linear faces)
    Tet4 = 3_004,

    /// Tetrahedron with 10 nodes (quadratic faces)
    Tet10 = 3_010,

    /// Hexahedron with 8 nodes (bilinear faces)
    Hex8 = 3_008,

    /// Hexahedron with 20 nodes (quadratic faces)
    Hex20 = 3_020,
}

impl GeoKind {
    /// Holds all enum values
    pub const VALUES: [Self; 18] = [
        // Lin
        Self::Lin2,
        Self::Lin3,
        Self::Lin4,
        Self::Lin5,
        // Tri
        Self::Tri3,
        Self::Tri6,
        Self::Tri10,
        Self::Tri15,
        // Qua
        Self::Qua4,
        Self::Qua8,
        Self::Qua9,
        Self::Qua12,
        Self::Qua16,
        Self::Qua17,
        // Tet
        Self::Tet4,
        Self::Tet10,
        // Hex
        Self::Hex8,
        Self::Hex20,
    ];
}

pub(crate) fn i32_to_fn_interp(kind: i32) -> FnInterp {
    match kind {
        // Lin
        1_002 => FnInterp(GeoKind::Lin2, Lin2::calc_interp),
        1_003 => FnInterp(GeoKind::Lin3, Lin3::calc_interp),
        1_004 => FnInterp(GeoKind::Lin4, Lin4::calc_interp),
        1_005 => FnInterp(GeoKind::Lin5, Lin5::calc_interp),
        // Tri
        2_003 => FnInterp(GeoKind::Tri3, Tri3::calc_interp),
        2_006 => FnInterp(GeoKind::Tri6, Tri6::calc_interp),
        2_010 => FnInterp(GeoKind::Tri10, Tri10::calc_interp),
        2_015 => FnInterp(GeoKind::Tri15, Tri15::calc_interp),
        // Qua
        2_004 => FnInterp(GeoKind::Qua4, Qua4::calc_interp),
        2_008 => FnInterp(GeoKind::Qua8, Qua8::calc_interp),
        2_009 => FnInterp(GeoKind::Qua9, Qua9::calc_interp),
        2_012 => FnInterp(GeoKind::Qua12, Qua12::calc_interp),
        2_016 => FnInterp(GeoKind::Qua16, Qua16::calc_interp),
        2_017 => FnInterp(GeoKind::Qua17, Qua17::calc_interp),
        // Tet
        3_004 => FnInterp(GeoKind::Tet4, Tet4::calc_interp),
        3_010 => FnInterp(GeoKind::Tet10, Tet10::calc_interp),
        // Hex
        3_008 => FnInterp(GeoKind::Hex8, Hex8::calc_interp),
        3_020 => FnInterp(GeoKind::Hex20, Hex20::calc_interp),
        _ => panic!("INTERNAL ERROR: cannot convert i32 to FnInterp"),
    }
}

pub(crate) fn i32_to_fn_deriv(kind: i32) -> FnDeriv {
    match kind {
        // Lin
        1_002 => FnDeriv(GeoKind::Lin2, Lin2::calc_deriv),
        1_003 => FnDeriv(GeoKind::Lin3, Lin3::calc_deriv),
        1_004 => FnDeriv(GeoKind::Lin4, Lin4::calc_deriv),
        1_005 => FnDeriv(GeoKind::Lin5, Lin5::calc_deriv),
        // Tri
        2_003 => FnDeriv(GeoKind::Tri3, Tri3::calc_deriv),
        2_006 => FnDeriv(GeoKind::Tri6, Tri6::calc_deriv),
        2_010 => FnDeriv(GeoKind::Tri10, Tri10::calc_deriv),
        2_015 => FnDeriv(GeoKind::Tri15, Tri15::calc_deriv),
        // Qua
        2_004 => FnDeriv(GeoKind::Qua4, Qua4::calc_deriv),
        2_008 => FnDeriv(GeoKind::Qua8, Qua8::calc_deriv),
        2_009 => FnDeriv(GeoKind::Qua9, Qua9::calc_deriv),
        2_012 => FnDeriv(GeoKind::Qua12, Qua12::calc_deriv),
        2_016 => FnDeriv(GeoKind::Qua16, Qua16::calc_deriv),
        2_017 => FnDeriv(GeoKind::Qua17, Qua17::calc_deriv),
        // Tet
        3_004 => FnDeriv(GeoKind::Tet4, Tet4::calc_deriv),
        3_010 => FnDeriv(GeoKind::Tet10, Tet10::calc_deriv),
        // Hex
        3_008 => FnDeriv(GeoKind::Hex8, Hex8::calc_deriv),
        3_020 => FnDeriv(GeoKind::Hex20, Hex20::calc_deriv),
        _ => panic!("INTERNAL ERROR: cannot convert i32 to FnDeriv"),
    }
}

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

#[cfg(test)]
mod tests {
    use super::{i32_to_fn_deriv, i32_to_fn_interp};
    use crate::shapes::{GeoClass, GeoKind};
    use std::collections::HashSet;

    #[test]
    fn derive_works() {
        let class = GeoClass::Tri.clone();
        let kind = GeoKind::Tri6.clone();
        let classes = HashSet::from([GeoClass::Tri, GeoClass::Qua]);
        let kinds = HashSet::from([GeoKind::Tri3, GeoKind::Qua4]);
        assert_eq!(class, GeoClass::Tri);
        assert_eq!(kind, GeoKind::Tri6);
        assert_eq!(format!("{:?}", class), "Tri");
        assert_eq!(format!("{:?}", kind), "Tri6");
        assert_eq!(classes.contains(&GeoClass::Tri), true);
        assert_eq!(kinds.contains(&GeoKind::Tri3), true);
    }

    #[test]
    fn data_is_consistent() {
        for kind in GeoKind::VALUES {
            match kind {
                // Lin
                GeoKind::Lin2 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 2);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin2);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin2);
                }
                GeoKind::Lin3 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 3);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin3);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin3);
                }
                GeoKind::Lin4 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 4);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin4);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin4);
                }
                GeoKind::Lin5 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 5);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin5);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin5);
                }

                // Tri
                GeoKind::Tri3 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 3);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri3);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri3);
                }
                GeoKind::Tri6 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 6);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri6);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri6);
                }
                GeoKind::Tri10 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 10);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri10);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri10);
                }
                GeoKind::Tri15 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 15);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri15);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri15);
                }

                // Qua
                GeoKind::Qua4 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 4);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua4);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua4);
                }
                GeoKind::Qua8 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 8);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua8);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua8);
                }
                GeoKind::Qua9 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 9);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua9);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua9);
                }
                GeoKind::Qua12 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 12);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua12);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua12);
                }
                GeoKind::Qua16 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 16);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua16);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua16);
                }
                GeoKind::Qua17 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 17);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua17);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua17);
                }

                // Tet
                GeoKind::Tet4 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 4);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tet4);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tet4);
                }
                GeoKind::Tet10 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 10);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tet10);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tet10);
                }

                // Hex
                GeoKind::Hex8 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 8);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Hex8);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Hex8);
                }
                GeoKind::Hex20 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 20);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Hex20);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Hex20);
                }
            }
        }
    }
}
<commit_msg>Add tests<commit_after>use super::*;
use serde::{Deserialize, Serialize};

/// Defines the class of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum GeoClass {
    /// Lines (segments) class
    Lin,

    /// Triangles class
    Tri,

    /// Quadrilaterals class
    Qua,

    /// Tetrahedra class
    Tet,

    /// Hexahedra class
    Hex,
}

/// Defines the kind of geometric shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub enum GeoKind {
    /// Line (segment) with 2 nodes (linear functions)
    Lin2 = 1_002,

    /// Line (segment) with 3 nodes (quadratic functions)
    Lin3 = 1_003,

    /// Line (segment) with 4 nodes (cubic functions)
    Lin4 = 1_004,

    /// Line (segment) with 5 nodes (quartic functions)
    Lin5 = 1_005,

    /// Triangle with 3 nodes (linear edges)
    Tri3 = 2_003,

    /// Triangle with 6 nodes (quadratic edges)
    Tri6 = 2_006,

    /// Triangle with 10 nodes (cubic edges; interior node)
    Tri10 = 2_010,

    /// Triangle with 15 nodes (quartic edges; interior nodes)
    Tri15 = 2_015,

    /// Quadrilateral with 4 nodes (linear edges)
    Qua4 = 2_004,

    /// Quadrilateral with 8 nodes (quadratic edges)
    Qua8 = 2_008,

    /// Quadrilateral with 9 nodes (quadratic edges; interior node)
    Qua9 = 2_009,

    /// Quadrilateral with 12 nodes (cubic edges)
    Qua12 = 2_012,

    /// Quadrilateral with 16 nodes (cubic edges; interior nodes)
    Qua16 = 2_016,

    /// Quadrilateral with 17 nodes (quartic edges; interior node)
    Qua17 = 2_017,

    /// Tetrahedron with 4 nodes (linear faces)
    Tet4 = 3_004,

    /// Tetrahedron with 10 nodes (quadratic faces)
    Tet10 = 3_010,

    /// Hexahedron with 8 nodes (bilinear faces)
    Hex8 = 3_008,

    /// Hexahedron with 20 nodes (quadratic faces)
    Hex20 = 3_020,
}

impl GeoKind {
    /// Holds all enum values
    pub const VALUES: [Self; 18] = [
        // Lin
        Self::Lin2,
        Self::Lin3,
        Self::Lin4,
        Self::Lin5,
        // Tri
        Self::Tri3,
        Self::Tri6,
        Self::Tri10,
        Self::Tri15,
        // Qua
        Self::Qua4,
        Self::Qua8,
        Self::Qua9,
        Self::Qua12,
        Self::Qua16,
        Self::Qua17,
        // Tet
        Self::Tet4,
        Self::Tet10,
        // Hex
        Self::Hex8,
        Self::Hex20,
    ];
}

pub(crate) fn i32_to_fn_interp(kind: i32) -> FnInterp {
    match kind {
        // Lin
        1_002 => FnInterp(GeoKind::Lin2, Lin2::calc_interp),
        1_003 => FnInterp(GeoKind::Lin3, Lin3::calc_interp),
        1_004 => FnInterp(GeoKind::Lin4, Lin4::calc_interp),
        1_005 => FnInterp(GeoKind::Lin5, Lin5::calc_interp),
        // Tri
        2_003 => FnInterp(GeoKind::Tri3, Tri3::calc_interp),
        2_006 => FnInterp(GeoKind::Tri6, Tri6::calc_interp),
        2_010 => FnInterp(GeoKind::Tri10, Tri10::calc_interp),
        2_015 => FnInterp(GeoKind::Tri15, Tri15::calc_interp),
        // Qua
        2_004 => FnInterp(GeoKind::Qua4, Qua4::calc_interp),
        2_008 => FnInterp(GeoKind::Qua8, Qua8::calc_interp),
        2_009 => FnInterp(GeoKind::Qua9, Qua9::calc_interp),
        2_012 => FnInterp(GeoKind::Qua12, Qua12::calc_interp),
        2_016 => FnInterp(GeoKind::Qua16, Qua16::calc_interp),
        2_017 => FnInterp(GeoKind::Qua17, Qua17::calc_interp),
        // Tet
        3_004 => FnInterp(GeoKind::Tet4, Tet4::calc_interp),
        3_010 => FnInterp(GeoKind::Tet10, Tet10::calc_interp),
        // Hex
        3_008 => FnInterp(GeoKind::Hex8, Hex8::calc_interp),
        3_020 => FnInterp(GeoKind::Hex20, Hex20::calc_interp),
        _ => panic!("INTERNAL ERROR: cannot convert i32 to FnInterp"),
    }
}

pub(crate) fn i32_to_fn_deriv(kind: i32) -> FnDeriv {
    match kind {
        // Lin
        1_002 => FnDeriv(GeoKind::Lin2, Lin2::calc_deriv),
        1_003 => FnDeriv(GeoKind::Lin3, Lin3::calc_deriv),
        1_004 => FnDeriv(GeoKind::Lin4, Lin4::calc_deriv),
        1_005 => FnDeriv(GeoKind::Lin5, Lin5::calc_deriv),
        // Tri
        2_003 => FnDeriv(GeoKind::Tri3, Tri3::calc_deriv),
        2_006 => FnDeriv(GeoKind::Tri6, Tri6::calc_deriv),
        2_010 => FnDeriv(GeoKind::Tri10, Tri10::calc_deriv),
        2_015 => FnDeriv(GeoKind::Tri15, Tri15::calc_deriv),
        // Qua
        2_004 => FnDeriv(GeoKind::Qua4, Qua4::calc_deriv),
        2_008 => FnDeriv(GeoKind::Qua8, Qua8::calc_deriv),
        2_009 => FnDeriv(GeoKind::Qua9, Qua9::calc_deriv),
        2_012 => FnDeriv(GeoKind::Qua12, Qua12::calc_deriv),
        2_016 => FnDeriv(GeoKind::Qua16, Qua16::calc_deriv),
        2_017 => FnDeriv(GeoKind::Qua17, Qua17::calc_deriv),
        // Tet
        3_004 => FnDeriv(GeoKind::Tet4, Tet4::calc_deriv),
        3_010 => FnDeriv(GeoKind::Tet10, Tet10::calc_deriv),
        // Hex
        3_008 => FnDeriv(GeoKind::Hex8, Hex8::calc_deriv),
        3_020 => FnDeriv(GeoKind::Hex20, Hex20::calc_deriv),
        _ => panic!("INTERNAL ERROR: cannot convert i32 to FnDeriv"),
    }
}

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

#[cfg(test)]
mod tests {
    use super::{i32_to_fn_deriv, i32_to_fn_interp};
    use crate::shapes::{GeoClass, GeoKind};
    use std::collections::HashSet;

    #[test]
    fn derive_works() {
        let class = GeoClass::Tri.clone();
        let kind = GeoKind::Tri6.clone();
        let classes = HashSet::from([GeoClass::Tri, GeoClass::Qua]);
        let kinds = HashSet::from([GeoKind::Tri3, GeoKind::Qua4]);
        assert_eq!(class, GeoClass::Tri);
        assert_eq!(kind, GeoKind::Tri6);
        assert_eq!(format!("{:?}", class), "Tri");
        assert_eq!(format!("{:?}", kind), "Tri6");
        assert_eq!(classes.contains(&GeoClass::Tri), true);
        assert_eq!(kinds.contains(&GeoKind::Tri3), true);
    }

    #[test]
    #[should_panic(expected = "INTERNAL ERROR: cannot convert i32 to FnInterp")]
    fn i32_to_fn_interp_panics_on_wrong_input() {
        i32_to_fn_interp(-1);
    }

    #[test]
    #[should_panic(expected = "INTERNAL ERROR: cannot convert i32 to FnDeriv")]
    fn i32_to_fn_deriv_panics_on_wrong_input() {
        i32_to_fn_deriv(-1);
    }

    #[test]
    fn data_is_consistent() {
        for kind in GeoKind::VALUES {
            match kind {
                // Lin
                GeoKind::Lin2 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 2);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin2);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin2);
                }
                GeoKind::Lin3 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 3);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin3);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin3);
                }
                GeoKind::Lin4 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 4);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin4);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin4);
                }
                GeoKind::Lin5 => {
                    let k = kind as i32;
                    let nnode = k - 1000;
                    assert_eq!(nnode, 5);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Lin5);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Lin5);
                }

                // Tri
                GeoKind::Tri3 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 3);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri3);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri3);
                }
                GeoKind::Tri6 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 6);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri6);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri6);
                }
                GeoKind::Tri10 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 10);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri10);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri10);
                }
                GeoKind::Tri15 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 15);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tri15);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tri15);
                }

                // Qua
                GeoKind::Qua4 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 4);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua4);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua4);
                }
                GeoKind::Qua8 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 8);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua8);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua8);
                }
                GeoKind::Qua9 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 9);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua9);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua9);
                }
                GeoKind::Qua12 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 12);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua12);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua12);
                }
                GeoKind::Qua16 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 16);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua16);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua16);
                }
                GeoKind::Qua17 => {
                    let k = kind as i32;
                    let nnode = k - 2000;
                    assert_eq!(nnode, 17);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Qua17);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Qua17);
                }

                // Tet
                GeoKind::Tet4 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 4);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tet4);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tet4);
                }
                GeoKind::Tet10 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 10);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Tet10);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Tet10);
                }

                // Hex
                GeoKind::Hex8 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 8);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Hex8);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Hex8);
                }
                GeoKind::Hex20 => {
                    let k = kind as i32;
                    let nnode = k - 3000;
                    assert_eq!(nnode, 20);
                    assert_eq!(i32_to_fn_interp(k).0, GeoKind::Hex20);
                    assert_eq!(i32_to_fn_deriv(k).0, GeoKind::Hex20);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Models about OAuth2 applications.

use std::fmt;

use super::id::{snowflake, ApplicationId, UserId};
use super::{user::User, utils::*};

/// Information about a user's application. An application does not necessarily
/// have an associated bot user.
#[derive(Clone, Deserialize, Serialize)]
#[non_exhaustive]
pub struct ApplicationInfo {
    /// The bot user associated with the application. See [`BotApplication`] for
    /// more information.
    pub bot: Option<BotApplication>,
    /// Indicator of whether the bot is public.
    ///
    /// If a bot is public, anyone may invite it to their [`Guild`]. While a bot
    /// is private, only the owner may add it to a guild.
    ///
    /// [`Guild`]: super::guild::Guild
    #[serde(default = "default_true")]
    pub bot_public: bool,
    /// Indicator of whether the bot requires an OAuth2 code grant.
    pub bot_require_code_grant: bool,
    /// A description of the application, assigned by the application owner.
    pub description: String,
    /// A set of bitflags assigned to the application, which represent gated
    /// feature flags that have been enabled for the application.
    pub flags: Option<ApplicationFlags>,
    /// A hash pointing to the application's icon.
    ///
    /// This is not necessarily equivalent to the bot user's avatar.
    pub icon: Option<String>,
    /// The unique numeric Id of the application.
    pub id: ApplicationId,
    /// The name assigned to the application by the application owner.
    pub name: String,
    /// A list of redirect URIs assigned to the application.
    pub redirect_uris: Vec<String>,
    /// A list of RPC Origins assigned to the application.
    pub rpc_origins: Vec<String>,
    /// The application team group.
    pub team: Option<Vec<Team>>,
    /// The given secret to the application.
    ///
    /// This is not equivalent to the application's bot user's token.
    pub secret: String,
}

impl fmt::Debug for ApplicationInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ApplicationInfo")
            .field("bot", &self.bot)
            .field("bot_public", &self.bot_public)
            .field("bot_require_code_grant", &self.bot_require_code_grant)
            .field("description", &self.description)
            .field("flags", &self.flags)
            .field("icon", &self.icon)
            .field("id", &self.id)
            .field("name", &self.name)
            .field("redirect_uris", &self.redirect_uris)
            .field("rpc_origins", &self.rpc_origins)
            .field("team", &self.team)
            .finish()
    }
}

/// Information about an application with an application's bot user.
#[derive(Clone, Deserialize, Serialize)]
#[non_exhaustive]
pub struct BotApplication {
    /// The unique Id of the bot user.
    pub id: UserId,
    /// A hash of the avatar, if one is assigned.
    ///
    /// Can be used to generate a full URL to the avatar.
    pub avatar: Option<String>,
    /// Indicator of whether it is a bot.
    #[serde(default)]
    pub bot: bool,
    /// The discriminator assigned to the bot user.
    ///
    /// While discriminators are not unique, the `username#discriminator` pair
    /// is.
    pub discriminator: u16,
    /// The bot user's username.
    pub name: String,
    /// The token used to authenticate as the bot user.
    ///
    /// **Note**: Keep this information private, as untrusted sources can use it
    /// to perform any action with a bot user.
    pub token: String,
}

impl fmt::Debug for BotApplication {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("BotApplication")
            .field("id", &self.id)
            .field("avatar", &self.avatar)
            .field("bot", &self.bot)
            .field("discriminator", &self.discriminator)
            .field("name", &self.name)
            .finish()
    }
}

/// Partial information about the given application.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PartialCurrentApplicationInfo {
    /// The unique Id of the user.
    pub id: ApplicationId,
    /// The flags associated with the application.
    pub flags: ApplicationFlags,
}

/// Information about the current application and its owner.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct CurrentApplicationInfo {
    pub description: String,
    pub icon: Option<String>,
    pub id: ApplicationId,
    pub name: String,
    pub owner: User,
    #[serde(default)]
    pub rpc_origins: Vec<String>,
    pub bot_public: bool,
    pub bot_require_code_grant: bool,
    pub team: Option<Team>,
}

/// Information about the Team group of the application.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Team {
    /// The icon of the team.
    pub icon: Option<String>,
    /// The snowflake ID of the team.
    #[serde(with = "snowflake")]
    pub id: u64,
    /// The name of the team.
    pub name: String,
    /// The members of the team
    pub members: Vec<TeamMember>,
    /// The user id of the team owner.
    pub owner_user_id: UserId,
}

/// Information about a Member on a Team.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TeamMember {
    /// The member's membership state.
    pub membership_state: MembershipState,
    /// The list of permissions of the member on the team.
    ///
    /// NOTE: Will always be ["*"] for now.
    pub permissions: Vec<String>,
    /// The ID of the team they are a member of.
    #[serde(with = "snowflake")]
    pub team_id: u64,
    /// The user type of the team member.
    pub user: User,
}

#[derive(Clone, Debug, Copy, PartialEq)]
pub enum MembershipState {
    Invited = 1,
    Accepted = 2,
    Unknown = !0,
}

enum_number!(MembershipState {
    Invited,
    Accepted
});

bitflags! {
    /// The flags of the application.
    #[derive(Default)]
    pub struct ApplicationFlags: u64 {
        const GATEWAY_PRESENCE = 1 << 12;
        const GATEWAY_PRESENCE_LIMITED = 1 << 13;
        const GATEWAY_GUILD_MEMBERS = 1 << 14;
        const GATEWAY_GUILD_MEMBERS_LIMITED = 1 << 15;
        const VERIFICATION_PENDING_GUILD_LIMIT = 2 << 16;
        const EMBEDDED = 2 << 17;
        const GATEWAY_MESSAGE_CONTENT = 2 << 18;
        const GATEWAY_MESSAGE_CONTENT_LIMITED = 2 << 19;
    }
}
<commit_msg>Remove unused `ApplicationInfo` & `BotApplication` structs (#1796)<commit_after>//! Models about OAuth2 applications.

use super::id::{snowflake, ApplicationId, UserId};
use super::user::User;

/// Partial information about the given application.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PartialCurrentApplicationInfo {
    /// The unique Id of the user.
    pub id: ApplicationId,
    /// The flags associated with the application.
    pub flags: ApplicationFlags,
}

/// Information about the current application and its owner.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct CurrentApplicationInfo {
    pub description: String,
    pub icon: Option<String>,
    pub id: ApplicationId,
    pub name: String,
    pub owner: User,
    #[serde(default)]
    pub rpc_origins: Vec<String>,
    pub bot_public: bool,
    pub bot_require_code_grant: bool,
    pub team: Option<Team>,
}

/// Information about the Team group of the application.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Team {
    /// The icon of the team.
    pub icon: Option<String>,
    /// The snowflake ID of the team.
    #[serde(with = "snowflake")]
    pub id: u64,
    /// The name of the team.
    pub name: String,
    /// The members of the team
    pub members: Vec<TeamMember>,
    /// The user id of the team owner.
    pub owner_user_id: UserId,
}

/// Information about a Member on a Team.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TeamMember {
    /// The member's membership state.
    pub membership_state: MembershipState,
    /// The list of permissions of the member on the team.
    ///
    /// NOTE: Will always be ["*"] for now.
    pub permissions: Vec<String>,
    /// The ID of the team they are a member of.
    #[serde(with = "snowflake")]
    pub team_id: u64,
    /// The user type of the team member.
    pub user: User,
}

#[derive(Clone, Debug, Copy, PartialEq)]
pub enum MembershipState {
    Invited = 1,
    Accepted = 2,
    Unknown = !0,
}

enum_number!(MembershipState {
    Invited,
    Accepted
});

bitflags! {
    /// The flags of the application.
    #[derive(Default)]
    pub struct ApplicationFlags: u64 {
        const GATEWAY_PRESENCE = 1 << 12;
        const GATEWAY_PRESENCE_LIMITED = 1 << 13;
        const GATEWAY_GUILD_MEMBERS = 1 << 14;
        const GATEWAY_GUILD_MEMBERS_LIMITED = 1 << 15;
        const VERIFICATION_PENDING_GUILD_LIMIT = 2 << 16;
        const EMBEDDED = 2 << 17;
        const GATEWAY_MESSAGE_CONTENT = 2 << 18;
        const GATEWAY_MESSAGE_CONTENT_LIMITED = 2 << 19;
    }
}
<|endoftext|>


<commit_before>extern crate libloading as lib;
extern crate rand;
extern crate test_server;

use std::fs;
use std::path::Path;
use std::str;
use test_server::futures::{Future, Stream};
use test_server::hyper;

#[cfg(target_os = "windows")]
const LOG_FILE_PATH: &str = "ctehxk2.log";
#[cfg(not(target_os = "windows"))]
const LOG_FILE_PATH: &str = "libctehxk2.log";

#[cfg(target_os = "windows")]
const LIB_PATH: &str = "../../target/debug/ctehxk2.dll";
#[cfg(target_os = "linux")]
const LIB_PATH: &str = "../../target/debug/libctehxk2.so";
#[cfg(target_os = "macos")]
const LIB_PATH: &str = "../../target/debug/libctehxk2.dylib";

#[test]
fn with_config_file() {
    let server = test_server::serve(Some(String::from("127.0.0.1:65432")));

    server.reply().status(hyper::Ok).body("0");

    match lib::Library::new(LIB_PATH) {
        Ok(lib) => unsafe {
            let init: lib::Symbol<unsafe extern "C" fn(u16, u16) -> i8> =
                lib.get(b"CT_init").unwrap();

            let ctn = rand::random::<u16>();
            let pn = rand::random::<u16>();

            assert_eq!(0, init(ctn, pn));
        },
        _ => assert!(false, format!("loading library from {}", LIB_PATH)),
    }

    let (method, uri, _version, _headers, body) = server.request().unwrap().deconstruct();

    assert_eq!(hyper::Method::Post, method);
    assert_eq!("/yaml/ct_init/17/321", uri.path());
    assert!(body.concat2().wait().unwrap().is_empty());

    assert!(Path::new(LOG_FILE_PATH).exists());

    let metadata = fs::metadata(LOG_FILE_PATH).unwrap();
    assert!(metadata.len() > 0);
}
<commit_msg>remove one unsafe<commit_after>extern crate libloading;
extern crate rand;
extern crate test_server;

use std::fs;
use std::path::Path;
use std::str;
use test_server::futures::{Future, Stream};
use test_server::hyper;

#[cfg(target_os = "windows")]
const LOG_FILE_PATH: &str = "ctehxk2.log";
#[cfg(not(target_os = "windows"))]
const LOG_FILE_PATH: &str = "libctehxk2.log";

#[cfg(target_os = "windows")]
const LIB_PATH: &str = "../../target/debug/ctehxk2.dll";
#[cfg(target_os = "linux")]
const LIB_PATH: &str = "../../target/debug/libctehxk2.so";
#[cfg(target_os = "macos")]
const LIB_PATH: &str = "../../target/debug/libctehxk2.dylib";

#[test]
fn with_config_file() {
    let server = test_server::serve(Some(String::from("127.0.0.1:65432")));
    server.reply().status(hyper::Ok).body("0");

    match libloading::Library::new(LIB_PATH) {
        Ok(lib) => {
            let init: libloading::Symbol<fn(u16, u16) -> i8> =
                unsafe { lib.get(b"CT_init").unwrap() };

            let ctn = rand::random::<u16>();
            let pn = rand::random::<u16>();

            assert_eq!(0, init(ctn, pn));
        }
        _ => assert!(false, format!("loading library from {}", LIB_PATH)),
    }

    let (method, uri, _version, _headers, body) = server.request().unwrap().deconstruct();

    assert_eq!(hyper::Method::Post, method);
    assert_eq!("/yaml/ct_init/17/321", uri.path());
    assert!(body.concat2().wait().unwrap().is_empty());

    assert!(Path::new(LOG_FILE_PATH).exists());

    let metadata = fs::metadata(LOG_FILE_PATH).unwrap();
    assert!(metadata.len() > 0);
}
<|endoftext|>


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

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Clone, Debug)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(PartialEq, Clone, Debug)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TaggedType<'a> = (Expression<'a>, Type);

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            // TODO: args -> types. modify the environment
            let body_ty = w(body, env, inst);
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        // TODO: Need unification algo here.
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            match w(callable, env, inst) {
                Type::Lambda(expected, body_type) => {
                    if args.len() != expected.len() {
                        panic!("wrong number of args given, {} for {}", args.len(), expected.len())
                    } else if expected != type_args.as_slice() {
                        panic!("bad types given: {:?} for {:?}", type_args.as_slice(), expected)
                    }

                    *body_type.clone()
                }

                _ => panic!("trying to call non-function")
            }
        }
    }
}

fn main() {
    // FIXME: this is so gnarly.
    // let incr = \x -> 123.0 in incr (123.0) :: Number
    let num = Expression::Number(123.0);
    let incr = Expression::Variable("incr");
    let x = Expression::Variable("x");
    let args = ["x", "y", "z"];
    let args_ = [&num];
    let fn_call = Expression::FnCall(&incr, &args_);
    let lambda = Expression::Lambda(&args, &num);

    // let expr = Expression::Let(
    //     "incr", &lambda,
    //     &fn_call);

    let expr = Expression::Let("x", &num, &x);

    let mut inst = VariableGen {current: 0};
    let mut env = HashMap::new();

    println!("this is the thing: {:?}\n", w(&lambda, &mut env, &mut inst))
}
<commit_msg>write some unit tests<commit_after>use std::fmt::Display;
use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TaggedType<'a> = (Expression<'a>, Type);

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            // TODO: args -> types. modify the environment
            let body_ty = w(body, env, inst);
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        // TODO: Need unification algo here.
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            match w(callable, env, inst) {
                Type::Lambda(expected, body_type) => {
                    if args.len() != expected.len() {
                        panic!("wrong number of args given, {} for {}", args.len(), expected.len())
                    } else if expected != type_args.as_slice() {
                        panic!("bad types given: {:?} for {:?}", type_args.as_slice(), expected)
                    }

                    *body_type.clone()
                }

                _ => panic!("trying to call non-function")
            }
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);
        // let var = Expression::Variable("x");

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);

        // FIXME: until we return a result type this will panic.
        // assert_eq!(w(&var, &mut env, &mut inst), Type::Unbound(1));
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(2)], Box::new(
                       Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }
}

fn main() {
    println!("patience.");
}
<|endoftext|>


<commit_before>#[deriving(Clone, Eq, Show)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

#[deriving(Clone, Eq, Show)]
pub struct Size {
    pub width: f64,
    pub height: f64,
}

#[deriving(Clone, Eq, Show)]
pub struct Rect {
    pub origin: Point,
    pub size: Size,
}

impl Point {
    pub fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }

    pub fn add(&self, addPoint: Point) -> Point {
        Point::new(self.x + addPoint.x, self.y + addPoint.y)
    }

    pub fn subtract(&self, offsetPoint: Point) -> Point {
        Point::new(self.x - offsetPoint.x, self.y - offsetPoint.y)
    }
}

impl Size {
    pub fn new(width: f64, height: f64) -> Size {
        Size { width: width, height: height }
    }
}

impl Rect {
    pub fn new(origin: Point, size: Size) -> Rect {
        Rect { origin: origin, size: size }
    }

    /// Find which the rect has an origin farther to the left.
    pub fn minXRect<'a>(rect1: &'a Rect, rect2: &'a Rect) -> (&'a Rect, &'a Rect) {
        if rect1.minX() <= rect2.minX() {
            (rect1, rect2)
        } else {
            (rect2, rect1)
        }
    }

    /// Find which the rect has an origin with a lower y value.
    pub fn minYRect<'a>(rect1: &'a Rect, rect2: &'a Rect) -> (&'a Rect, &'a Rect) {
        if rect1.minY() <= rect2.minY() {
            (rect1, rect2)
        } else {
            (rect2, rect1)
        }
    }

    /**
     Check if this rect entirely contains another rect.
     */
    pub fn contains(&self, rect: &Rect) -> bool {
        // Find which the rect has an origin farther to the left.
        let (minXRect, otherRect) = Rect::minXRect(self, rect);

        // If the rectangles don't intersect, one cannot be contained in the other.
        self.intersects(rect)
        && (minXRect.maxX() >= otherRect.maxX() && minXRect.maxY() >= otherRect.maxY())
    }

    /**
     Check if this rect and another rect intersect.
     */
    pub fn intersects(&self, rect: &Rect) -> bool {
        let (minXRect, otherRect) = Rect::minXRect(self, rect);

        let intersects: bool = (minXRect.maxX() >= otherRect.minX())
            && ((minXRect.minY() >= otherRect.minY() && minXRect.minY() <= otherRect.maxY())
                || (minXRect.minY() <= otherRect.minY() && minXRect.maxY() >= otherRect.minY()));

        intersects
    }

    /**
     Get the intersection with another rect.
     */
    pub fn intersect(&self, rect: &Rect) -> Option<Rect> {
        if !self.intersects(rect) {
            return None;
        }

        let (minXRect, otherXRect) = Rect::minXRect(self, rect);
        let (minYRect, otherYRect) = Rect::minYRect(self, rect);
        let commonXStart = otherXRect.minX();
        let commonYStart = otherYRect.minY();

        let commonXEnd = minXRect.maxX().min(otherXRect.maxX());
        let commonYEnd = minYRect.maxY().min(otherYRect.maxY());

        let width = commonXEnd - commonXStart;
        let height = commonYEnd - commonYStart;

        Some(Rect::new(Point::new(commonXStart, commonYStart), Size::new(width, height)))
    }

    pub fn maxX(&self) -> f64 {
        self.origin.x + self.size.width
    }

    pub fn maxY(&self) -> f64 {
        self.origin.y + self.size.height
    }

    pub fn minX(&self) -> f64 {
        self.origin.x
    }

    pub fn minY(&self) -> f64 {
        self.origin.y
    }

    pub fn height(&self) -> f64 {
        self.size.height
    }

    pub fn width(&self) -> f64 {
        self.size.width
    }
}
<commit_msg>Don't derive Eq for geometry using f64<commit_after>#[deriving(Clone, Show)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

#[deriving(Clone, Show)]
pub struct Size {
    pub width: f64,
    pub height: f64,
}

#[deriving(Clone, Show)]
pub struct Rect {
    pub origin: Point,
    pub size: Size,
}

impl Point {
    pub fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }

    pub fn add(&self, addPoint: Point) -> Point {
        Point::new(self.x + addPoint.x, self.y + addPoint.y)
    }

    pub fn subtract(&self, offsetPoint: Point) -> Point {
        Point::new(self.x - offsetPoint.x, self.y - offsetPoint.y)
    }
}

impl Size {
    pub fn new(width: f64, height: f64) -> Size {
        Size { width: width, height: height }
    }
}

impl Rect {
    pub fn new(origin: Point, size: Size) -> Rect {
        Rect { origin: origin, size: size }
    }

    /// Find which the rect has an origin farther to the left.
    pub fn minXRect<'a>(rect1: &'a Rect, rect2: &'a Rect) -> (&'a Rect, &'a Rect) {
        if rect1.minX() <= rect2.minX() {
            (rect1, rect2)
        } else {
            (rect2, rect1)
        }
    }

    /// Find which the rect has an origin with a lower y value.
    pub fn minYRect<'a>(rect1: &'a Rect, rect2: &'a Rect) -> (&'a Rect, &'a Rect) {
        if rect1.minY() <= rect2.minY() {
            (rect1, rect2)
        } else {
            (rect2, rect1)
        }
    }

    /**
     Check if this rect entirely contains another rect.
     */
    pub fn contains(&self, rect: &Rect) -> bool {
        // Find which the rect has an origin farther to the left.
        let (minXRect, otherRect) = Rect::minXRect(self, rect);

        // If the rectangles don't intersect, one cannot be contained in the other.
        self.intersects(rect)
        && (minXRect.maxX() >= otherRect.maxX() && minXRect.maxY() >= otherRect.maxY())
    }

    /**
     Check if this rect and another rect intersect.
     */
    pub fn intersects(&self, rect: &Rect) -> bool {
        let (minXRect, otherRect) = Rect::minXRect(self, rect);

        let intersects: bool = (minXRect.maxX() >= otherRect.minX())
            && ((minXRect.minY() >= otherRect.minY() && minXRect.minY() <= otherRect.maxY())
                || (minXRect.minY() <= otherRect.minY() && minXRect.maxY() >= otherRect.minY()));

        intersects
    }

    /**
     Get the intersection with another rect.
     */
    pub fn intersect(&self, rect: &Rect) -> Option<Rect> {
        if !self.intersects(rect) {
            return None;
        }

        let (minXRect, otherXRect) = Rect::minXRect(self, rect);
        let (minYRect, otherYRect) = Rect::minYRect(self, rect);
        let commonXStart = otherXRect.minX();
        let commonYStart = otherYRect.minY();

        let commonXEnd = minXRect.maxX().min(otherXRect.maxX());
        let commonYEnd = minYRect.maxY().min(otherYRect.maxY());

        let width = commonXEnd - commonXStart;
        let height = commonYEnd - commonYStart;

        Some(Rect::new(Point::new(commonXStart, commonYStart), Size::new(width, height)))
    }

    pub fn maxX(&self) -> f64 {
        self.origin.x + self.size.width
    }

    pub fn maxY(&self) -> f64 {
        self.origin.y + self.size.height
    }

    pub fn minX(&self) -> f64 {
        self.origin.x
    }

    pub fn minY(&self) -> f64 {
        self.origin.y
    }

    pub fn height(&self) -> f64 {
        self.size.height
    }

    pub fn width(&self) -> f64 {
        self.size.width
    }
}
<|endoftext|>


<commit_before>//! Proxy engine
//!
//! This module is intended for processing incoming requests from clients,
//! handling occurred errors during a work, communicating with a message
//! broker and preparing appropriate responses in the certain format.
//!

use std::collections::HashMap;
use std::sync::Arc;

use futures::future::{lazy, Future};
use tungstenite::Message;
use uuid::Uuid;

use crate::cli::CliOptions;
use crate::config::get_config;
use crate::error::{Result, PathfinderError};
use crate::rabbitmq::RabbitMQClient;
use super::middleware::{
    CustomUserHeaders, EmptyMiddleware, JwtTokenMiddleware, Middleware,
    MiddlewareFuture
};
use super::MessageSender;
use super::futures::rpc_request_future;
use super::router::{extract_endpoints, ReadOnlyEndpoint, Router};
use super::options::RpcOptions;
use super::serializer::JsonMessage;
use super::utils::{deserialize_message};

/// Proxy engine for processing messages, handling errors and communicating
/// with a message broker.
pub struct Engine {
    router: Arc<Router>,
    middlewares: Arc<HashMap<String, Arc<Box<Middleware>>>>
}

impl Engine {
    /// Returns a new instance of `Engine`.
    pub fn new(cli: &CliOptions) -> Engine {
        let config = get_config(&cli.config);
        let endpoints = extract_endpoints(config);
        let router = Router::new(endpoints);
        let middlewares_list: Vec<(&str, Box<Middleware>)> = vec![
            ("jwt", Box::new(JwtTokenMiddleware::new())),
            ("empty", Box::new(EmptyMiddleware::new())),
        ];
        let middlewares = middlewares_list
            .into_iter()
            .map(|(key, middleware)| (String::from(key), Arc::new(middleware)))
            .collect();

        Engine {
            router: Arc::new(router),
            middlewares: Arc::new(middlewares),
        }
    }

    /// Performs deserializing an incoming message into JSON, searching for
    /// a route, applying a middleware and sending a request to microservice
    /// in the certain format.
    pub fn process_request(
        &self,
        message: Message,
        transmitter: MessageSender,
        rabbitmq_client: Arc<RabbitMQClient>
    ) -> Box<Future<Item=(), Error=PathfinderError> + Send + Sync + 'static> {
        // 1. Deserialize message into JSON
        let json_message = match deserialize_message(&message) {
            Ok(json_message) => json_message,
            Err(error) => return Box::new(lazy(move || Err(error)))
        };

        // 2. Finding an endpoint in according to the URL in the message body
        let url = json_message["url"].as_str().unwrap();
        let endpoint = match self.get_endpoint(url) {
            Ok(endpoint) => endpoint.clone(),
            Err(error) => return Box::new(lazy(move || Err(error)))
        };

        // 3. Instantiate futures that will be processing client credentials and a request
        let default_headers = self.generate_default_headers(&json_message.clone(), endpoint.clone());
        let transmitter_inner = transmitter.clone();
        let rabbitmq_client_inner = rabbitmq_client.clone();
        let rpc_options = Arc::new(RpcOptions::default()
            .with_endpoint(endpoint.clone())
            .with_message(json_message.clone())
            .with_queue_name(Arc::new(format!("{}", Uuid::new_v4()))
        ));

        let middleware_future = self.get_middleware_future(json_message.clone(), endpoint.clone(), rabbitmq_client.clone());
        Box::new(
            middleware_future.and_then(move |custom_headers: CustomUserHeaders| {
                let mut request_headers = default_headers.clone();
                for (key, value) in custom_headers.clone().iter() {
                    let header_name = key.to_string();
                    let header_value = value.to_string();
                    request_headers.insert(header_name, header_value);
                }
                rpc_request_future(
                    transmitter_inner.clone(),
                    rabbitmq_client_inner.clone(),
                    rpc_options.clone(),
                    request_headers.clone()
                )
            })
        )
    }

    /// Returns an endpoint based on specified URL.
    fn get_endpoint(&self, url: &str) -> Result<ReadOnlyEndpoint> {
        let router = self.router.clone();
        router.match_url(&url)
    }

    /// Returns a middleware for processing client credentials.
    fn get_middleware_future(
        &self,
        json_message: JsonMessage,
        endpoint: ReadOnlyEndpoint,
        rabbitmq_client: Arc<RabbitMQClient>
    ) -> MiddlewareFuture {
        let middleware = self.get_middleware_by_endpoint(endpoint);
        let rabbitmq_client_local = rabbitmq_client.clone();
        middleware.process_request(json_message, rabbitmq_client_local)
    }

    /// Returns a middleware that matches to the passed endpoint
    fn get_middleware_by_endpoint(&self, endpoint: ReadOnlyEndpoint) -> Arc<Box<Middleware>> {
        match endpoint.is_token_required() {
            true => self.middlewares.clone()["jwt"].clone(),
            false => self.middlewares.clone()["empty"].clone()
        }
    }

    /// Generates default headers for the message.
    fn generate_default_headers(&self, json: &JsonMessage, endpoint: ReadOnlyEndpoint) -> HashMap<String, String> {
        [
            (String::from("routing_key"), endpoint.get_routing_key()),
            (String::from("request_url"), endpoint.get_url()),
            (String::from("permissions"), json["permissions"].as_str().unwrap_or("").to_string()),
            (String::from("user_id"), json["user_id"].as_str().unwrap_or("").to_string()),
        ].iter().cloned().collect()
    }
}
<commit_msg>Updated method signatures and variable names<commit_after>//! Proxy engine
//!
//! This module is intended for processing incoming requests from clients,
//! handling occurred errors during a work, communicating with a message
//! broker and preparing appropriate responses in the certain format.
//!

use std::collections::HashMap;
use std::sync::Arc;

use futures::future::{lazy, Future};
use tungstenite::Message;
use uuid::Uuid;

use crate::cli::CliOptions;
use crate::config::get_config;
use crate::error::{Result, PathfinderError};
use crate::rabbitmq::RabbitMQContext;
use super::middleware::{
    CustomUserHeaders, EmptyMiddleware, JwtTokenMiddleware, Middleware,
    MiddlewareFuture
};
use super::MessageSender;
use super::futures::rpc_request_future;
use super::router::{extract_endpoints, ReadOnlyEndpoint, Router};
use super::options::RpcOptions;
use super::serializer::JsonMessage;
use super::utils::{deserialize_message};

/// Proxy engine for processing messages, handling errors and communicating
/// with a message broker.
pub struct Engine {
    router: Arc<Router>,
    middlewares: Arc<HashMap<String, Arc<Box<Middleware>>>>
}

impl Engine {
    /// Returns a new instance of `Engine`.
    pub fn new(cli: &CliOptions) -> Engine {
        let config = get_config(&cli.config);
        let endpoints = extract_endpoints(config);
        let router = Router::new(endpoints);
        let middlewares_list: Vec<(&str, Box<Middleware>)> = vec![
            ("jwt", Box::new(JwtTokenMiddleware::new())),
            ("empty", Box::new(EmptyMiddleware::new())),
        ];
        let middlewares = middlewares_list
            .into_iter()
            .map(|(key, middleware)| (String::from(key), Arc::new(middleware)))
            .collect();

        Engine {
            router: Arc::new(router),
            middlewares: Arc::new(middlewares),
        }
    }

    /// Performs deserializing an incoming message into JSON, searching for
    /// a route, applying a middleware and sending a request to microservice
    /// in the certain format.
    pub fn process_request(
        &self,
        message: Message,
        transmitter: MessageSender,
        rabbitmq_context: Arc<RabbitMQContext>
    ) -> Box<Future<Item=(), Error=PathfinderError> + Send + Sync + 'static> {
        // 1. Deserialize message into JSON
        let json_message = match deserialize_message(&message) {
            Ok(json_message) => json_message,
            Err(error) => return Box::new(lazy(move || Err(error)))
        };

        // 2. Finding an endpoint in according to the URL in the message body
        let url = json_message["url"].as_str().unwrap();
        let endpoint = match self.get_endpoint(url) {
            Ok(endpoint) => endpoint.clone(),
            Err(error) => return Box::new(lazy(move || Err(error)))
        };

        // 3. Instantiate futures that will be processing client credentials and a request
        let default_headers = self.generate_default_headers(&json_message.clone(), endpoint.clone());
        let transmitter_inner = transmitter.clone();
        let rabbitmq_context_inner = rabbitmq_context.clone();
        let rpc_options = Arc::new(RpcOptions::default()
            .with_endpoint(endpoint.clone())
            .with_message(json_message.clone())
            .with_queue_name(Arc::new(format!("{}", Uuid::new_v4()))
        ));

        let middleware_future = self.get_middleware_future(json_message.clone(), endpoint.clone(), rabbitmq_context.clone());
        Box::new(
            middleware_future.and_then(move |custom_headers: CustomUserHeaders| {
                let mut request_headers = default_headers.clone();
                for (key, value) in custom_headers.clone().iter() {
                    let header_name = key.to_string();
                    let header_value = value.to_string();
                    request_headers.insert(header_name, header_value);
                }
                rpc_request_future(
                    transmitter_inner.clone(),
                    rabbitmq_context_inner.clone(),
                    rpc_options.clone(),
                    request_headers.clone()
                )
            })
        )
    }

    /// Returns an endpoint based on specified URL.
    fn get_endpoint(&self, url: &str) -> Result<ReadOnlyEndpoint> {
        let router = self.router.clone();
        router.match_url(&url)
    }

    /// Returns a middleware for processing client credentials.
    fn get_middleware_future(
        &self,
        json_message: JsonMessage,
        endpoint: ReadOnlyEndpoint,
        rabbitmq_context: Arc<RabbitMQContext>
    ) -> MiddlewareFuture {
        let middleware = self.get_middleware_by_endpoint(endpoint);
        let rabbitmq_client_local = rabbitmq_context.clone();
        middleware.process_request(json_message, rabbitmq_context)
    }

    /// Returns a middleware that matches to the passed endpoint
    fn get_middleware_by_endpoint(&self, endpoint: ReadOnlyEndpoint) -> Arc<Box<Middleware>> {
        match endpoint.is_token_required() {
            true => self.middlewares.clone()["jwt"].clone(),
            false => self.middlewares.clone()["empty"].clone()
        }
    }

    /// Generates default headers for the message.
    fn generate_default_headers(&self, json: &JsonMessage, endpoint: ReadOnlyEndpoint) -> HashMap<String, String> {
        [
            (String::from("routing_key"), endpoint.get_routing_key()),
            (String::from("request_url"), endpoint.get_url()),
            (String::from("permissions"), json["permissions"].as_str().unwrap_or("").to_string()),
            (String::from("user_id"), json["user_id"].as_str().unwrap_or("").to_string()),
        ].iter().cloned().collect()
    }
}
<|endoftext|>


<commit_before>//! Configure the process resource limits.
use cfg_if::cfg_if;

use crate::errno::Errno;
use crate::Result;
pub use libc::rlim_t;
use std::mem;

cfg_if! {
    if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
        use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY};
    }else if #[cfg(any(
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "macos",
        target_os = "ios",
        target_os = "android",
        target_os = "dragonfly",
        all(target_os = "linux", not(target_env = "gnu"))
    ))]{
        use libc::{c_int, rlimit, RLIM_INFINITY};
    }
}

libc_enum! {
    /// The Resource enum is platform dependent. Check different platform
    /// manuals for more details. Some platform links has been provided for
    /// earier reference (non-exhaustive).
    ///
    /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
    /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)

    // linux-gnu uses u_int as resource enum, which is implemented in libc as
    // well.
    //
    // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
    // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
    #[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))]
    #[cfg_attr(any(
            target_os = "freebsd",
            target_os = "openbsd",
            target_os = "netbsd",
            target_os = "macos",
            target_os = "ios",
            target_os = "android",
            target_os = "dragonfly",
            all(target_os = "linux", not(target_env = "gnu"))
        ), repr(i32))]
    #[non_exhaustive]
    pub enum Resource {
        #[cfg(not(any(
                    target_os = "freebsd",
                    target_os = "netbsd",
                    target_os = "openbsd"
        )))]
        /// The maximum amount (in bytes) of virtual memory the process is
        /// allowed to map.
        RLIMIT_AS,
        /// The largest size (in bytes) core(5) file that may be created.
        RLIMIT_CORE,
        /// The maximum amount of cpu time (in seconds) to be used by each
        /// process.
        RLIMIT_CPU,
        /// The maximum size (in bytes) of the data segment for a process
        RLIMIT_DATA,
        /// The largest size (in bytes) file that may be created.
        RLIMIT_FSIZE,
        /// The maximum number of open files for this process.
        RLIMIT_NOFILE,
        /// The maximum size (in bytes) of the stack segment for a process.
        RLIMIT_STACK,

        #[cfg(target_os = "freebsd")]
        /// The maximum number of kqueues this user id is allowed to create.
        RLIMIT_KQUEUES,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A limit on the combined number of flock locks and fcntl leases that
        /// this process may establish.
        RLIMIT_LOCKS,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        /// The maximum size (in bytes) which a process may lock into memory
        /// using the mlock(2) system call.
        RLIMIT_MEMLOCK,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A limit on the number of bytes that can be allocated for POSIX
        /// message queues  for  the  real  user  ID  of  the  calling process.
        RLIMIT_MSGQUEUE,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A ceiling to which the process's nice value can be raised using
        /// setpriority or nice.
        RLIMIT_NICE,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        /// The maximum number of simultaneous processes for this user id.
        RLIMIT_NPROC,

        #[cfg(target_os = "freebsd")]
        /// The maximum number of pseudo-terminals this user id is allowed to
        /// create.
        RLIMIT_NPTS,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        /// When there is memory pressure and swap is available, prioritize
        /// eviction of a process' resident pages beyond this amount (in bytes).
        RLIMIT_RSS,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A ceiling on the real-time priority that may be set for this process
        /// using sched_setscheduler and  sched_set‐ param.
        RLIMIT_RTPRIO,

        #[cfg(any(target_os = "linux"))]
        /// A limit (in microseconds) on the amount of CPU time that a process
        /// scheduled under a real-time scheduling policy may con‐ sume without
        /// making a blocking system call.
        RLIMIT_RTTIME,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A limit on the number of signals that may be queued for the real
        /// user ID of the  calling  process.
        RLIMIT_SIGPENDING,

        #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
        /// The maximum size (in bytes) of socket buffer usage for this user.
        RLIMIT_SBSIZE,

        #[cfg(target_os = "freebsd")]
        /// The maximum size (in bytes) of the swap space that may be reserved
        /// or used by all of this user id's processes.
        RLIMIT_SWAP,

        #[cfg(target_os = "freebsd")]
        /// An alias for RLIMIT_AS.
        RLIMIT_VMEM,
    }
}

/// Get the current processes resource limits
///
/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
/// there is no limit.
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to get the limits of.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{getrlimit, Resource};
///
/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
/// println!("current soft_limit: {:?}", soft_limit);
/// println!("current hard_limit: {:?}", hard_limit);
/// ```
///
/// # References
///
/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
    let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();

    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
        }else{
            let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
        }
    }

    Errno::result(res).map(|_| {
        let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
        (Some(rlim_cur), Some(rlim_max))
    })
}

/// Set the current processes resource limits
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to set the limits of.
/// * `soft_limit`: The value that the kernel enforces for the corresponding
///   resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`.
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
///   the current hard limit for non-root users. Note: `None` input will be
///   replaced by constant `RLIM_INFINITY`.
///
/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can
/// > results `EPERM` Error. So you will need to set the number explicitly.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{setrlimit, Resource};
///
/// let soft_limit = Some(1024);
/// let hard_limit = Some(1048576);
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
/// ```
///
/// # References
///
/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
///
/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
pub fn setrlimit(
    resource: Resource,
    soft_limit: Option<rlim_t>,
    hard_limit: Option<rlim_t>,
) -> Result<()> {
    let new_rlim = rlimit {
        rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
        rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
    };
    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
        }else{
            let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
        }
    }

    Errno::result(res).map(drop)
}
<commit_msg>Lower limits in setrlimit example<commit_after>//! Configure the process resource limits.
use cfg_if::cfg_if;

use crate::errno::Errno;
use crate::Result;
pub use libc::rlim_t;
use std::mem;

cfg_if! {
    if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
        use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY};
    }else if #[cfg(any(
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "macos",
        target_os = "ios",
        target_os = "android",
        target_os = "dragonfly",
        all(target_os = "linux", not(target_env = "gnu"))
    ))]{
        use libc::{c_int, rlimit, RLIM_INFINITY};
    }
}

libc_enum! {
    /// The Resource enum is platform dependent. Check different platform
    /// manuals for more details. Some platform links has been provided for
    /// earier reference (non-exhaustive).
    ///
    /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
    /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)

    // linux-gnu uses u_int as resource enum, which is implemented in libc as
    // well.
    //
    // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
    // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
    #[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))]
    #[cfg_attr(any(
            target_os = "freebsd",
            target_os = "openbsd",
            target_os = "netbsd",
            target_os = "macos",
            target_os = "ios",
            target_os = "android",
            target_os = "dragonfly",
            all(target_os = "linux", not(target_env = "gnu"))
        ), repr(i32))]
    #[non_exhaustive]
    pub enum Resource {
        #[cfg(not(any(
                    target_os = "freebsd",
                    target_os = "netbsd",
                    target_os = "openbsd"
        )))]
        /// The maximum amount (in bytes) of virtual memory the process is
        /// allowed to map.
        RLIMIT_AS,
        /// The largest size (in bytes) core(5) file that may be created.
        RLIMIT_CORE,
        /// The maximum amount of cpu time (in seconds) to be used by each
        /// process.
        RLIMIT_CPU,
        /// The maximum size (in bytes) of the data segment for a process
        RLIMIT_DATA,
        /// The largest size (in bytes) file that may be created.
        RLIMIT_FSIZE,
        /// The maximum number of open files for this process.
        RLIMIT_NOFILE,
        /// The maximum size (in bytes) of the stack segment for a process.
        RLIMIT_STACK,

        #[cfg(target_os = "freebsd")]
        /// The maximum number of kqueues this user id is allowed to create.
        RLIMIT_KQUEUES,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A limit on the combined number of flock locks and fcntl leases that
        /// this process may establish.
        RLIMIT_LOCKS,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        /// The maximum size (in bytes) which a process may lock into memory
        /// using the mlock(2) system call.
        RLIMIT_MEMLOCK,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A limit on the number of bytes that can be allocated for POSIX
        /// message queues  for  the  real  user  ID  of  the  calling process.
        RLIMIT_MSGQUEUE,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A ceiling to which the process's nice value can be raised using
        /// setpriority or nice.
        RLIMIT_NICE,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        /// The maximum number of simultaneous processes for this user id.
        RLIMIT_NPROC,

        #[cfg(target_os = "freebsd")]
        /// The maximum number of pseudo-terminals this user id is allowed to
        /// create.
        RLIMIT_NPTS,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        /// When there is memory pressure and swap is available, prioritize
        /// eviction of a process' resident pages beyond this amount (in bytes).
        RLIMIT_RSS,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A ceiling on the real-time priority that may be set for this process
        /// using sched_setscheduler and  sched_set‐ param.
        RLIMIT_RTPRIO,

        #[cfg(any(target_os = "linux"))]
        /// A limit (in microseconds) on the amount of CPU time that a process
        /// scheduled under a real-time scheduling policy may con‐ sume without
        /// making a blocking system call.
        RLIMIT_RTTIME,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        /// A limit on the number of signals that may be queued for the real
        /// user ID of the  calling  process.
        RLIMIT_SIGPENDING,

        #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
        /// The maximum size (in bytes) of socket buffer usage for this user.
        RLIMIT_SBSIZE,

        #[cfg(target_os = "freebsd")]
        /// The maximum size (in bytes) of the swap space that may be reserved
        /// or used by all of this user id's processes.
        RLIMIT_SWAP,

        #[cfg(target_os = "freebsd")]
        /// An alias for RLIMIT_AS.
        RLIMIT_VMEM,
    }
}

/// Get the current processes resource limits
///
/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
/// there is no limit.
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to get the limits of.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{getrlimit, Resource};
///
/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
/// println!("current soft_limit: {:?}", soft_limit);
/// println!("current hard_limit: {:?}", hard_limit);
/// ```
///
/// # References
///
/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
    let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();

    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
        }else{
            let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
        }
    }

    Errno::result(res).map(|_| {
        let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
        (Some(rlim_cur), Some(rlim_max))
    })
}

/// Set the current processes resource limits
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to set the limits of.
/// * `soft_limit`: The value that the kernel enforces for the corresponding
///   resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`.
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
///   the current hard limit for non-root users. Note: `None` input will be
///   replaced by constant `RLIM_INFINITY`.
///
/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can
/// > results `EPERM` Error. So you will need to set the number explicitly.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{setrlimit, Resource};
///
/// let soft_limit = Some(512);
/// let hard_limit = Some(1024);
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
/// ```
///
/// # References
///
/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
///
/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
pub fn setrlimit(
    resource: Resource,
    soft_limit: Option<rlim_t>,
    hard_limit: Option<rlim_t>,
) -> Result<()> {
    let new_rlim = rlimit {
        rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
        rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
    };
    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
        }else{
            let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
        }
    }

    Errno::result(res).map(drop)
}
<|endoftext|>