<commit_before>use std::iter::Peekable;
use std::str::CharIndices;

use {Error, Result};

/// The outline of a shape.
///
/// http://www.w3.org/TR/SVG/paths.html
pub struct Path;

/// The data attribute of a path.
///
/// http://www.w3.org/TR/SVG/paths.html#PathData
pub struct Data {
    /// A series of path commands.
    pub commands: Vec<Command>,
}

/// A command used to draw a path.
#[derive(Debug)]
pub enum Command {
    /// [Establish][1] a new current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
    MoveTo(Positioning, Vec<f64>),

    /// [End][1] the current subpath.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
    ClosePath,

    /// [Draw][1] straight lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    LineTo(Positioning, Vec<f64>),

    /// [Draw][1] horizontal lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    HorizontalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] vertical lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    VerticalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    CurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve assuming the first control point to be
    /// the reflection of the second control point on the previous command
    /// relative to the current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    SmoothCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    QuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve assuming the control point to be the
    /// reflection of the control point on the previous command relative to the
    /// current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    SmoothQuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] an elliptical arc.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
    EllipticalArc(Positioning, Vec<f64>),
}

/// An attribute of a command indicating whether the coordinates of the command
/// are absolute or relative to the current position.
#[derive(Debug)]
pub enum Positioning {
    Absolute,
    Relative,
}

impl Data {
    #[inline]
    pub fn parse(line: &str) -> Result<Data> {
        DataParser::new(line).process()
    }
}

struct DataParser<'s> {
    line: &'s str,
    position: usize,
    cursor: Peekable<CharIndices<'s>>,
}

macro_rules! ok(
    ($result:expr) => (
        match $result {
            Ok(result) => result,
            Err(error) => return Err(error),
        }
    )
);

macro_rules! raise(
    ($parser:expr, $($arg:tt)*) => (
        return Err(Error {
            line: 1,
            column: $parser.position,
            message: format!($($arg)*),
        })
    );
);

impl<'s> DataParser<'s> {
    fn new(line: &'s str) -> DataParser<'s> {
        DataParser {
            line: line,
            position: 0,
            cursor: line.char_indices().peekable(),
        }
    }

    fn process(&mut self) -> Result<Data> {
        let mut commands = Vec::new();

        loop {
            self.skip_whitespace();

            match ok!(self.read_command()) {
                Some(command) => commands.push(command),
                _ => break,
            }
        }

        Ok(Data {
            commands: commands,
        })
    }

    fn read_command(&mut self) -> Result<Option<Command>> {
        use self::Command::*;
        use self::Positioning::*;

        let name = match self.next() {
            Some(name) => match name {
                'A'...'Z' | 'a'...'z' => name,
                _ => raise!(self, "expected a path command"),
            },
            _ => return Ok(None),
        };

        self.skip_whitespace();

        let params = ok!(self.read_parameters());

        Ok(Some(match name {
            'M' => MoveTo(Absolute, params),
            'm' => MoveTo(Relative, params),

            'Z' | 'z' => ClosePath,

            'L' => LineTo(Absolute, params),
            'l' => LineTo(Relative, params),

            'H' => HorizontalLineTo(Absolute, params),
            'h' => HorizontalLineTo(Relative, params),

            'V' => VerticalLineTo(Absolute, params),
            'v' => VerticalLineTo(Relative, params),

            'C' => CurveTo(Absolute, params),
            'c' => CurveTo(Relative, params),

            'S' => SmoothCurveTo(Absolute, params),
            's' => SmoothCurveTo(Relative, params),

            'Q' => QuadraticBezierCurveTo(Absolute, params),
            'q' => QuadraticBezierCurveTo(Relative, params),

            'T' => SmoothQuadraticBezierCurveTo(Absolute, params),
            't' => SmoothQuadraticBezierCurveTo(Relative, params),

            'A' => EllipticalArc(Absolute, params),
            'a' => EllipticalArc(Relative, params),

            _ => raise!(self, "found an unknown path command '{}'", name),
        }))
    }

    fn read_parameters(&mut self) -> Result<Vec<f64>> {
        let mut params = Vec::new();

        loop {
            match ok!(self.read_number()) {
                Some(number) => params.push(number),
                _ => break,
            }

            self.skip_whitespace();
            self.skip(",");
        }

        Ok(params)
    }

    fn read_number(&mut self) -> Result<Option<f64>> {
        self.skip_whitespace();

        let start = self.position;

        self.skip("-");
        self.skip_digits();
        self.skip(".");
        self.skip_digits();

        let end = self.position;

        if start == end {
            return Ok(None)
        }

        let number = &self.line[start..end];

        match number.parse() {
            Ok(number) => Ok(Some(number)),
            Err(_) => raise!(self, "failed to parse a number '{}'", number),
        }
    }

    #[inline]
    fn skip_whitespace(&mut self) {
        self.skip(" \t")
    }

    #[inline]
    fn skip_digits(&mut self) {
        self.skip("0123456789")
    }

    fn skip(&mut self, chars: &str) {
        loop {
            match self.peek() {
                Some(c) => {
                    if chars.contains_char(c) {
                        self.next();
                    } else {
                        break;
                    }
                },
                _ => break,
            }
        }
    }

    #[inline]
    fn next(&mut self) -> Option<char> {
        match self.cursor.next() {
            Some((_, c)) => {
                self.position += 1;
                Some(c)
            },
            _ => None,
        }
    }

    #[inline]
    fn peek(&mut self) -> Option<char> {
        match self.cursor.peek() {
            Some(&(_, c)) => Some(c),
            _ => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Data, DataParser};
    use super::Command::*;
    use super::Positioning::*;

    #[test]
    fn data_parser() {
        let data = Data::parse("M1,2 l3,4").ok().unwrap();

        assert_eq!(data.commands.len(), 2);

        match data.commands[0] {
            MoveTo(Absolute, ref params) => assert_eq!(*params, vec![1.0, 2.0]),
            _ => assert!(false),
        }

        match data.commands[1] {
            LineTo(Relative, ref params) => assert_eq!(*params, vec![3.0, 4.0]),
            _ => assert!(false),
        }
    }

    #[test]
    fn data_parser_read_command() {
        macro_rules! run(
            ($line:expr) => ({
                let mut parser = DataParser::new($line);
                parser.read_command().ok().unwrap().unwrap()
            });
        );

        macro_rules! test(
            ($line:expr, $command:ident, $positioning:ident, $params:expr) => (
                match run!($line) {
                    $command($positioning, params) => assert_eq!(params, $params),
                    _ => assert!(false),
                }
            );
            ($line:expr, $command:ident) => (
                match run!($line) {
                    $command => {},
                    _ => assert!(false),
                }
            );
        );

        test!("M4,2", MoveTo, Absolute, vec![4.0, 2.0]);
        test!("m4,2", MoveTo, Relative, vec![4.0, 2.0]);

        test!("Z", ClosePath);
        test!("z", ClosePath);

        test!("L7, 8  9", LineTo, Absolute, vec![7.0, 8.0, 9.0]);
        test!("l 7,8 9", LineTo, Relative, vec![7.0, 8.0, 9.0]);

        test!("H\t6,9", HorizontalLineTo, Absolute, vec![6.0, 9.0]);
        test!("h6,  \t9", HorizontalLineTo, Relative, vec![6.0, 9.0]);

        test!("V2.1,-3", VerticalLineTo, Absolute, vec![2.1, -3.0]);
        test!("v2.1 -3", VerticalLineTo, Relative, vec![2.1, -3.0]);

        test!("C0,1 0,2", CurveTo, Absolute, vec![0.0, 1.0, 0.0, 2.0]);
        test!("c0 ,1 0,  2", CurveTo, Relative, vec![0.0, 1.0, 0.0, 2.0]);

        test!("S42,0", SmoothCurveTo, Absolute, vec![42.0, 0.0]);
        test!("s \t 42,0", SmoothCurveTo, Relative, vec![42.0, 0.0]);

        test!("Q90.5 0", QuadraticBezierCurveTo, Absolute, vec![90.5, 0.0]);
        test!("q90.5, 0", QuadraticBezierCurveTo, Relative, vec![90.5, 0.0]);

        test!("T-1", SmoothQuadraticBezierCurveTo, Absolute, vec![-1.0]);
        test!("t -1", SmoothQuadraticBezierCurveTo, Relative, vec![-1.0]);

        test!("A2.6,0 -7", EllipticalArc, Absolute, vec![2.6, 0.0, -7.0]);
        test!("a 2.6 ,0 -7", EllipticalArc, Relative, vec![2.6, 0.0, -7.0]);
    }

    #[test]
    fn data_parser_read_parameters() {
        let mut parser = DataParser::new("1,2 3,4 5 6.7");
        let params = parser.read_parameters().ok().unwrap();
        assert_eq!(params, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.7]);
    }

    #[test]
    fn data_parser_read_number() {
        let lines = vec!["-1", "3", "3.14"];
        let numbers = vec![-1.0, 3.0, 3.14];

        for (line, &number) in lines.iter().zip(numbers.iter()) {
            let mut parser = DataParser::new(line);
            assert_eq!(parser.read_number().ok().unwrap().unwrap(), number);
        }
    }

    #[test]
    fn data_parser_skip_whitespace() {
        let mut parser = DataParser::new(" \t    m ");
        parser.skip_whitespace();
        assert_eq!(parser.position, 6);
    }
}
<commit_msg>Document path::Data::parse<commit_after>use std::iter::Peekable;
use std::str::CharIndices;

use {Error, Result};

/// The outline of a shape.
///
/// http://www.w3.org/TR/SVG/paths.html
pub struct Path;

/// The data attribute of a path.
///
/// http://www.w3.org/TR/SVG/paths.html#PathData
pub struct Data {
    /// A series of path commands.
    pub commands: Vec<Command>,
}

/// A command used to draw a path.
#[derive(Debug)]
pub enum Command {
    /// [Establish][1] a new current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
    MoveTo(Positioning, Vec<f64>),

    /// [End][1] the current subpath.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
    ClosePath,

    /// [Draw][1] straight lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    LineTo(Positioning, Vec<f64>),

    /// [Draw][1] horizontal lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    HorizontalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] vertical lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    VerticalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    CurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve assuming the first control point to be
    /// the reflection of the second control point on the previous command
    /// relative to the current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    SmoothCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    QuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve assuming the control point to be the
    /// reflection of the control point on the previous command relative to the
    /// current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    SmoothQuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] an elliptical arc.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
    EllipticalArc(Positioning, Vec<f64>),
}

/// An attribute of a command indicating whether the coordinates of the command
/// are absolute or relative to the current position.
#[derive(Debug)]
pub enum Positioning {
    Absolute,
    Relative,
}

impl Data {
    /// Parse the data attribute of a path.
    #[inline]
    pub fn parse(line: &str) -> Result<Data> {
        DataParser::new(line).process()
    }
}

struct DataParser<'s> {
    line: &'s str,
    position: usize,
    cursor: Peekable<CharIndices<'s>>,
}

macro_rules! ok(
    ($result:expr) => (
        match $result {
            Ok(result) => result,
            Err(error) => return Err(error),
        }
    )
);

macro_rules! raise(
    ($parser:expr, $($arg:tt)*) => (
        return Err(Error {
            line: 1,
            column: $parser.position,
            message: format!($($arg)*),
        })
    );
);

impl<'s> DataParser<'s> {
    fn new(line: &'s str) -> DataParser<'s> {
        DataParser {
            line: line,
            position: 0,
            cursor: line.char_indices().peekable(),
        }
    }

    fn process(&mut self) -> Result<Data> {
        let mut commands = Vec::new();

        loop {
            self.skip_whitespace();

            match ok!(self.read_command()) {
                Some(command) => commands.push(command),
                _ => break,
            }
        }

        Ok(Data {
            commands: commands,
        })
    }

    fn read_command(&mut self) -> Result<Option<Command>> {
        use self::Command::*;
        use self::Positioning::*;

        let name = match self.next() {
            Some(name) => match name {
                'A'...'Z' | 'a'...'z' => name,
                _ => raise!(self, "expected a path command"),
            },
            _ => return Ok(None),
        };

        self.skip_whitespace();

        let params = ok!(self.read_parameters());

        Ok(Some(match name {
            'M' => MoveTo(Absolute, params),
            'm' => MoveTo(Relative, params),

            'Z' | 'z' => ClosePath,

            'L' => LineTo(Absolute, params),
            'l' => LineTo(Relative, params),

            'H' => HorizontalLineTo(Absolute, params),
            'h' => HorizontalLineTo(Relative, params),

            'V' => VerticalLineTo(Absolute, params),
            'v' => VerticalLineTo(Relative, params),

            'C' => CurveTo(Absolute, params),
            'c' => CurveTo(Relative, params),

            'S' => SmoothCurveTo(Absolute, params),
            's' => SmoothCurveTo(Relative, params),

            'Q' => QuadraticBezierCurveTo(Absolute, params),
            'q' => QuadraticBezierCurveTo(Relative, params),

            'T' => SmoothQuadraticBezierCurveTo(Absolute, params),
            't' => SmoothQuadraticBezierCurveTo(Relative, params),

            'A' => EllipticalArc(Absolute, params),
            'a' => EllipticalArc(Relative, params),

            _ => raise!(self, "found an unknown path command '{}'", name),
        }))
    }

    fn read_parameters(&mut self) -> Result<Vec<f64>> {
        let mut params = Vec::new();

        loop {
            match ok!(self.read_number()) {
                Some(number) => params.push(number),
                _ => break,
            }

            self.skip_whitespace();
            self.skip(",");
        }

        Ok(params)
    }

    fn read_number(&mut self) -> Result<Option<f64>> {
        self.skip_whitespace();

        let start = self.position;

        self.skip("-");
        self.skip_digits();
        self.skip(".");
        self.skip_digits();

        let end = self.position;

        if start == end {
            return Ok(None)
        }

        let number = &self.line[start..end];

        match number.parse() {
            Ok(number) => Ok(Some(number)),
            Err(_) => raise!(self, "failed to parse a number '{}'", number),
        }
    }

    #[inline]
    fn skip_whitespace(&mut self) {
        self.skip(" \t")
    }

    #[inline]
    fn skip_digits(&mut self) {
        self.skip("0123456789")
    }

    fn skip(&mut self, chars: &str) {
        loop {
            match self.peek() {
                Some(c) => {
                    if chars.contains_char(c) {
                        self.next();
                    } else {
                        break;
                    }
                },
                _ => break,
            }
        }
    }

    #[inline]
    fn next(&mut self) -> Option<char> {
        match self.cursor.next() {
            Some((_, c)) => {
                self.position += 1;
                Some(c)
            },
            _ => None,
        }
    }

    #[inline]
    fn peek(&mut self) -> Option<char> {
        match self.cursor.peek() {
            Some(&(_, c)) => Some(c),
            _ => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Data, DataParser};
    use super::Command::*;
    use super::Positioning::*;

    #[test]
    fn data_parser() {
        let data = Data::parse("M1,2 l3,4").ok().unwrap();

        assert_eq!(data.commands.len(), 2);

        match data.commands[0] {
            MoveTo(Absolute, ref params) => assert_eq!(*params, vec![1.0, 2.0]),
            _ => assert!(false),
        }

        match data.commands[1] {
            LineTo(Relative, ref params) => assert_eq!(*params, vec![3.0, 4.0]),
            _ => assert!(false),
        }
    }

    #[test]
    fn data_parser_read_command() {
        macro_rules! run(
            ($line:expr) => ({
                let mut parser = DataParser::new($line);
                parser.read_command().ok().unwrap().unwrap()
            });
        );

        macro_rules! test(
            ($line:expr, $command:ident, $positioning:ident, $params:expr) => (
                match run!($line) {
                    $command($positioning, params) => assert_eq!(params, $params),
                    _ => assert!(false),
                }
            );
            ($line:expr, $command:ident) => (
                match run!($line) {
                    $command => {},
                    _ => assert!(false),
                }
            );
        );

        test!("M4,2", MoveTo, Absolute, vec![4.0, 2.0]);
        test!("m4,2", MoveTo, Relative, vec![4.0, 2.0]);

        test!("Z", ClosePath);
        test!("z", ClosePath);

        test!("L7, 8  9", LineTo, Absolute, vec![7.0, 8.0, 9.0]);
        test!("l 7,8 9", LineTo, Relative, vec![7.0, 8.0, 9.0]);

        test!("H\t6,9", HorizontalLineTo, Absolute, vec![6.0, 9.0]);
        test!("h6,  \t9", HorizontalLineTo, Relative, vec![6.0, 9.0]);

        test!("V2.1,-3", VerticalLineTo, Absolute, vec![2.1, -3.0]);
        test!("v2.1 -3", VerticalLineTo, Relative, vec![2.1, -3.0]);

        test!("C0,1 0,2", CurveTo, Absolute, vec![0.0, 1.0, 0.0, 2.0]);
        test!("c0 ,1 0,  2", CurveTo, Relative, vec![0.0, 1.0, 0.0, 2.0]);

        test!("S42,0", SmoothCurveTo, Absolute, vec![42.0, 0.0]);
        test!("s \t 42,0", SmoothCurveTo, Relative, vec![42.0, 0.0]);

        test!("Q90.5 0", QuadraticBezierCurveTo, Absolute, vec![90.5, 0.0]);
        test!("q90.5, 0", QuadraticBezierCurveTo, Relative, vec![90.5, 0.0]);

        test!("T-1", SmoothQuadraticBezierCurveTo, Absolute, vec![-1.0]);
        test!("t -1", SmoothQuadraticBezierCurveTo, Relative, vec![-1.0]);

        test!("A2.6,0 -7", EllipticalArc, Absolute, vec![2.6, 0.0, -7.0]);
        test!("a 2.6 ,0 -7", EllipticalArc, Relative, vec![2.6, 0.0, -7.0]);
    }

    #[test]
    fn data_parser_read_parameters() {
        let mut parser = DataParser::new("1,2 3,4 5 6.7");
        let params = parser.read_parameters().ok().unwrap();
        assert_eq!(params, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.7]);
    }

    #[test]
    fn data_parser_read_number() {
        let lines = vec!["-1", "3", "3.14"];
        let numbers = vec![-1.0, 3.0, 3.14];

        for (line, &number) in lines.iter().zip(numbers.iter()) {
            let mut parser = DataParser::new(line);
            assert_eq!(parser.read_number().ok().unwrap().unwrap(), number);
        }
    }

    #[test]
    fn data_parser_skip_whitespace() {
        let mut parser = DataParser::new(" \t    m ");
        parser.skip_whitespace();
        assert_eq!(parser.position, 6);
    }
}
<|endoftext|>


<commit_before>use super::Error;
#[cfg(any(unix, docsrs))]
use super::os::unix as imp;
#[cfg(windows)]
use super::os::windows as imp;
use std::ffi::OsStr;
use std::fmt;
use std::marker;
use std::ops;

/// A loaded dynamic library.
#[cfg_attr(docsrs, doc(cfg(any(unix, windows))))]
pub struct Library(imp::Library);

impl Library {
    /// Find and load a dynamic library.
    ///
    /// The `filename` argument may be either:
    ///
    /// * A library filename;
    /// * The absolute path to the library;
    /// * A relative (to the current working directory) path to the library.
    ///
    /// # Safety
    ///
    /// When a library is loaded, initialisation routines contained within it are executed.
    /// For the purposes of safety, the execution of these routines is conceptually the same calling an
    /// unknown foreign function and may impose arbitrary requirements on the caller for the call
    /// to be sound.
    ///
    /// Additionally, the callers of this function must also ensure that execution of the
    /// termination routines contained within the library is safe as well. These routines may be
    /// executed when the library is unloaded.
    ///
    /// # Thread-safety
    ///
    /// The implementation strives to be as MT-safe as sanely possible, however on certain
    /// platforms the underlying error-handling related APIs not always MT-safe. This library
    /// shares these limitations on those platforms. In particular, on certain UNIX targets
    /// `dlerror` is not MT-safe, resulting in garbage error messages in certain MT-scenarios.
    ///
    /// Calling this function from multiple threads is not MT-safe if used in conjunction with
    /// library filenames and the library search path is modified (`SetDllDirectory` function on
    /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
    ///
    /// # Platform-specific behaviour
    ///
    /// When a plain library filename is supplied, the locations in which the library is searched are
    /// platform specific and cannot be adjusted in a portable manner. See the documentation for
    /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods
    /// for further information on library lookup behaviour.
    ///
    /// If the `filename` specifies a library filename without a path and with the extension omitted,
    /// the `.dll` extension is implicitly added on Windows.
    ///
    /// [`os::unix::Library::new`]: crate::os::unix::Library::new
    /// [`os::windows::Library::new`]: crate::os::windows::Library::new
    ///
    /// # Tips
    ///
    /// Distributing your dynamic libraries under a filename common to all platforms (e.g.
    /// `awesome.module`) allows you to avoid code which has to account for platform’s conventional
    /// library filenames.
    ///
    /// Strive to specify an absolute or at least a relative path to your library, unless
    /// system-wide libraries are being loaded. Platform-dependent library search locations
    /// combined with various quirks related to path-less filenames may cause flakiness in
    /// programs.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// // Any of the following are valid.
    /// unsafe {
    ///     let _ = Library::new("/path/to/awesome.module").unwrap();
    ///     let _ = Library::new("../awesome.module").unwrap();
    ///     let _ = Library::new("libsomelib.so.1").unwrap();
    /// }
    /// ```
    pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> {
        imp::Library::new(filename).map(From::from)
    }

    /// Get a pointer to a function or static variable by symbol name.
    ///
    /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a
    /// null-terminated `symbol` may help to avoid an allocation.
    ///
    /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
    /// most likely invalid.
    ///
    /// # Safety
    ///
    /// Users of this API must specify the correct type of the function or variable loaded.
    ///
    /// # Platform-specific behaviour
    ///
    /// The implementation of thread-local variables is extremely platform specific and uses of such
    /// variables that work on e.g. Linux may have unintended behaviour on other targets.
    ///
    /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such
    /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym`
    /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null
    /// pointer without it being an error. If loading a null pointer is something you care about,
    /// consider using the [`os::unix::Library::get_singlethreaded`] call.
    ///
    /// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
    ///
    /// # Examples
    ///
    /// Given a loaded library:
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// let lib = unsafe {
    ///     Library::new("/path/to/awesome.module").unwrap()
    /// };
    /// ```
    ///
    /// Loading and using a function looks like this:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = unsafe {
    /// #     Library::new("/path/to/awesome.module").unwrap()
    /// # };
    /// unsafe {
    ///     let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
    ///         lib.get(b"awesome_function\0").unwrap();
    ///     awesome_function(0.42);
    /// }
    /// ```
    ///
    /// A static variable may also be loaded and inspected:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() };
    /// unsafe {
    ///     let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap();
    ///     **awesome_variable = 42.0;
    /// };
    /// ```
    pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>, Error> {
        self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
    }

    /// Unload the library.
    ///
    /// This method might be a no-op, depending on the flags with which the `Library` was opened,
    /// what library was opened or other platform specifics.
    ///
    /// You only need to call this if you are interested in handling any errors that may arise when
    /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
    /// library and ignore the errors were they arise.
    ///
    /// The underlying data structures may still get leaked if an error does occur.
    pub fn close(self) -> Result<(), Error> {
        self.0.close()
    }
}

impl fmt::Debug for Library {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl From<imp::Library> for Library {
    fn from(lib: imp::Library) -> Library {
        Library(lib)
    }
}

impl From<Library> for imp::Library {
    fn from(lib: Library) -> imp::Library {
        lib.0
    }
}

unsafe impl Send for Library {}
unsafe impl Sync for Library {}

/// Symbol from a library.
///
/// This type is a safeguard against using dynamically loaded symbols after a `Library` is
/// unloaded. The primary method to create an instance of a `Symbol` is via [`Library::get`].
///
/// The `Deref` trait implementation allows the use of `Symbol` as if it was a function or variable
/// itself, without taking care to “extract” the function or variable manually most of the time.
///
/// [`Library::get`]: Library::get
#[cfg_attr(docsrs, doc(cfg(any(unix, windows))))]
pub struct Symbol<'lib, T: 'lib> {
    inner: imp::Symbol<T>,
    pd: marker::PhantomData<&'lib T>,
}

impl<'lib, T> Symbol<'lib, T> {
    /// Extract the wrapped `os::platform::Symbol`.
    ///
    /// # Safety
    ///
    /// Using this function relinquishes all the lifetime guarantees. It is up to the developer to
    /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
    /// was loaded from.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// unsafe {
    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    /// }
    /// ```
    pub unsafe fn into_raw(self) -> imp::Symbol<T> {
        self.inner
    }

    /// Wrap the `os::platform::Symbol` into this safe wrapper.
    ///
    /// Note that, in order to create association between the symbol and the library this symbol
    /// came from, this function requires a reference to the library.
    ///
    /// # Safety
    ///
    /// The `library` reference must be exactly the library `sym` was loaded from.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// unsafe {
    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    ///     let symbol = Symbol::from_raw(symbol, &lib);
    /// }
    /// ```
    pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, library: &'lib L) -> Symbol<'lib, T> {
        let _ = library; // ignore here for documentation purposes.
        Symbol {
            inner: sym,
            pd: marker::PhantomData,
        }
    }
}

impl<'lib, T> Symbol<'lib, Option<T>> {
    /// Lift Option out of the symbol.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// unsafe {
    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
    ///     let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap();
    ///     let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null");
    /// }
    /// ```
    pub fn lift_option(self) -> Option<Symbol<'lib, T>> {
        self.inner.lift_option().map(|is| Symbol {
            inner: is,
            pd: marker::PhantomData,
        })
    }
}

impl<'lib, T> Clone for Symbol<'lib, T> {
    fn clone(&self) -> Symbol<'lib, T> {
        Symbol {
            inner: self.inner.clone(),
            pd: marker::PhantomData,
        }
    }
}

// FIXME: implement FnOnce for callable stuff instead.
impl<'lib, T> ops::Deref for Symbol<'lib, T> {
    type Target = T;
    fn deref(&self) -> &T {
        ops::Deref::deref(&self.inner)
    }
}

impl<'lib, T> fmt::Debug for Symbol<'lib, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.inner.fmt(f)
    }
}

unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {}
unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {}
<commit_msg>Fix documentation building on Windows<commit_after>use super::Error;
#[cfg(docsrs)]
use super::os::unix as imp; // the implementation used here doesn't matter particularly much...
#[cfg(all(not(docsrs), unix))]
use super::os::unix as imp;
#[cfg(all(not(docsrs), windows))]
use super::os::windows as imp;
use std::ffi::OsStr;
use std::fmt;
use std::marker;
use std::ops;

/// A loaded dynamic library.
#[cfg_attr(docsrs, doc(cfg(any(unix, windows))))]
pub struct Library(imp::Library);

impl Library {
    /// Find and load a dynamic library.
    ///
    /// The `filename` argument may be either:
    ///
    /// * A library filename;
    /// * The absolute path to the library;
    /// * A relative (to the current working directory) path to the library.
    ///
    /// # Safety
    ///
    /// When a library is loaded, initialisation routines contained within it are executed.
    /// For the purposes of safety, the execution of these routines is conceptually the same calling an
    /// unknown foreign function and may impose arbitrary requirements on the caller for the call
    /// to be sound.
    ///
    /// Additionally, the callers of this function must also ensure that execution of the
    /// termination routines contained within the library is safe as well. These routines may be
    /// executed when the library is unloaded.
    ///
    /// # Thread-safety
    ///
    /// The implementation strives to be as MT-safe as sanely possible, however on certain
    /// platforms the underlying error-handling related APIs not always MT-safe. This library
    /// shares these limitations on those platforms. In particular, on certain UNIX targets
    /// `dlerror` is not MT-safe, resulting in garbage error messages in certain MT-scenarios.
    ///
    /// Calling this function from multiple threads is not MT-safe if used in conjunction with
    /// library filenames and the library search path is modified (`SetDllDirectory` function on
    /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
    ///
    /// # Platform-specific behaviour
    ///
    /// When a plain library filename is supplied, the locations in which the library is searched are
    /// platform specific and cannot be adjusted in a portable manner. See the documentation for
    /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods
    /// for further information on library lookup behaviour.
    ///
    /// If the `filename` specifies a library filename without a path and with the extension omitted,
    /// the `.dll` extension is implicitly added on Windows.
    ///
    /// [`os::unix::Library::new`]: crate::os::unix::Library::new
    /// [`os::windows::Library::new`]: crate::os::windows::Library::new
    ///
    /// # Tips
    ///
    /// Distributing your dynamic libraries under a filename common to all platforms (e.g.
    /// `awesome.module`) allows you to avoid code which has to account for platform’s conventional
    /// library filenames.
    ///
    /// Strive to specify an absolute or at least a relative path to your library, unless
    /// system-wide libraries are being loaded. Platform-dependent library search locations
    /// combined with various quirks related to path-less filenames may cause flakiness in
    /// programs.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// // Any of the following are valid.
    /// unsafe {
    ///     let _ = Library::new("/path/to/awesome.module").unwrap();
    ///     let _ = Library::new("../awesome.module").unwrap();
    ///     let _ = Library::new("libsomelib.so.1").unwrap();
    /// }
    /// ```
    pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> {
        imp::Library::new(filename).map(From::from)
    }

    /// Get a pointer to a function or static variable by symbol name.
    ///
    /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a
    /// null-terminated `symbol` may help to avoid an allocation.
    ///
    /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
    /// most likely invalid.
    ///
    /// # Safety
    ///
    /// Users of this API must specify the correct type of the function or variable loaded.
    ///
    /// # Platform-specific behaviour
    ///
    /// The implementation of thread-local variables is extremely platform specific and uses of such
    /// variables that work on e.g. Linux may have unintended behaviour on other targets.
    ///
    /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such
    /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym`
    /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null
    /// pointer without it being an error. If loading a null pointer is something you care about,
    /// consider using the [`os::unix::Library::get_singlethreaded`] call.
    ///
    /// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
    ///
    /// # Examples
    ///
    /// Given a loaded library:
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// let lib = unsafe {
    ///     Library::new("/path/to/awesome.module").unwrap()
    /// };
    /// ```
    ///
    /// Loading and using a function looks like this:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = unsafe {
    /// #     Library::new("/path/to/awesome.module").unwrap()
    /// # };
    /// unsafe {
    ///     let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
    ///         lib.get(b"awesome_function\0").unwrap();
    ///     awesome_function(0.42);
    /// }
    /// ```
    ///
    /// A static variable may also be loaded and inspected:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() };
    /// unsafe {
    ///     let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap();
    ///     **awesome_variable = 42.0;
    /// };
    /// ```
    pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>, Error> {
        self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
    }

    /// Unload the library.
    ///
    /// This method might be a no-op, depending on the flags with which the `Library` was opened,
    /// what library was opened or other platform specifics.
    ///
    /// You only need to call this if you are interested in handling any errors that may arise when
    /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
    /// library and ignore the errors were they arise.
    ///
    /// The underlying data structures may still get leaked if an error does occur.
    pub fn close(self) -> Result<(), Error> {
        self.0.close()
    }
}

impl fmt::Debug for Library {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl From<imp::Library> for Library {
    fn from(lib: imp::Library) -> Library {
        Library(lib)
    }
}

impl From<Library> for imp::Library {
    fn from(lib: Library) -> imp::Library {
        lib.0
    }
}

unsafe impl Send for Library {}
unsafe impl Sync for Library {}

/// Symbol from a library.
///
/// This type is a safeguard against using dynamically loaded symbols after a `Library` is
/// unloaded. The primary method to create an instance of a `Symbol` is via [`Library::get`].
///
/// The `Deref` trait implementation allows the use of `Symbol` as if it was a function or variable
/// itself, without taking care to “extract” the function or variable manually most of the time.
///
/// [`Library::get`]: Library::get
#[cfg_attr(docsrs, doc(cfg(any(unix, windows))))]
pub struct Symbol<'lib, T: 'lib> {
    inner: imp::Symbol<T>,
    pd: marker::PhantomData<&'lib T>,
}

impl<'lib, T> Symbol<'lib, T> {
    /// Extract the wrapped `os::platform::Symbol`.
    ///
    /// # Safety
    ///
    /// Using this function relinquishes all the lifetime guarantees. It is up to the developer to
    /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
    /// was loaded from.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// unsafe {
    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    /// }
    /// ```
    pub unsafe fn into_raw(self) -> imp::Symbol<T> {
        self.inner
    }

    /// Wrap the `os::platform::Symbol` into this safe wrapper.
    ///
    /// Note that, in order to create association between the symbol and the library this symbol
    /// came from, this function requires a reference to the library.
    ///
    /// # Safety
    ///
    /// The `library` reference must be exactly the library `sym` was loaded from.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// unsafe {
    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    ///     let symbol = Symbol::from_raw(symbol, &lib);
    /// }
    /// ```
    pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, library: &'lib L) -> Symbol<'lib, T> {
        let _ = library; // ignore here for documentation purposes.
        Symbol {
            inner: sym,
            pd: marker::PhantomData,
        }
    }
}

impl<'lib, T> Symbol<'lib, Option<T>> {
    /// Lift Option out of the symbol.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// unsafe {
    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
    ///     let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap();
    ///     let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null");
    /// }
    /// ```
    pub fn lift_option(self) -> Option<Symbol<'lib, T>> {
        self.inner.lift_option().map(|is| Symbol {
            inner: is,
            pd: marker::PhantomData,
        })
    }
}

impl<'lib, T> Clone for Symbol<'lib, T> {
    fn clone(&self) -> Symbol<'lib, T> {
        Symbol {
            inner: self.inner.clone(),
            pd: marker::PhantomData,
        }
    }
}

// FIXME: implement FnOnce for callable stuff instead.
impl<'lib, T> ops::Deref for Symbol<'lib, T> {
    type Target = T;
    fn deref(&self) -> &T {
        ops::Deref::deref(&self.inner)
    }
}

impl<'lib, T> fmt::Debug for Symbol<'lib, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.inner.fmt(f)
    }
}

unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {}
unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {}
<|endoftext|>


<commit_before>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use super::control_prelude::*;
use std::borrow::Cow;

#[derive(Clone)]
pub struct Text(HandleRc);

impl Text {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupText(ptr::null_mut());
            Text(HandleRc::new(ih))
        }
    }

    pub fn value(&self) -> String {
        get_str_attribute(self.handle(), "VALUE\0")
    }
    pub unsafe fn value_slice(&self) -> Cow<str> {
        get_str_attribute_slice(self.handle(), "VALUE\0")
    }

    pub fn set_value(&self, value: &str) -> &Self {
        set_str_attribute(self.handle(), "VALUE\0", value);
        self
    }

    /// Inserts text at the end of the current text. Ignored if called before the control is mapped.
    /// If this control is multiline, [`append_newline`](./struct.Text.html#method.append_newline)
    /// is true, and the current text is not empty, then a `'\n'` character will be automatically
    /// inserted before the appended text.
    pub fn append(&self, text: &str) -> &Self {
        set_str_attribute(self.handle(), "APPEND\0", text);
        self
    }

    /// If true, this control is multiline, and the current text is not empty, then a `'\n'`
    /// character will be automatically inserted before text appended using `append`.
    ///
    /// The default is true.
    pub fn append_newline(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "APPENDNEWLINE\0") == "YES"
        }
    }

    /// See [`append_newline`](./struct.Text.html#method.append_newline)
    pub fn set_append_newline(&self, enabled: bool) -> &Self {
        set_str_attribute(self.handle(), "APPENDNEWLINE\0", if enabled { "YES\0" } else { "NO\0" });
        self
    }

    pub fn multiline(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "MULTILINE\0") == "YES"
        }
    }

    pub fn set_multiline(&self, multiline: bool) -> &Self {
        set_str_attribute(self.handle(), "MULTILINE\0", if multiline { "YES\0" } else { "NO\0" });
        self
    }

    pub fn visible_lines(&self) -> i32 {
        unsafe {
            let val = get_str_attribute_slice(self.handle(), "VISIBLELINES\0");
            val.parse().expect("could not convert VISIBLELINES to an integer")
        }
    }

    pub fn set_visible_lines(&self, lines: i32) -> &Self {
        set_str_attribute(self.handle(), "VISIBLELINES\0", &lines.to_string());
        self
    }
}

impl_control_traits!(Text);

impl ActiveAttribute for Text {}
impl CanFocusAttribute for Text {}
impl MinMaxSizeAttribute for Text {}
impl ScrollbarAttribute for Text {}
impl TipAttribute for Text {}
impl VisibleAttribute for Text {}

impl MenuCommonCallbacks for Text {}
impl GetKillFocusCallbacks for Text {}
impl EnterLeaveWindowCallbacks for Text {}
impl ValueChangedCallback for Text {}

#[derive(Clone)]
pub struct CaretArgs {
    pub lin: usize,
    pub col: usize,
    pub pos: usize,
    _dummy: (),
}

impl_callbacks! {
    Text {
        "CARET_CB\0" => caret_event {
            CARET_CALLBACKS<FnMut(&CaretArgs), CaretCallbackToken>
        }
        unsafe extern fn caret_cb(ih: *mut Ihandle, lin: c_int, col: c_int, pos: c_int) -> c_int {
            with_callbacks(ih, &CARET_CALLBACKS, |cbs| {
                let args = CaretArgs {
                    lin: lin as usize,
                    col: col as usize,
                    pos: pos as usize,
                    _dummy: (),
                };
                for cb in cbs {
                    (&mut *cb.1.borrow_mut())(&args);
                }
                IUP_DEFAULT
            })
        }
    }
}
<commit_msg>Add Text::visible_columns<commit_after>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use super::control_prelude::*;
use std::borrow::Cow;

#[derive(Clone)]
pub struct Text(HandleRc);

impl Text {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupText(ptr::null_mut());
            Text(HandleRc::new(ih))
        }
    }

    pub fn value(&self) -> String {
        get_str_attribute(self.handle(), "VALUE\0")
    }
    pub unsafe fn value_slice(&self) -> Cow<str> {
        get_str_attribute_slice(self.handle(), "VALUE\0")
    }

    pub fn set_value(&self, value: &str) -> &Self {
        set_str_attribute(self.handle(), "VALUE\0", value);
        self
    }

    /// Inserts text at the end of the current text. Ignored if called before the control is mapped.
    /// If this control is multiline, [`append_newline`](./struct.Text.html#method.append_newline)
    /// is true, and the current text is not empty, then a `'\n'` character will be automatically
    /// inserted before the appended text.
    pub fn append(&self, text: &str) -> &Self {
        set_str_attribute(self.handle(), "APPEND\0", text);
        self
    }

    /// If true, this control is multiline, and the current text is not empty, then a `'\n'`
    /// character will be automatically inserted before text appended using `append`.
    ///
    /// The default is true.
    pub fn append_newline(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "APPENDNEWLINE\0") == "YES"
        }
    }

    /// See [`append_newline`](./struct.Text.html#method.append_newline)
    pub fn set_append_newline(&self, enabled: bool) -> &Self {
        set_str_attribute(self.handle(), "APPENDNEWLINE\0", if enabled { "YES\0" } else { "NO\0" });
        self
    }

    pub fn multiline(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "MULTILINE\0") == "YES"
        }
    }

    pub fn set_multiline(&self, multiline: bool) -> &Self {
        set_str_attribute(self.handle(), "MULTILINE\0", if multiline { "YES\0" } else { "NO\0" });
        self
    }

    pub fn visible_columns(&self) -> i32 {
        unsafe {
            let val = get_str_attribute_slice(self.handle(), "VISIBLECOLUMNS\0");
            val.parse().expect("could not convert VISIBLECOLUMNS to an integer")
        }
    }

    pub fn set_visible_columns(&self, columns: i32) -> &Self {
        set_str_attribute(self.handle(), "VISIBLECOLUMNS\0", &columns.to_string());
        self
    }

    pub fn visible_lines(&self) -> i32 {
        unsafe {
            let val = get_str_attribute_slice(self.handle(), "VISIBLELINES\0");
            val.parse().expect("could not convert VISIBLELINES to an integer")
        }
    }

    pub fn set_visible_lines(&self, lines: i32) -> &Self {
        set_str_attribute(self.handle(), "VISIBLELINES\0", &lines.to_string());
        self
    }
}

impl_control_traits!(Text);

impl ActiveAttribute for Text {}
impl CanFocusAttribute for Text {}
impl MinMaxSizeAttribute for Text {}
impl ScrollbarAttribute for Text {}
impl TipAttribute for Text {}
impl VisibleAttribute for Text {}

impl MenuCommonCallbacks for Text {}
impl GetKillFocusCallbacks for Text {}
impl EnterLeaveWindowCallbacks for Text {}
impl ValueChangedCallback for Text {}

#[derive(Clone)]
pub struct CaretArgs {
    pub lin: usize,
    pub col: usize,
    pub pos: usize,
    _dummy: (),
}

impl_callbacks! {
    Text {
        "CARET_CB\0" => caret_event {
            CARET_CALLBACKS<FnMut(&CaretArgs), CaretCallbackToken>
        }
        unsafe extern fn caret_cb(ih: *mut Ihandle, lin: c_int, col: c_int, pos: c_int) -> c_int {
            with_callbacks(ih, &CARET_CALLBACKS, |cbs| {
                let args = CaretArgs {
                    lin: lin as usize,
                    col: col as usize,
                    pos: pos as usize,
                    _dummy: (),
                };
                for cb in cbs {
                    (&mut *cb.1.borrow_mut())(&args);
                }
                IUP_DEFAULT
            })
        }
    }
}
<|endoftext|>


<commit_before>use rusttype::{Font, FontCollection, Scale, point};
use std::fs::File;
use std::io::Read;
use std::ops::Deref;
use std::path::{Path, PathBuf};

use texture::{Dim2, Flat, R32F, Sampler, Texture};

pub type Result<A> = ::std::result::Result<A, FontError>;

/// Error used while rasterizing text.
#[derive(Debug)]
pub enum FontError {
  IncorrectPath(PathBuf),
  MultipleFonts,
  RasterizationFailed(String)
}

/// A texture containing some text (`String`).
///
/// This type is used to represent static text and works *on its own*. Once you’re handed such a
/// type, you can directly use the texture in a shader as it contains the pixels representing the
/// rasterizing glyphs.
pub struct TextTexture {
  texture: Texture<Flat, Dim2, R32F>,
}

impl Deref for TextTexture {
  type Target = Texture<Flat, Dim2, R32F>;

  fn deref(&self) -> &Self::Target {
    &self.texture
  }
}

/// Rasterizer responsible of rasterizing text.
pub struct Rasterizer<'a> {
  font: Font<'a>,
}

impl<'a> Rasterizer<'a> {
  /// Create a rasterizer from a font path.
  pub fn from_file<P>(font_path: P) -> Result<Self> where P: AsRef<Path> {
    let font_path = font_path.as_ref();
    let mut data = Vec::new();

    {
      let mut fh = File::open(font_path).map_err(|_| FontError::IncorrectPath(font_path.to_owned()))?;
      let _ = fh.read_to_end(&mut data);
    }

    let font = FontCollection::from_bytes(data).into_font().ok_or(FontError::MultipleFonts)?;

    Ok(Rasterizer {
      font: font
    })
  }

  /// Rasterize some text into a texture.
  pub fn rasterize(&self, text: &str, height: f32) -> Result<TextTexture> {
    // prepare the font hints
    let px_height = height.ceil() as usize;
    let scale = Scale::uniform(height);
    let v_metrics = self.font.v_metrics(scale);
    let offset = point(0., v_metrics.ascent);
    let glyphs: Vec<_> = self.font.layout(text, scale, offset).collect();
    let width = glyphs.iter().rev().filter_map(|glyph|
      glyph.pixel_bounding_box().map(|bb|
        bb.min.x as f32 + glyph.unpositioned().h_metrics().advance_width)).next().unwrap_or(0.).ceil() as usize;

    // rasterize the string into a buffer
    let mut texels = vec![0.; width * px_height];

    for glyph in glyphs {
      if let Some(bb) = glyph.pixel_bounding_box() {
        // not a control character or some shit; we can rasterize it!
        glyph.draw(|x, y, v| {
          let x = x as i32 + bb.min.x;
          let y = y as i32 + bb.min.y;

          // clipping test
          if x > 0 && x < width as i32 && y > 0 && y < height as i32 {
            let x = x as usize;
            let y = height.ceil() as usize - y as usize;
            texels[x + y * width] = v;
          }
        });
      }
    }

    // create the texture from the buffer
    let sampler = Sampler::default();
    let texture = Texture::new((width as u32, px_height as u32), 4, &sampler).map_err(|e|
      FontError::RasterizationFailed(format!("{:?}", e)))?;
    texture.upload(true, &texels);

    Ok(TextTexture {
      texture: texture
    })
  }
}
<commit_msg>Preparing dynamic texts code for later.<commit_after>use rusttype::{Font, FontCollection, Scale, point};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::ops::Deref;
use std::path::{Path, PathBuf};

use linear::Vector2;
use texture::{Dim2, Flat, R32F, Sampler, Texture};

pub type Result<A> = ::std::result::Result<A, FontError>;

/// Error used while rasterizing text.
#[derive(Debug)]
pub enum FontError {
  IncorrectPath(PathBuf),
  MultipleFonts,
  RasterizationFailed(String)
}

/// A texture containing some text (`String`).
///
/// This type is used to represent static text and works *on its own*. Once you’re handed such a
/// type, you can directly use the texture in a shader as it contains the pixels representing the
/// rasterizing glyphs.
pub struct TextTexture {
  texture: Texture<Flat, Dim2, R32F>,
}

impl Deref for TextTexture {
  type Target = Texture<Flat, Dim2, R32F>;

  fn deref(&self) -> &Self::Target {
    &self.texture
  }
}

/// Rasterizer responsible of rasterizing text.
pub struct Rasterizer<'a> {
  font: Font<'a>,
  cache: HashMap<char, GlyphMetrics>
}

impl<'a> Rasterizer<'a> {
  /// Create a rasterizer from a font path.
  pub fn from_file<P>(font_path: P) -> Result<Self> where P: AsRef<Path> {
    let font_path = font_path.as_ref();
    let mut data = Vec::new();

    {
      let mut fh = File::open(font_path).map_err(|_| FontError::IncorrectPath(font_path.to_owned()))?;
      let _ = fh.read_to_end(&mut data);
    }

    let font = FontCollection::from_bytes(data).into_font().ok_or(FontError::MultipleFonts)?;

    Ok(Rasterizer {
      font: font,
      cache: HashMap::new()
    })
  }

  /// Rasterize some text into a texture.
  pub fn rasterize(&self, text: &str, height: f32) -> Result<TextTexture> {
    // prepare the font hints
    let px_height = height.ceil() as usize;
    let scale = Scale::uniform(height);
    let v_metrics = self.font.v_metrics(scale);
    let offset = point(0., v_metrics.ascent);
    let glyphs: Vec<_> = self.font.layout(text, scale, offset).collect();
    let width = glyphs.iter().rev().filter_map(|glyph|
      glyph.pixel_bounding_box().map(|bb|
        bb.min.x as f32 + glyph.unpositioned().h_metrics().advance_width)).next().unwrap_or(0.).ceil() as usize;

    // rasterize the string into a buffer
    let mut texels = vec![0.; width * px_height];

    for glyph in glyphs {
      if let Some(bb) = glyph.pixel_bounding_box() {
        // not a control character or some shit; we can rasterize it!
        glyph.draw(|x, y, v| {
          let x = x as i32 + bb.min.x;
          let y = y as i32 + bb.min.y;

          // clipping test
          if x > 0 && x < width as i32 && y > 0 && y < height as i32 {
            let x = x as usize;
            let y = height.ceil() as usize - y as usize;
            texels[x + y * width] = v;
          }
        });
      }
    }

    // create the texture from the buffer
    let sampler = Sampler::default();
    let texture = Texture::new((width as u32, px_height as u32), 4, &sampler).map_err(|e|
      FontError::RasterizationFailed(format!("{:?}", e)))?;
    texture.upload(true, &texels);

    Ok(TextTexture {
      texture: texture
    })
  }
}

/// Glyph metrics. Represents information about a given glyph to ease dynamic text rendering.
struct GlyphMetrics {
  bounding_box: GlyphBoundingBox,
  uv_coords: GlyphUVCoords
}

/// Glyph bounding box.
///
/// This is not a pixel bounding box – it’s not the smallest rectangular area that tightly wraps a
/// glyph’s pixels. This type of bounding box wraps the whole glyph, with all the spaces required
/// above and below the baseline and after the glyph.
struct GlyphBoundingBox {
  lower: Vector2<f32>,
  upper: Vector2<f32>
}

/// Glyph UV coordinates.
///
/// UV coordinates are used to retrieve the pixels in a texture containing all the glyphs.
struct GlyphUVCoords {
  lower: Vector2<f32>,
  upper: Vector2<f32>
}
<|endoftext|>


<commit_before>/// unit creates a type representing a single unit.
///
/// # Examples
///
/// ```
/// #[macro_use(unit)]
/// extern crate fiz_math;
/// use std::fmt::Debug;
///
/// // GoldCoins are golden!
/// unit!(GoldCoins);
///
/// // SilverCoins are like gold coins, only worse!
/// unit!(SilverCoins);
///
/// fn collect<T: Debug>(coins: GoldCoins<T>) {
///     println!("We've collected {:?} golden coins!", coins.0);
/// }
///
/// fn main() {
///     let x = GoldCoins(5);
///     collect(x);
///
///     let y = SilverCoins(6);
///
///     // Try uncommenting the line below and see:
///     //
///     // error: mismatched types: expected `GoldCoins<_>`, found `SilverCoins<_>`"
///     //
///     //collect(y);
///
///     // add, subtract, multiply, divide:
///     assert_eq!(y + SilverCoins(4), SilverCoins(10));
///     assert_eq!(y - SilverCoins(3), SilverCoins(3));
///     assert_eq!(y * SilverCoins(3), SilverCoins(18));
///     assert_eq!(y / SilverCoins(2), SilverCoins(3));
///
///     // ordering and equality:
///     assert!(y < SilverCoins(7));
///     assert!(y > SilverCoins(5));
///     assert!(y <= SilverCoins(6));
///     assert!(y >= SilverCoins(6));
///     assert!(y == SilverCoins(6));
/// }
/// ```
///
#[macro_export]
macro_rules! unit {
    ( $ident:ident ) => {
        #[derive(Copy, Clone, Debug)]
        pub struct $ident<T>(pub T);

        unit!(impl_std_ops, $ident);
        unit!(impl_std_cmp, $ident);
        unit!(impl_num_traits, $ident);
    };

    (impl_std_ops, $ident:ident ) => {
        // addition
        impl<T: ::std::ops::Add<Output = T>> ::std::ops::Add for $ident<T> {
            type Output = Self;

            fn add(self, _rhs: Self) -> Self {
                $ident(self.0 + _rhs.0)
            }
        }

        // subtraction
        impl<T: ::std::ops::Sub<Output = T>> ::std::ops::Sub for $ident<T> {
            type Output = Self;

            fn sub(self, _rhs: Self) -> Self {
                $ident(self.0 - _rhs.0)
            }
        }

        // multiplication
        impl<T: ::std::ops::Mul<Output = T>> ::std::ops::Mul for $ident<T> {
            type Output = Self;

            fn mul(self, _rhs: Self) -> Self {
                $ident(self.0 * _rhs.0)
            }
        }

        // division
        impl<T: ::std::ops::Div<Output = T>> ::std::ops::Div for $ident<T> {
            type Output = Self;

            fn div(self, _rhs: Self) -> Self {
                $ident(self.0 / _rhs.0)
            }
        }

        // remainder
        impl<T: ::std::ops::Rem<Output = T>> ::std::ops::Rem for $ident<T> {
            type Output = Self;

            fn rem(self, rhs: Self) -> Self { $ident(self.0 % rhs.0) }
        }

        // negation
        impl<T: ::std::ops::Neg<Output = T>> ::std::ops::Neg for $ident<T> {
            type Output = Self;

            fn neg(self) -> Self::Output { $ident(-self.0) }
        }

        // not
        impl<T: ::std::ops::Not<Output = T>> ::std::ops::Not for $ident<T> {
            type Output = Self;

            fn not(self) -> Self::Output { $ident(!self.0) }
        }

        // bitwise and
        impl<T: ::std::ops::BitAnd<Output = T>> ::std::ops::BitAnd for $ident<T> {
            type Output = Self;

            fn bitand(self, rhs: Self) -> Self::Output { $ident(self.0 & rhs.0) }
        }

        // bitwise or
        impl<T: ::std::ops::BitOr<Output = T>> ::std::ops::BitOr for $ident<T> {
            type Output = Self;

            fn bitor(self, rhs: Self) -> Self::Output { $ident(self.0 | rhs.0) }
        }

        // bitwise xor
        impl<T: ::std::ops::BitXor<Output = T>> ::std::ops::BitXor for $ident<T> {
            type Output = Self;

            fn bitxor(self, rhs: Self) -> Self::Output { $ident(self.0 ^ rhs.0) }
        }

        // shift left
        impl<T: ::std::ops::Shl<usize>> ::std::ops::Shl<usize> for $ident<T> {
            type Output = $ident<T::Output>;

            fn shl(self, rhs: usize) -> Self::Output { $ident(self.0 << rhs) }
        }

        // shift right
        impl<T: ::std::ops::Shr<usize>> ::std::ops::Shr<usize> for $ident<T> {
            type Output = $ident<T::Output>;

            fn shr(self, rhs: usize) -> Self::Output { $ident(self.0 >> rhs) }
        }
    };

    (impl_std_cmp, $ident:ident ) => {
        // partial equality
        impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $ident<T> {
            fn eq(&self, _rhs: &Self) -> bool {
                self.0 == _rhs.0
            }
        }

        // equality
        impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $ident<T> {}

        // partial ordering
        impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $ident<T> {
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                if self.0 < other.0 {
                    Some(::std::cmp::Ordering::Less)
                } else if self.0 > other.0 {
                    Some(::std::cmp::Ordering::Greater)
                } else if self == other {
                    Some(::std::cmp::Ordering::Equal)
                } else {
                    None
                }
            }
        }

        // total ordering
        impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $ident<T> {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.0 < other.0 {
                    ::std::cmp::Ordering::Less
                } else if self.0 > other.0 {
                    ::std::cmp::Ordering::Greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }
    };

    (impl_num_traits, $ident:ident ) => {
        impl<T: $crate::num_export::traits::Zero> $crate::num_export::traits::Zero for $ident<T> {
            fn zero() -> Self { $ident(T::zero()) }
            fn is_zero(&self) -> bool { self.0.is_zero() }
        }

        impl<T: $crate::num_export::traits::One> $crate::num_export::traits::One for $ident<T> {
            fn one() -> Self { $ident(T::one()) }
        }

        impl<T: $crate::num_export::traits::Num> $crate::num_export::traits::Num for $ident<T> {
            type FromStrRadixErr = T::FromStrRadixErr;
            fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
                match T::from_str_radix(str, radix) {
                    Ok(x) => { Ok($ident(x)) }
                    Err(e) => { Err(e) }
                }
            }
        }
    };
}
<commit_msg>unit! impl num::traits::ToPrimitive<commit_after>/// unit creates a type representing a single unit.
///
/// # Examples
///
/// ```
/// #[macro_use(unit)]
/// extern crate fiz_math;
/// use std::fmt::Debug;
///
/// // GoldCoins are golden!
/// unit!(GoldCoins);
///
/// // SilverCoins are like gold coins, only worse!
/// unit!(SilverCoins);
///
/// fn collect<T: Debug>(coins: GoldCoins<T>) {
///     println!("We've collected {:?} golden coins!", coins.0);
/// }
///
/// fn main() {
///     let x = GoldCoins(5);
///     collect(x);
///
///     let y = SilverCoins(6);
///
///     // Try uncommenting the line below and see:
///     //
///     // error: mismatched types: expected `GoldCoins<_>`, found `SilverCoins<_>`"
///     //
///     //collect(y);
///
///     // add, subtract, multiply, divide:
///     assert_eq!(y + SilverCoins(4), SilverCoins(10));
///     assert_eq!(y - SilverCoins(3), SilverCoins(3));
///     assert_eq!(y * SilverCoins(3), SilverCoins(18));
///     assert_eq!(y / SilverCoins(2), SilverCoins(3));
///
///     // ordering and equality:
///     assert!(y < SilverCoins(7));
///     assert!(y > SilverCoins(5));
///     assert!(y <= SilverCoins(6));
///     assert!(y >= SilverCoins(6));
///     assert!(y == SilverCoins(6));
/// }
/// ```
///
#[macro_export]
macro_rules! unit {
    ( $ident:ident ) => {
        #[derive(Copy, Clone, Debug)]
        pub struct $ident<T>(pub T);

        unit!(impl_std_ops, $ident);
        unit!(impl_std_cmp, $ident);
        unit!(impl_num_traits, $ident);
    };

    (impl_std_ops, $ident:ident ) => {
        // addition
        impl<T: ::std::ops::Add<Output = T>> ::std::ops::Add for $ident<T> {
            type Output = Self;

            fn add(self, _rhs: Self) -> Self {
                $ident(self.0 + _rhs.0)
            }
        }

        // subtraction
        impl<T: ::std::ops::Sub<Output = T>> ::std::ops::Sub for $ident<T> {
            type Output = Self;

            fn sub(self, _rhs: Self) -> Self {
                $ident(self.0 - _rhs.0)
            }
        }

        // multiplication
        impl<T: ::std::ops::Mul<Output = T>> ::std::ops::Mul for $ident<T> {
            type Output = Self;

            fn mul(self, _rhs: Self) -> Self {
                $ident(self.0 * _rhs.0)
            }
        }

        // division
        impl<T: ::std::ops::Div<Output = T>> ::std::ops::Div for $ident<T> {
            type Output = Self;

            fn div(self, _rhs: Self) -> Self {
                $ident(self.0 / _rhs.0)
            }
        }

        // remainder
        impl<T: ::std::ops::Rem<Output = T>> ::std::ops::Rem for $ident<T> {
            type Output = Self;

            fn rem(self, rhs: Self) -> Self { $ident(self.0 % rhs.0) }
        }

        // negation
        impl<T: ::std::ops::Neg<Output = T>> ::std::ops::Neg for $ident<T> {
            type Output = Self;

            fn neg(self) -> Self::Output { $ident(-self.0) }
        }

        // not
        impl<T: ::std::ops::Not<Output = T>> ::std::ops::Not for $ident<T> {
            type Output = Self;

            fn not(self) -> Self::Output { $ident(!self.0) }
        }

        // bitwise and
        impl<T: ::std::ops::BitAnd<Output = T>> ::std::ops::BitAnd for $ident<T> {
            type Output = Self;

            fn bitand(self, rhs: Self) -> Self::Output { $ident(self.0 & rhs.0) }
        }

        // bitwise or
        impl<T: ::std::ops::BitOr<Output = T>> ::std::ops::BitOr for $ident<T> {
            type Output = Self;

            fn bitor(self, rhs: Self) -> Self::Output { $ident(self.0 | rhs.0) }
        }

        // bitwise xor
        impl<T: ::std::ops::BitXor<Output = T>> ::std::ops::BitXor for $ident<T> {
            type Output = Self;

            fn bitxor(self, rhs: Self) -> Self::Output { $ident(self.0 ^ rhs.0) }
        }

        // shift left
        impl<T: ::std::ops::Shl<usize>> ::std::ops::Shl<usize> for $ident<T> {
            type Output = $ident<T::Output>;

            fn shl(self, rhs: usize) -> Self::Output { $ident(self.0 << rhs) }
        }

        // shift right
        impl<T: ::std::ops::Shr<usize>> ::std::ops::Shr<usize> for $ident<T> {
            type Output = $ident<T::Output>;

            fn shr(self, rhs: usize) -> Self::Output { $ident(self.0 >> rhs) }
        }
    };

    (impl_std_cmp, $ident:ident ) => {
        // partial equality
        impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $ident<T> {
            fn eq(&self, _rhs: &Self) -> bool {
                self.0 == _rhs.0
            }
        }

        // equality
        impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $ident<T> {}

        // partial ordering
        impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $ident<T> {
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                if self.0 < other.0 {
                    Some(::std::cmp::Ordering::Less)
                } else if self.0 > other.0 {
                    Some(::std::cmp::Ordering::Greater)
                } else if self == other {
                    Some(::std::cmp::Ordering::Equal)
                } else {
                    None
                }
            }
        }

        // total ordering
        impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $ident<T> {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.0 < other.0 {
                    ::std::cmp::Ordering::Less
                } else if self.0 > other.0 {
                    ::std::cmp::Ordering::Greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }
    };

    (impl_num_traits, $ident:ident ) => {
        impl<T: $crate::num_export::traits::Zero> $crate::num_export::traits::Zero for $ident<T> {
            fn zero() -> Self { $ident(T::zero()) }
            fn is_zero(&self) -> bool { self.0.is_zero() }
        }

        impl<T: $crate::num_export::traits::One> $crate::num_export::traits::One for $ident<T> {
            fn one() -> Self { $ident(T::one()) }
        }

        impl<T: $crate::num_export::traits::Num> $crate::num_export::traits::Num for $ident<T> {
            type FromStrRadixErr = T::FromStrRadixErr;
            fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
                match T::from_str_radix(str, radix) {
                    Ok(x) => { Ok($ident(x)) }
                    Err(e) => { Err(e) }
                }
            }
        }

        impl<T: $crate::num_export::traits::ToPrimitive> $crate::num_export::traits::ToPrimitive for $ident<T> {
            fn to_i64(&self) -> Option<i64> { self.0.to_i64() }
            fn to_u64(&self) -> Option<u64> { self.0.to_u64() }
            fn to_isize(&self) -> Option<isize> { self.0.to_isize() }
            fn to_i8(&self) -> Option<i8> { self.0.to_i8() }
            fn to_i16(&self) -> Option<i16> { self.0.to_i16() }
            fn to_i32(&self) -> Option<i32> { self.0.to_i32() }
            fn to_usize(&self) -> Option<usize> { self.0.to_usize() }
            fn to_u8(&self) -> Option<u8> { self.0.to_u8() }
            fn to_u16(&self) -> Option<u16> { self.0.to_u16() }
            fn to_u32(&self) -> Option<u32> { self.0.to_u32() }
            fn to_f32(&self) -> Option<f32> { self.0.to_f32() }
            fn to_f64(&self) -> Option<f64> { self.0.to_f64() }
        }
    };
}
<|endoftext|>


<commit_before>use crate::*;
use bytes::Bytes;
use tokio::net::TcpStream;
use std::sync::Arc;
use dashmap::DashMap;
use futures::stream::StreamExt;
use futures::{select, Future, SinkExt};
use futures_util::future::FutureExt;
use uuid::Uuid;

#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Clone)]
pub(crate) struct PeerIdentity(Vec<u8>);

impl PeerIdentity {
    pub fn new() -> Self {
        let id = Uuid::new_v4();
        Self(id.as_bytes().to_vec())
    }
}

impl From<Vec<u8>> for PeerIdentity {
    fn from(data: Vec<u8>) -> Self {
        if data.len() == 0 {
            PeerIdentity::new()
        } else if data.len() > 255 {
            panic!("ZMQ_IDENTITY should not be more than 255 bytes long")
        } else {
            Self(data)
        }
    }
}

impl From<PeerIdentity> for Vec<u8> {
    fn from(p_id: PeerIdentity) -> Self {
        p_id.0
    }
}

impl From<PeerIdentity> for Bytes {
    fn from(p_id: PeerIdentity) -> Self {
        Bytes::from(p_id.0)
    }
}

pub(crate) struct Peer {
    pub(crate) identity: PeerIdentity,
    pub(crate) send_queue: futures::channel::mpsc::Sender<Message>,
    pub(crate) recv_queue: futures::channel::mpsc::Receiver<Message>,
    _io_close_handle: futures::channel::oneshot::Sender<bool>,
}

const COMPATIBILITY_MATRIX: [u8; 121] = [
    // PAIR, PUB, SUB, REQ, REP, DEALER, ROUTER, PULL, PUSH, XPUB, XSUB
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // PAIR
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, // PUB
    0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, // SUB
    0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, // REQ
    0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, // REP
    0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, // DEALER
    0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, // ROUTER
    0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // PULL
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // PUSH
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, // XPUB
    0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, // XSUB
];

/// Checks if two sokets are compatible with each other
/// ```
/// use zmq_rs::SocketType;
/// use zmq_rs::util::sockets_compatible;
/// assert!(sockets_compatible(SocketType::PUB, SocketType::SUB));
/// assert!(sockets_compatible(SocketType::REQ, SocketType::REP));
/// assert!(sockets_compatible(SocketType::DEALER, SocketType::ROUTER));
/// ```
pub fn sockets_compatible(one: SocketType, another: SocketType) -> bool {
    let row_index = one.to_usize().unwrap();
    let col_index = another.to_usize().unwrap();
    COMPATIBILITY_MATRIX[row_index * 11 + col_index] != 0
}

pub(crate) async fn greet_exchange(socket: &mut Framed<TcpStream, ZmqCodec>) -> ZmqResult<()> {
    socket
        .send(Message::Greeting(ZmqGreeting::default()))
        .await?;

    let greeting: Option<Result<Message, ZmqError>> = socket.next().await;

    match greeting {
        Some(Ok(Message::Greeting(greet))) => match greet.version {
            (3, 0) => Ok(()),
            _ => Err(ZmqError::Other("Unsupported protocol version")),
        },
        _ => Err(ZmqError::Codec("Failed Greeting exchange")),
    }
}

pub(crate) async fn greet_exchange_w_parts(
    sink: &mut tokio_util::codec::FramedWrite<
        tokio::io::WriteHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
    stream: &mut tokio_util::codec::FramedRead<
        tokio::io::ReadHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
) -> ZmqResult<()> {
    sink.send(Message::Greeting(ZmqGreeting::default())).await?;

    let greeting: Option<Result<Message, ZmqError>> = stream.next().await;

    match greeting {
        Some(Ok(Message::Greeting(greet))) => match greet.version {
            (3, 0) => Ok(()),
            _ => Err(ZmqError::Other("Unsupported protocol version")),
        },
        _ => Err(ZmqError::Codec("Failed Greeting exchange")),
    }
}

pub(crate) async fn ready_exchange(
    socket: &mut Framed<TcpStream, ZmqCodec>,
    socket_type: SocketType,
) -> ZmqResult<PeerIdentity> {
    let ready = ZmqCommand::ready(socket_type);
    socket.send(Message::Command(ready)).await?;

    let ready_repl: Option<ZmqResult<Message>> = socket.next().await;
    match ready_repl {
        Some(Ok(Message::Command(command))) => match command.name {
            ZmqCommandName::READY => {
                let other_sock_type = command
                    .properties
                    .get("Socket-Type")
                    .map(|x| SocketType::try_from(x.as_str()))
                    .unwrap_or(Err(ZmqError::Codec("Failed to parse other socket type")))?;

                let peer_id = command.properties.get("Identity").map_or_else(
                    || PeerIdentity::new(),
                    |x| PeerIdentity(x.clone().into_bytes()),
                );

                if sockets_compatible(socket_type, other_sock_type) {
                    Ok(peer_id)
                } else {
                    Err(ZmqError::Other(
                        "Provided sockets combination is not compatible",
                    ))
                }
            }
        },
        Some(Ok(_)) => Err(ZmqError::Codec("Failed to confirm ready state")),
        Some(Err(e)) => Err(e),
        None => Err(ZmqError::Other("No reply from server")),
    }
}

pub(crate) async fn ready_exchange_w_parts(
    sink: &mut tokio_util::codec::FramedWrite<
        tokio::io::WriteHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
    stream: &mut tokio_util::codec::FramedRead<
        tokio::io::ReadHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
    socket_type: SocketType,
) -> ZmqResult<PeerIdentity> {
    let ready = ZmqCommand::ready(socket_type);
    sink.send(Message::Command(ready)).await?;

    let ready_repl: Option<ZmqResult<Message>> = stream.next().await;
    match ready_repl {
        Some(Ok(Message::Command(command))) => match command.name {
            ZmqCommandName::READY => {
                let other_sock_type = command
                    .properties
                    .get("Socket-Type")
                    .map(|x| SocketType::try_from(x.as_str()))
                    .unwrap_or(Err(ZmqError::Codec("Failed to parse other socket type")))?;

                let peer_id = command.properties.get("Identity").map_or_else(
                    || PeerIdentity::new(),
                    |x| PeerIdentity::from(x.clone().into_bytes()),
                );

                if sockets_compatible(socket_type, other_sock_type) {
                    Ok(peer_id)
                } else {
                    Err(ZmqError::Other(
                        "Provided sockets combination is not compatible",
                    ))
                }
            }
        },
        Some(Ok(_)) => Err(ZmqError::Codec("Failed to confirm ready state")),
        Some(Err(e)) => Err(e),
        None => Err(ZmqError::Other("No reply from server")),
    }
}

pub(crate) async fn raw_connect(
    socket_type: SocketType,
    endpoint: &str,
) -> ZmqResult<Framed<TcpStream, ZmqCodec>> {
    let addr = endpoint.parse::<SocketAddr>()?;
    let mut raw_socket = Framed::new(TcpStream::connect(addr).await?, ZmqCodec::new());
    greet_exchange(&mut raw_socket).await?;
    ready_exchange(&mut raw_socket, socket_type).await?;
    Ok(raw_socket)
}

pub(crate) async fn peer_connected(
    socket: tokio::net::TcpStream,
    peers: Arc<DashMap<PeerIdentity, Peer>>,
    socket_type: SocketType
) {
    let (read, write) = tokio::io::split(socket);
    let mut read_part = tokio_util::codec::FramedRead::new(read, ZmqCodec::new());
    let mut write_part = tokio_util::codec::FramedWrite::new(write, ZmqCodec::new());

    greet_exchange_w_parts(&mut write_part, &mut read_part)
        .await
        .expect("Failed to exchange greetings");

    let peer_id = ready_exchange_w_parts(&mut write_part, &mut read_part, socket_type)
        .await
        .expect("Failed to exchange ready messages");
    println!("Peer connected {:?}", peer_id);

    let default_queue_size = 100;
    let (_send_queue, _send_queue_receiver) = futures::channel::mpsc::channel(default_queue_size);
    let (mut _recv_queue, _recv_queue_receiver) = futures::channel::mpsc::channel(default_queue_size);
    let (sender, receiver) = futures::channel::oneshot::channel::<bool>();

    let peer = Peer {
        identity: peer_id.clone(),
        send_queue: _send_queue,
        recv_queue: _recv_queue_receiver,
        _io_close_handle: sender,
    };

    peers.insert(peer_id.clone(), peer);

    let mut stop_handle = receiver.fuse();
    //let mut write_part = write_part.fuse();
    let mut incoming_queue = read_part.fuse();
    let mut outgoing_queue = _send_queue_receiver.fuse();
    loop {
        futures::select! {
                incoming = incoming_queue.next() => {
                    match incoming {
                        Some(Ok(Message::MultipartMessage(message))) => {
                            let mut envelope = vec![ZmqMessage { data: peer_id.clone().into()}];
                            envelope.extend(message);
                            dbg!(&envelope);
                            _recv_queue.send(Message::MultipartMessage(envelope))
                                .await
                                .expect("Failed to send");
                            println!("Sent message");
                        }
                        None => {
                            println!("Client disconnected {:?}", &peer_id);
                            peers.remove(&peer_id);
                            break;
                        }
                        _ => todo!(),
                    }
                },
                outgoing = outgoing_queue.next() => {
                    match outgoing {
                        Some(message) => {
                            let result = write_part.send(message).await;
                            dbg!(result);
                        },
                        None => {
                            println!("Outgoing queue closed. Stopping send coro");
                            break;
                        }
                    }
                },
                _ = stop_handle => {
                    println!("Stop callback received");
                    break;
                }
            }
    }
}

/// Opens port described by endpoint and starts a coroutine to accept new connections on it
/// Returns stop_handle channel that can be used to stop accepting new connections
pub(crate) async fn start_accepting_connections(
    endpoint: &str,
    peers: Arc<DashMap<PeerIdentity, Peer>>,
    socket_type: SocketType
) -> ZmqResult<futures::channel::oneshot::Sender<bool>> {
    let mut listener = tokio::net::TcpListener::bind(endpoint).await?;
    let (stop_handle, stop_callback) = futures::channel::oneshot::channel::<bool>();
    tokio::spawn(async move {
        let mut stop_callback = stop_callback.fuse();
        loop {
            select! {
                    incoming = listener.accept().fuse() => {
                        let (socket, _) = incoming.expect("Failed to accept connection");
                        tokio::spawn(peer_connected(socket, peers.clone(), socket_type));
                    },
                    _ = stop_callback => {
                        println!("Stop signal received");
                        break
                    }
                }
        }
    });
    Ok(stop_handle)
}<commit_msg>More tests for god of tests<commit_after>use crate::*;
use bytes::Bytes;
use tokio::net::TcpStream;
use std::sync::Arc;
use dashmap::DashMap;
use futures::stream::StreamExt;
use futures::{select, Future, SinkExt};
use futures_util::future::FutureExt;
use uuid::Uuid;

#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Clone)]
pub(crate) struct PeerIdentity(Vec<u8>);

impl PeerIdentity {
    pub fn new() -> Self {
        let id = Uuid::new_v4();
        Self(id.as_bytes().to_vec())
    }
}

impl From<Vec<u8>> for PeerIdentity {
    fn from(data: Vec<u8>) -> Self {
        if data.len() == 0 {
            PeerIdentity::new()
        } else if data.len() > 255 {
            panic!("ZMQ_IDENTITY should not be more than 255 bytes long")
        } else {
            Self(data)
        }
    }
}

impl From<PeerIdentity> for Vec<u8> {
    fn from(p_id: PeerIdentity) -> Self {
        p_id.0
    }
}

impl From<PeerIdentity> for Bytes {
    fn from(p_id: PeerIdentity) -> Self {
        Bytes::from(p_id.0)
    }
}

pub(crate) struct Peer {
    pub(crate) identity: PeerIdentity,
    pub(crate) send_queue: futures::channel::mpsc::Sender<Message>,
    pub(crate) recv_queue: futures::channel::mpsc::Receiver<Message>,
    _io_close_handle: futures::channel::oneshot::Sender<bool>,
}

const COMPATIBILITY_MATRIX: [u8; 121] = [
    // PAIR, PUB, SUB, REQ, REP, DEALER, ROUTER, PULL, PUSH, XPUB, XSUB
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // PAIR
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, // PUB
    0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, // SUB
    0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, // REQ
    0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, // REP
    0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, // DEALER
    0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, // ROUTER
    0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // PULL
    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // PUSH
    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, // XPUB
    0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, // XSUB
];

/// Checks if two sokets are compatible with each other
/// ```
/// use zmq_rs::SocketType;
/// use zmq_rs::util::sockets_compatible;
/// assert!(sockets_compatible(SocketType::PUB, SocketType::SUB));
/// assert!(sockets_compatible(SocketType::REQ, SocketType::REP));
/// assert!(sockets_compatible(SocketType::DEALER, SocketType::ROUTER));
/// assert!(!sockets_compatible(SocketType::PUB, SocketType::REP));
/// ```
pub fn sockets_compatible(one: SocketType, another: SocketType) -> bool {
    let row_index = one.to_usize().unwrap();
    let col_index = another.to_usize().unwrap();
    COMPATIBILITY_MATRIX[row_index * 11 + col_index] != 0
}

pub(crate) async fn greet_exchange(socket: &mut Framed<TcpStream, ZmqCodec>) -> ZmqResult<()> {
    socket
        .send(Message::Greeting(ZmqGreeting::default()))
        .await?;

    let greeting: Option<Result<Message, ZmqError>> = socket.next().await;

    match greeting {
        Some(Ok(Message::Greeting(greet))) => match greet.version {
            (3, 0) => Ok(()),
            _ => Err(ZmqError::Other("Unsupported protocol version")),
        },
        _ => Err(ZmqError::Codec("Failed Greeting exchange")),
    }
}

pub(crate) async fn greet_exchange_w_parts(
    sink: &mut tokio_util::codec::FramedWrite<
        tokio::io::WriteHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
    stream: &mut tokio_util::codec::FramedRead<
        tokio::io::ReadHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
) -> ZmqResult<()> {
    sink.send(Message::Greeting(ZmqGreeting::default())).await?;

    let greeting: Option<Result<Message, ZmqError>> = stream.next().await;

    match greeting {
        Some(Ok(Message::Greeting(greet))) => match greet.version {
            (3, 0) => Ok(()),
            _ => Err(ZmqError::Other("Unsupported protocol version")),
        },
        _ => Err(ZmqError::Codec("Failed Greeting exchange")),
    }
}

pub(crate) async fn ready_exchange(
    socket: &mut Framed<TcpStream, ZmqCodec>,
    socket_type: SocketType,
) -> ZmqResult<PeerIdentity> {
    let ready = ZmqCommand::ready(socket_type);
    socket.send(Message::Command(ready)).await?;

    let ready_repl: Option<ZmqResult<Message>> = socket.next().await;
    match ready_repl {
        Some(Ok(Message::Command(command))) => match command.name {
            ZmqCommandName::READY => {
                let other_sock_type = command
                    .properties
                    .get("Socket-Type")
                    .map(|x| SocketType::try_from(x.as_str()))
                    .unwrap_or(Err(ZmqError::Codec("Failed to parse other socket type")))?;

                let peer_id = command.properties.get("Identity").map_or_else(
                    || PeerIdentity::new(),
                    |x| PeerIdentity(x.clone().into_bytes()),
                );

                if sockets_compatible(socket_type, other_sock_type) {
                    Ok(peer_id)
                } else {
                    Err(ZmqError::Other(
                        "Provided sockets combination is not compatible",
                    ))
                }
            }
        },
        Some(Ok(_)) => Err(ZmqError::Codec("Failed to confirm ready state")),
        Some(Err(e)) => Err(e),
        None => Err(ZmqError::Other("No reply from server")),
    }
}

pub(crate) async fn ready_exchange_w_parts(
    sink: &mut tokio_util::codec::FramedWrite<
        tokio::io::WriteHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
    stream: &mut tokio_util::codec::FramedRead<
        tokio::io::ReadHalf<tokio::net::TcpStream>,
        ZmqCodec,
    >,
    socket_type: SocketType,
) -> ZmqResult<PeerIdentity> {
    let ready = ZmqCommand::ready(socket_type);
    sink.send(Message::Command(ready)).await?;

    let ready_repl: Option<ZmqResult<Message>> = stream.next().await;
    match ready_repl {
        Some(Ok(Message::Command(command))) => match command.name {
            ZmqCommandName::READY => {
                let other_sock_type = command
                    .properties
                    .get("Socket-Type")
                    .map(|x| SocketType::try_from(x.as_str()))
                    .unwrap_or(Err(ZmqError::Codec("Failed to parse other socket type")))?;

                let peer_id = command.properties.get("Identity").map_or_else(
                    || PeerIdentity::new(),
                    |x| PeerIdentity::from(x.clone().into_bytes()),
                );

                if sockets_compatible(socket_type, other_sock_type) {
                    Ok(peer_id)
                } else {
                    Err(ZmqError::Other(
                        "Provided sockets combination is not compatible",
                    ))
                }
            }
        },
        Some(Ok(_)) => Err(ZmqError::Codec("Failed to confirm ready state")),
        Some(Err(e)) => Err(e),
        None => Err(ZmqError::Other("No reply from server")),
    }
}

pub(crate) async fn raw_connect(
    socket_type: SocketType,
    endpoint: &str,
) -> ZmqResult<Framed<TcpStream, ZmqCodec>> {
    let addr = endpoint.parse::<SocketAddr>()?;
    let mut raw_socket = Framed::new(TcpStream::connect(addr).await?, ZmqCodec::new());
    greet_exchange(&mut raw_socket).await?;
    ready_exchange(&mut raw_socket, socket_type).await?;
    Ok(raw_socket)
}

pub(crate) async fn peer_connected(
    socket: tokio::net::TcpStream,
    peers: Arc<DashMap<PeerIdentity, Peer>>,
    socket_type: SocketType
) {
    let (read, write) = tokio::io::split(socket);
    let mut read_part = tokio_util::codec::FramedRead::new(read, ZmqCodec::new());
    let mut write_part = tokio_util::codec::FramedWrite::new(write, ZmqCodec::new());

    greet_exchange_w_parts(&mut write_part, &mut read_part)
        .await
        .expect("Failed to exchange greetings");

    let peer_id = ready_exchange_w_parts(&mut write_part, &mut read_part, socket_type)
        .await
        .expect("Failed to exchange ready messages");
    println!("Peer connected {:?}", peer_id);

    let default_queue_size = 100;
    let (_send_queue, _send_queue_receiver) = futures::channel::mpsc::channel(default_queue_size);
    let (mut _recv_queue, _recv_queue_receiver) = futures::channel::mpsc::channel(default_queue_size);
    let (sender, receiver) = futures::channel::oneshot::channel::<bool>();

    let peer = Peer {
        identity: peer_id.clone(),
        send_queue: _send_queue,
        recv_queue: _recv_queue_receiver,
        _io_close_handle: sender,
    };

    peers.insert(peer_id.clone(), peer);

    let mut stop_handle = receiver.fuse();
    //let mut write_part = write_part.fuse();
    let mut incoming_queue = read_part.fuse();
    let mut outgoing_queue = _send_queue_receiver.fuse();
    loop {
        futures::select! {
                incoming = incoming_queue.next() => {
                    match incoming {
                        Some(Ok(Message::MultipartMessage(message))) => {
                            let mut envelope = vec![ZmqMessage { data: peer_id.clone().into()}];
                            envelope.extend(message);
                            dbg!(&envelope);
                            _recv_queue.send(Message::MultipartMessage(envelope))
                                .await
                                .expect("Failed to send");
                            println!("Sent message");
                        }
                        None => {
                            println!("Client disconnected {:?}", &peer_id);
                            peers.remove(&peer_id);
                            break;
                        }
                        _ => todo!(),
                    }
                },
                outgoing = outgoing_queue.next() => {
                    match outgoing {
                        Some(message) => {
                            let result = write_part.send(message).await;
                            dbg!(result);
                        },
                        None => {
                            println!("Outgoing queue closed. Stopping send coro");
                            break;
                        }
                    }
                },
                _ = stop_handle => {
                    println!("Stop callback received");
                    break;
                }
            }
    }
}

/// Opens port described by endpoint and starts a coroutine to accept new connections on it
/// Returns stop_handle channel that can be used to stop accepting new connections
pub(crate) async fn start_accepting_connections(
    endpoint: &str,
    peers: Arc<DashMap<PeerIdentity, Peer>>,
    socket_type: SocketType
) -> ZmqResult<futures::channel::oneshot::Sender<bool>> {
    let mut listener = tokio::net::TcpListener::bind(endpoint).await?;
    let (stop_handle, stop_callback) = futures::channel::oneshot::channel::<bool>();
    tokio::spawn(async move {
        let mut stop_callback = stop_callback.fuse();
        loop {
            select! {
                    incoming = listener.accept().fuse() => {
                        let (socket, _) = incoming.expect("Failed to accept connection");
                        tokio::spawn(peer_connected(socket, peers.clone(), socket_type));
                    },
                    _ = stop_callback => {
                        println!("Stop signal received");
                        break
                    }
                }
        }
    });
    Ok(stop_handle)
}<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;

pub fn parse_update_count(tag: String) -> u64 {
    tag.split(' ').last().unwrap().parse().unwrap_or(0)
}

pub fn read_all<R: Read>(r: &mut R, mut buf: &mut [u8]) -> io::Result<()> {
    let mut start = 0;
    while start != buf.len() {
        match r.read(&mut buf[start..]) {
            Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "unexpected EOF")),
            Ok(len) => start += len,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}
<commit_msg>Clean up read_all<commit_after>use std::io;
use std::io::prelude::*;

pub fn parse_update_count(tag: String) -> u64 {
    tag.split(' ').last().unwrap().parse().unwrap_or(0)
}

pub fn read_all<R: Read>(r: &mut R, mut buf: &mut [u8]) -> io::Result<()> {
    while !buf.is_empty() {
        match r.read(buf) {
            Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "unexpected EOF")),
            Ok(len) => buf = &mut {buf}[len..],
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    Ok(())
}
<|endoftext|>