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

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

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

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

use crate::error::Error;
use crate::result::Result;

use log::error;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Token<'a> {
    BackQuote,
    Colon,
    Comment(&'a str),
    Dot,
    String(&'a str),
    Name(&'a str),
    Newline,
    Number(&'a str),
    ParenEnd,
    ParenStart,
    Quote,
    SquareBracketEnd,
    SquareBracketStart,
    Tilde,
    Whitespace(&'a str),
    End,
}

pub fn tokenize(s: &str) -> Result<Vec<Token>> {
    let mut lex = Lexer::new(s);
    let mut res = Vec::new();

    loop {
        match lex.eat_token()? {
            Token::End => break,
            tok => res.push(tok),
        }
    }

    Ok(res)
}

struct Lexer<'a> {
    input: &'a str,
}

impl<'a> Token<'a> {
    pub fn is_whitespace(&self) -> bool {
        match self {
            Token::Whitespace(_) => true,
            _ => false,
        }
    }

    pub fn is_newline(&self) -> bool {
        match self {
            Token::Newline => true,
            _ => false,
        }
    }

    pub fn is_comment(&self) -> bool {
        match self {
            Token::Comment(_) => true,
            _ => false,
        }
    }

    pub fn is_tilde(&self) -> bool {
        match self {
            Token::Tilde => true,
            _ => false,
        }
    }
}

impl<'a> Lexer<'a> {
    pub fn new(input: &str) -> Lexer {
        Lexer { input }
    }

    pub fn eat_token(&mut self) -> Result<Token<'a>> {
        if self.input.is_empty() {
            self.input = &self.input[..0];
            return Ok(Token::End);
        }

        if let Some(ch) = self.input.chars().nth(0) {
            let res = match ch {
                '(' => Ok((Token::ParenStart, 1)),
                ')' => Ok((Token::ParenEnd, 1)),
                '[' => Ok((Token::SquareBracketStart, 1)),
                ']' => Ok((Token::SquareBracketEnd, 1)),
                '.' => Ok((Token::Dot, 1)),
                ':' => Ok((Token::Colon, 1)),
                '\'' => Ok((Token::Quote, 1)),
                '`' => Ok((Token::BackQuote, 1)),
                '~' => Ok((Token::Tilde, 1)),
                '"' => eat_string(&self.input),
                ';' => eat_comment(&self.input),
                '-' | '0'...'9' => eat_number(&self.input),
                '\n' => Ok((Token::Newline, 1)),
                ch if ch.is_whitespace() => eat_whitespace(&self.input),
                _ if is_name(ch) => eat_name(&self.input),
                ch => {
                    error!("Parser invalid char {}", ch);
                    Err(Error::Lexer)
                }
            };

            let (tok, size) = match res {
                Ok(v) => v,
                Err(kind) => return Err(kind),
            };

            self.input = &self.input[size..];

            Ok(tok)
        } else {
            Err(Error::Lexer)
        }
    }
}

fn is_name(ch: char) -> bool {
    ch.is_alphanumeric() || is_symbol(ch)
}

fn is_symbol(ch: char) -> bool {
    match ch {
        '+' | '-' | '*' | '/' | '=' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '<' | '>' | '?' => {
            true
        }
        _ => false,
    }
}

fn eat_name(input: &str) -> Result<(Token, usize)> {
    for (ind, ch) in input.char_indices() {
        if !is_name(ch) {
            return Ok((Token::Name(&input[..ind]), ind));
        }
    }

    Ok((Token::Name(input), input.len()))
}

fn eat_whitespace(input: &str) -> Result<(Token, usize)> {
    for (ind, ch) in input.char_indices() {
        if !ch.is_whitespace() {
            return Ok((Token::Whitespace(&input[..ind]), ind));
        }
    }

    Ok((Token::Whitespace(input), input.len()))
}

fn eat_number(input: &str) -> Result<(Token, usize)> {
    let mut digits = false;
    let mut dot = false;
    let mut size = input.len();

    let (prefix_offset, rest) = if input.starts_with('-') {
        match input[1..].chars().next() {
            Some(ch) if ch.is_digit(10) => (1, &input[1..]),
            // Actually a name beginning with '-' rather a number
            _ => return eat_name(input),
        }
    } else {
        (0, input)
    };

    for (ind, ch) in rest.char_indices() {
        match ch {
            '.' => {
                if dot {
                    error!("ParserInvalidLiteral");
                    return Err(Error::Lexer);
                }
                dot = true;
            }
            _ if ch.is_digit(10) => {
                digits = true;
            }
            _ => {
                size = prefix_offset + ind;
                break;
            }
        }
    }

    if !digits {
        error!("ParserInvalidLiteral");
        Err(Error::Lexer)
    } else {
        Ok((Token::Number(&input[..size]), size))
    }
}

fn eat_string(input: &str) -> Result<(Token, usize)> {
    let rest = &input[1..]; // remove the first doublequote
    let mut size = rest.len();

    for (ind, ch) in rest.char_indices() {
        match ch {
            '"' => {
                size = ind;
                break;
            }
            _ => {
                continue;
            }
        }
    }
    Ok((Token::String(&rest[..size]), size + 2))
}

fn eat_comment(input: &str) -> Result<(Token, usize)> {
    let rest = &input[1..]; // remove the first character (;)
    let mut size = rest.len();

    for (ind, ch) in rest.char_indices() {
        match ch {
            '\n' => {
                size = ind;
                break;
            }
            _ => {
                continue;
            }
        }
    }
    Ok((Token::Comment(&rest[..size]), size + 1))
}

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

    #[test]
    fn test_lexer() {
        assert_eq!(
            tokenize("()").unwrap(),
            [Token::ParenStart, Token::ParenEnd]
        );

        assert_eq!(
            tokenize("( )").unwrap(),
            [Token::ParenStart, Token::Whitespace(" "), Token::ParenEnd]
        );

        assert_eq!(
            tokenize("[]").unwrap(),
            [Token::SquareBracketStart, Token::SquareBracketEnd]
        );

        assert_eq!(
            tokenize("'(1)").unwrap(),
            [
                Token::Quote,
                Token::ParenStart,
                Token::Number("1"),
                Token::ParenEnd
            ]
        );

        assert_eq!(
            tokenize("some-vector.vec/length").unwrap(),
            [
                Token::Name("some-vector"),
                Token::Dot,
                Token::Name("vec/length")
            ]
        );

        assert_eq!(tokenize("5").unwrap(), [Token::Number("5")]);
        assert_eq!(tokenize("-3").unwrap(), [Token::Number("-3")]);
        assert_eq!(tokenize("3.14").unwrap(), [Token::Number("3.14")]);
        assert_eq!(tokenize("-0.34").unwrap(), [Token::Number("-0.34")]);

        assert_eq!(
            tokenize("1 foo 3").unwrap(),
            [
                Token::Number("1"),
                Token::Whitespace(" "),
                Token::Name("foo"),
                Token::Whitespace(" "),
                Token::Number("3")
            ]
        );

        assert_eq!(
            tokenize("1 ~ 3").unwrap(),
            [
                Token::Number("1"),
                Token::Whitespace(" "),
                Token::Tilde,
                Token::Whitespace(" "),
                Token::Number("3")
            ]
        );

        assert_eq!(
            tokenize("hello\nworld").unwrap(),
            [Token::Name("hello"), Token::Newline, Token::Name("world")]
        );

        assert_eq!(
            tokenize("hello ; some comment").unwrap(),
            [
                Token::Name("hello"),
                Token::Whitespace(" "),
                Token::Comment(" some comment")
            ]
        );

        assert_eq!(
            tokenize("hello\n\"world\"").unwrap(),
            [Token::Name("hello"), Token::Newline, Token::String("world")]
        );

        assert_eq!(
            tokenize("\"hello.png\" ; some comment").unwrap(),
            [
                Token::String("hello.png"),
                Token::Whitespace(" "),
                Token::Comment(" some comment")
            ]
        );

        assert_eq!(
            tokenize("hello ; some comment\n(valid)").unwrap(),
            [
                Token::Name("hello"),
                Token::Whitespace(" "),
                Token::Comment(" some comment"),
                Token::Newline,
                Token::ParenStart,
                Token::Name("valid"),
                Token::ParenEnd
            ]
        );

        assert_eq!(
            tokenize("23 ~ (gen/scalar min: 3 max: 100)").unwrap(),
            [
                Token::Number("23"),
                Token::Whitespace(" "),
                Token::Tilde,
                Token::Whitespace(" "),
                Token::ParenStart,
                Token::Name("gen/scalar"),
                Token::Whitespace(" "),
                Token::Name("min"),
                Token::Colon,
                Token::Whitespace(" "),
                Token::Number("3"),
                Token::Whitespace(" "),
                Token::Name("max"),
                Token::Colon,
                Token::Whitespace(" "),
                Token::Number("100"),
                Token::ParenEnd,
            ]
        );
    }
}
<commit_msg>triple dot syntax (...) is deprecated, using ..= instead<commit_after>// Copyright (C) 2019 Inderjit Gill

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

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

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

use crate::error::Error;
use crate::result::Result;

use log::error;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Token<'a> {
    BackQuote,
    Colon,
    Comment(&'a str),
    Dot,
    String(&'a str),
    Name(&'a str),
    Newline,
    Number(&'a str),
    ParenEnd,
    ParenStart,
    Quote,
    SquareBracketEnd,
    SquareBracketStart,
    Tilde,
    Whitespace(&'a str),
    End,
}

pub fn tokenize(s: &str) -> Result<Vec<Token>> {
    let mut lex = Lexer::new(s);
    let mut res = Vec::new();

    loop {
        match lex.eat_token()? {
            Token::End => break,
            tok => res.push(tok),
        }
    }

    Ok(res)
}

struct Lexer<'a> {
    input: &'a str,
}

impl<'a> Token<'a> {
    pub fn is_whitespace(&self) -> bool {
        match self {
            Token::Whitespace(_) => true,
            _ => false,
        }
    }

    pub fn is_newline(&self) -> bool {
        match self {
            Token::Newline => true,
            _ => false,
        }
    }

    pub fn is_comment(&self) -> bool {
        match self {
            Token::Comment(_) => true,
            _ => false,
        }
    }

    pub fn is_tilde(&self) -> bool {
        match self {
            Token::Tilde => true,
            _ => false,
        }
    }
}

impl<'a> Lexer<'a> {
    pub fn new(input: &str) -> Lexer {
        Lexer { input }
    }

    pub fn eat_token(&mut self) -> Result<Token<'a>> {
        if self.input.is_empty() {
            self.input = &self.input[..0];
            return Ok(Token::End);
        }

        if let Some(ch) = self.input.chars().nth(0) {
            let res = match ch {
                '(' => Ok((Token::ParenStart, 1)),
                ')' => Ok((Token::ParenEnd, 1)),
                '[' => Ok((Token::SquareBracketStart, 1)),
                ']' => Ok((Token::SquareBracketEnd, 1)),
                '.' => Ok((Token::Dot, 1)),
                ':' => Ok((Token::Colon, 1)),
                '\'' => Ok((Token::Quote, 1)),
                '`' => Ok((Token::BackQuote, 1)),
                '~' => Ok((Token::Tilde, 1)),
                '"' => eat_string(&self.input),
                ';' => eat_comment(&self.input),
                '-' | '0'..='9' => eat_number(&self.input),
                '\n' => Ok((Token::Newline, 1)),
                ch if ch.is_whitespace() => eat_whitespace(&self.input),
                _ if is_name(ch) => eat_name(&self.input),
                ch => {
                    error!("Parser invalid char {}", ch);
                    Err(Error::Lexer)
                }
            };

            let (tok, size) = match res {
                Ok(v) => v,
                Err(kind) => return Err(kind),
            };

            self.input = &self.input[size..];

            Ok(tok)
        } else {
            Err(Error::Lexer)
        }
    }
}

fn is_name(ch: char) -> bool {
    ch.is_alphanumeric() || is_symbol(ch)
}

fn is_symbol(ch: char) -> bool {
    match ch {
        '+' | '-' | '*' | '/' | '=' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '<' | '>' | '?' => {
            true
        }
        _ => false,
    }
}

fn eat_name(input: &str) -> Result<(Token, usize)> {
    for (ind, ch) in input.char_indices() {
        if !is_name(ch) {
            return Ok((Token::Name(&input[..ind]), ind));
        }
    }

    Ok((Token::Name(input), input.len()))
}

fn eat_whitespace(input: &str) -> Result<(Token, usize)> {
    for (ind, ch) in input.char_indices() {
        if !ch.is_whitespace() {
            return Ok((Token::Whitespace(&input[..ind]), ind));
        }
    }

    Ok((Token::Whitespace(input), input.len()))
}

fn eat_number(input: &str) -> Result<(Token, usize)> {
    let mut digits = false;
    let mut dot = false;
    let mut size = input.len();

    let (prefix_offset, rest) = if input.starts_with('-') {
        match input[1..].chars().next() {
            Some(ch) if ch.is_digit(10) => (1, &input[1..]),
            // Actually a name beginning with '-' rather a number
            _ => return eat_name(input),
        }
    } else {
        (0, input)
    };

    for (ind, ch) in rest.char_indices() {
        match ch {
            '.' => {
                if dot {
                    error!("ParserInvalidLiteral");
                    return Err(Error::Lexer);
                }
                dot = true;
            }
            _ if ch.is_digit(10) => {
                digits = true;
            }
            _ => {
                size = prefix_offset + ind;
                break;
            }
        }
    }

    if !digits {
        error!("ParserInvalidLiteral");
        Err(Error::Lexer)
    } else {
        Ok((Token::Number(&input[..size]), size))
    }
}

fn eat_string(input: &str) -> Result<(Token, usize)> {
    let rest = &input[1..]; // remove the first doublequote
    let mut size = rest.len();

    for (ind, ch) in rest.char_indices() {
        match ch {
            '"' => {
                size = ind;
                break;
            }
            _ => {
                continue;
            }
        }
    }
    Ok((Token::String(&rest[..size]), size + 2))
}

fn eat_comment(input: &str) -> Result<(Token, usize)> {
    let rest = &input[1..]; // remove the first character (;)
    let mut size = rest.len();

    for (ind, ch) in rest.char_indices() {
        match ch {
            '\n' => {
                size = ind;
                break;
            }
            _ => {
                continue;
            }
        }
    }
    Ok((Token::Comment(&rest[..size]), size + 1))
}

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

    #[test]
    fn test_lexer() {
        assert_eq!(
            tokenize("()").unwrap(),
            [Token::ParenStart, Token::ParenEnd]
        );

        assert_eq!(
            tokenize("( )").unwrap(),
            [Token::ParenStart, Token::Whitespace(" "), Token::ParenEnd]
        );

        assert_eq!(
            tokenize("[]").unwrap(),
            [Token::SquareBracketStart, Token::SquareBracketEnd]
        );

        assert_eq!(
            tokenize("'(1)").unwrap(),
            [
                Token::Quote,
                Token::ParenStart,
                Token::Number("1"),
                Token::ParenEnd
            ]
        );

        assert_eq!(
            tokenize("some-vector.vec/length").unwrap(),
            [
                Token::Name("some-vector"),
                Token::Dot,
                Token::Name("vec/length")
            ]
        );

        assert_eq!(tokenize("5").unwrap(), [Token::Number("5")]);
        assert_eq!(tokenize("-3").unwrap(), [Token::Number("-3")]);
        assert_eq!(tokenize("3.14").unwrap(), [Token::Number("3.14")]);
        assert_eq!(tokenize("-0.34").unwrap(), [Token::Number("-0.34")]);

        assert_eq!(
            tokenize("1 foo 3").unwrap(),
            [
                Token::Number("1"),
                Token::Whitespace(" "),
                Token::Name("foo"),
                Token::Whitespace(" "),
                Token::Number("3")
            ]
        );

        assert_eq!(
            tokenize("1 ~ 3").unwrap(),
            [
                Token::Number("1"),
                Token::Whitespace(" "),
                Token::Tilde,
                Token::Whitespace(" "),
                Token::Number("3")
            ]
        );

        assert_eq!(
            tokenize("hello\nworld").unwrap(),
            [Token::Name("hello"), Token::Newline, Token::Name("world")]
        );

        assert_eq!(
            tokenize("hello ; some comment").unwrap(),
            [
                Token::Name("hello"),
                Token::Whitespace(" "),
                Token::Comment(" some comment")
            ]
        );

        assert_eq!(
            tokenize("hello\n\"world\"").unwrap(),
            [Token::Name("hello"), Token::Newline, Token::String("world")]
        );

        assert_eq!(
            tokenize("\"hello.png\" ; some comment").unwrap(),
            [
                Token::String("hello.png"),
                Token::Whitespace(" "),
                Token::Comment(" some comment")
            ]
        );

        assert_eq!(
            tokenize("hello ; some comment\n(valid)").unwrap(),
            [
                Token::Name("hello"),
                Token::Whitespace(" "),
                Token::Comment(" some comment"),
                Token::Newline,
                Token::ParenStart,
                Token::Name("valid"),
                Token::ParenEnd
            ]
        );

        assert_eq!(
            tokenize("23 ~ (gen/scalar min: 3 max: 100)").unwrap(),
            [
                Token::Number("23"),
                Token::Whitespace(" "),
                Token::Tilde,
                Token::Whitespace(" "),
                Token::ParenStart,
                Token::Name("gen/scalar"),
                Token::Whitespace(" "),
                Token::Name("min"),
                Token::Colon,
                Token::Whitespace(" "),
                Token::Number("3"),
                Token::Whitespace(" "),
                Token::Name("max"),
                Token::Colon,
                Token::Whitespace(" "),
                Token::Number("100"),
                Token::ParenEnd,
            ]
        );
    }
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Mattis Marjak (mattis.marjak@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#[macro_export]
macro_rules! hsm_define_objects_noparents {
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident),* ) ) => {
        hsm_define_objects!($st_str, $st_en, $st_evt, $shr_dat, ($($s),*) );
        hsm_state_parents!($st_en ; $($s -> None),*);
    };
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident $x:tt),*)) => {
        hsm_define_objects!($st_str, $st_en, $st_evt, $shr_dat, ($($s $x),*) );
        hsm_state_parents!($st_en ; $($s -> None),*);
    }
}

#[macro_export]
macro_rules! hsm_define_objects {
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident),* ) ) => {
        use $crate::Parent;
        _hsm_create_states!($($s),*);
        _hsm_create_state_enum!($st_en, ($($s),*));
        _hsm_create_state_struct!($st_str, $st_en, $st_evt, $shr_dat, ($($s),*) );
    };
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident $x:tt),*)) => {
        use $crate::Parent;
        _hsm_create_states!( $($s $x),* );
        _hsm_create_state_enum!($st_en, ($($s),*));
        _hsm_create_state_struct!($st_str, $st_en, $st_evt, $shr_dat, ($($s),*) );
    }
}

#[macro_export]
macro_rules! hsm_delayed_transition {
    ($probe:ident, $x:block) => {
        match $probe {
            true  => $crate::Action::DelayedTransition,
            false => $crate::Action::Transition($x)
        }
    }
}

#[macro_export]
macro_rules! hsm_impl_state {
    ($state:ident, $events:ident, $states:ident, $shr_data:ident,
     $($pat:pat => $result:expr),*) => {
        impl<'a, 'b, 'c, 'd, 'e> $crate::State<$events, $states, $shr_data> for $state {
            #[allow(unused_variables)]
            fn handle_event(&mut self, shr_data: &mut $shr_data, evt: &$crate::Event<$events>, probe: bool) -> $crate::Action<$states> {
                match *evt {
                    $( $pat => $result),*
                }
            }
        }
    };
    ($state:ident, $events:ident, $states:ident, $shr_data:ident,
     $shr:ident, $evt:ident, $probe:ident, $($pat:pat => $result:expr),*) => {
        impl<'a, 'b, 'c, 'd, 'e> $crate::State<$events, $states, $shr_data> for $state {
            #[allow(unused_variables)]
            fn handle_event(&mut self, $shr: &mut $shr_data, $evt: &$crate::Event<$events>, $probe: bool) -> $crate::Action<$states> {
                match *$evt {
                    $( $pat => $result),*
                }
            }
        }
    }
}

#[macro_export]
macro_rules! hsm_state_parents {
    ($st_en:ident ; $($nam:ident -> $parent:ident),*) => {
        $(_hsm_impl_state_parent!($st_en ; $nam -> $parent);)*
    }
}

#[macro_export]
macro_rules! _hsm_impl_state_parent {
    ($st_en:ident ; $nam:ident -> None) => {
        impl $crate::Parent<$st_en> for $nam {
            fn get_parent() -> Option<$st_en> { None }
        }
    };
    ($st_en:ident ; $nam:ident -> $parent:ident) => {
        impl $crate::Parent<$st_en> for $nam {
            fn get_parent() -> Option<$st_en> { Some($st_en::$parent) }
        }
    }
}

#[macro_export]
macro_rules! _hsm_create_states {
    ( $($s:ident),* ) => {
        $(_hsm_create_state!($s);)*
    };
    ($($s:ident $x:tt),*) => {
        $(_hsm_create_state!($s $x);)*
    }
}

#[macro_export]
macro_rules! _hsm_create_state_common {
    ($nam:ident) => {
        impl $crate::Name for $nam {
            fn name(&self) -> &'static str {
                stringify!($nam)
            }
        }
    }
}

#[macro_export]
macro_rules! _hsm_create_state {
    ($nam:ident) => {
        #[derive(Debug)]
        pub struct $nam;
        impl $crate::Initializer for $nam {
            fn new() -> Self {
                $nam
            }
        }
        _hsm_create_state_common!($nam);
    };
    ($nam:ident { $($field_name:ident : $field_type:ty = $field_default:expr),* }) => {
        #[derive(Debug)]
        pub struct $nam {
            _phantom        : ::std::marker::PhantomData<u8>,
            $( $field_name  : $field_type ),*
        }
        impl $crate::Initializer for $nam {
            fn new() -> Self {
                $nam {
                    _phantom        : ::std::marker::PhantomData,
                    $( $field_name  : $field_default ),*
                }
            }
        }
        _hsm_create_state_common!($nam);
    }
}

#[macro_export]
macro_rules! _hsm_create_state_enum {
    ($st_en:ident, ($($s:ident),*) ) => {
        #[derive(Debug, Clone, Eq, PartialEq)]
        pub enum $st_en {
            $( $s ),*
        }
        impl ::std::fmt::Display for $st_en {
            fn fmt(&self, f:&mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
                match *self {
                    $( $st_en::$s => try!(::std::fmt::Display::fmt(stringify!($s), f)) ),*
                };
                Ok(())
            }
        }
        impl $crate::InstanceParent<$st_en> for $st_en {
            fn get_parent(&self) -> Option<$st_en> {
                match *self {
                    $( $st_en::$s => $s::get_parent() ),*
                }
            }
        }
    }
}

#[macro_export]
macro_rules! _hsm_create_state_struct {
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ($($s:ident),*) ) => {
        #[derive(Debug)]
        #[allow(non_snake_case)]
        pub struct $st_str {
            $( $s : $s ),*
        }
        impl $crate::Initializer for $st_str {
            fn new() -> Self {
                $st_str {
                    $( $s : $s::new() ),*
                }
            }
        }
        impl<'a, 'b, 'c, 'd, 'e> $crate::StateLookup<$st_en, $st_evt, $shr_dat> for $st_str {
            fn lookup(&mut self, typ: &$st_en) -> &mut $crate::State<$st_evt, $st_en, $shr_dat> {
                match *typ {
                    $($st_en::$s => &mut self.$s ),*
                }
            }
        }
    }
}
<commit_msg>fix impl state macro var types<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Mattis Marjak (mattis.marjak@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#[macro_export]
macro_rules! hsm_define_objects_noparents {
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident),* ) ) => {
        hsm_define_objects!($st_str, $st_en, $st_evt, $shr_dat, ($($s),*) );
        hsm_state_parents!($st_en ; $($s -> None),*);
    };
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident $x:tt),*)) => {
        hsm_define_objects!($st_str, $st_en, $st_evt, $shr_dat, ($($s $x),*) );
        hsm_state_parents!($st_en ; $($s -> None),*);
    }
}

#[macro_export]
macro_rules! hsm_define_objects {
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident),* ) ) => {
        use $crate::Parent;
        _hsm_create_states!($($s),*);
        _hsm_create_state_enum!($st_en, ($($s),*));
        _hsm_create_state_struct!($st_str, $st_en, $st_evt, $shr_dat, ($($s),*) );
    };
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ( $($s:ident $x:tt),*)) => {
        use $crate::Parent;
        _hsm_create_states!( $($s $x),* );
        _hsm_create_state_enum!($st_en, ($($s),*));
        _hsm_create_state_struct!($st_str, $st_en, $st_evt, $shr_dat, ($($s),*) );
    }
}

#[macro_export]
macro_rules! hsm_delayed_transition {
    ($probe:ident, $x:block) => {
        match $probe {
            true  => $crate::Action::DelayedTransition,
            false => $crate::Action::Transition($x)
        }
    }
}

#[macro_export]
macro_rules! hsm_impl_state {
    ($state:ty, $events:ty, $states:ty, $shr_data:ty,
     $($pat:pat => $result:expr),*) => {
        impl<'a, 'b, 'c, 'd, 'e> $crate::State<$events, $states, $shr_data> for $state {
            #[allow(unused_variables)]
            fn handle_event(&mut self, shr_data: &mut $shr_data, evt: &$crate::Event<$events>, probe: bool) -> $crate::Action<$states> {
                match *evt {
                    $( $pat => $result),*
                }
            }
        }
    };
    ($state:ty, $events:ty, $states:ty, $shr_data:ty,
     $shr:ident, $evt:ident, $probe:ident, $($pat:pat => $result:expr),*) => {
        impl<'a, 'b, 'c, 'd, 'e> $crate::State<$events, $states, $shr_data> for $state {
            #[allow(unused_variables)]
            fn handle_event(&mut self, $shr: &mut $shr_data, $evt: &$crate::Event<$events>, $probe: bool) -> $crate::Action<$states> {
                match *$evt {
                    $( $pat => $result),*
                }
            }
        }
    }
}

#[macro_export]
macro_rules! hsm_state_parents {
    ($st_en:ident ; $($nam:ident -> $parent:ident),*) => {
        $(_hsm_impl_state_parent!($st_en ; $nam -> $parent);)*
    }
}

#[macro_export]
macro_rules! _hsm_impl_state_parent {
    ($st_en:ident ; $nam:ident -> None) => {
        impl $crate::Parent<$st_en> for $nam {
            fn get_parent() -> Option<$st_en> { None }
        }
    };
    ($st_en:ident ; $nam:ident -> $parent:ident) => {
        impl $crate::Parent<$st_en> for $nam {
            fn get_parent() -> Option<$st_en> { Some($st_en::$parent) }
        }
    }
}

#[macro_export]
macro_rules! _hsm_create_states {
    ( $($s:ident),* ) => {
        $(_hsm_create_state!($s);)*
    };
    ($($s:ident $x:tt),*) => {
        $(_hsm_create_state!($s $x);)*
    }
}

#[macro_export]
macro_rules! _hsm_create_state_common {
    ($nam:ident) => {
        impl $crate::Name for $nam {
            fn name(&self) -> &'static str {
                stringify!($nam)
            }
        }
    }
}

#[macro_export]
macro_rules! _hsm_create_state {
    ($nam:ident) => {
        #[derive(Debug)]
        pub struct $nam;
        impl $crate::Initializer for $nam {
            fn new() -> Self {
                $nam
            }
        }
        _hsm_create_state_common!($nam);
    };
    ($nam:ident { $($field_name:ident : $field_type:ty = $field_default:expr),* }) => {
        #[derive(Debug)]
        pub struct $nam {
            _phantom        : ::std::marker::PhantomData<u8>,
            $( $field_name  : $field_type ),*
        }
        impl $crate::Initializer for $nam {
            fn new() -> Self {
                $nam {
                    _phantom        : ::std::marker::PhantomData,
                    $( $field_name  : $field_default ),*
                }
            }
        }
        _hsm_create_state_common!($nam);
    }
}

#[macro_export]
macro_rules! _hsm_create_state_enum {
    ($st_en:ident, ($($s:ident),*) ) => {
        #[derive(Debug, Clone, Eq, PartialEq)]
        pub enum $st_en {
            $( $s ),*
        }
        impl ::std::fmt::Display for $st_en {
            fn fmt(&self, f:&mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
                match *self {
                    $( $st_en::$s => try!(::std::fmt::Display::fmt(stringify!($s), f)) ),*
                };
                Ok(())
            }
        }
        impl $crate::InstanceParent<$st_en> for $st_en {
            fn get_parent(&self) -> Option<$st_en> {
                match *self {
                    $( $st_en::$s => $s::get_parent() ),*
                }
            }
        }
    }
}

#[macro_export]
macro_rules! _hsm_create_state_struct {
    ($st_str:ident, $st_en:ident, $st_evt:ty, $shr_dat:ident, ($($s:ident),*) ) => {
        #[derive(Debug)]
        #[allow(non_snake_case)]
        pub struct $st_str {
            $( $s : $s ),*
        }
        impl $crate::Initializer for $st_str {
            fn new() -> Self {
                $st_str {
                    $( $s : $s::new() ),*
                }
            }
        }
        impl<'a, 'b, 'c, 'd, 'e> $crate::StateLookup<$st_en, $st_evt, $shr_dat> for $st_str {
            fn lookup(&mut self, typ: &$st_en) -> &mut $crate::State<$st_evt, $st_en, $shr_dat> {
                match *typ {
                    $($st_en::$s => &mut self.$s ),*
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate file_api;

use std::io::SeekFrom;

#[test]
fn file_exists() {
	let file = "tests/sample_data_file.txt".to_string();
	assert!(file_api::reader::exists(&file) == true);
}

#[test]
fn file_not_exists() {
	let file = "tests/file_not_exists.toml".to_string();
	assert!(file_api::reader::exists(&file) == false);
}

#[test]
fn url_exists() {
	let file = "http://google.com".to_string();
	assert!(file_api::reader::exists(&file) == true);
}

#[test]
fn url_not_exists() {
	let file = "http://no_dns.bad".to_string();
	assert!(file_api::reader::exists(&file) == false);
}

#[test]
fn read_content() {
	// let file = "http://localhost:4000/api/cards".to_string();
	// let file = "http://google.com/api".to_string();
	// let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/2015_GF_ORF_01_07_05.mxf".to_string();
	let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/freeMXF-mxf1a.mxf".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let data = reader.read(16).unwrap();
	assert!(data.len() == 16);
	assert!(reader.get_position().unwrap() == 16);

	reader.read(2).unwrap();
	assert!(reader.get_position().unwrap() == 18);
}

#[test]
fn seek_content() {
	// let file = "http://localhost:4000/api/cards".to_string();
	// let file = "http://google.com/api".to_string();
	// let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/2015_GF_ORF_01_07_05.mxf".to_string();
	let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/freeMXF-mxf1a.mxf".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let new_position = reader.seek(SeekFrom::Start(16)).unwrap();
	assert!(new_position == 16);
	assert!(reader.get_position().unwrap() == 16);

	let new_position = reader.seek(SeekFrom::Start(8)).unwrap();
	assert!(new_position == 8);
	assert!(reader.get_position().unwrap() == 8);

	let new_position = reader.seek(SeekFrom::Current(2)).unwrap();
	assert!(new_position == 10);
	assert!(reader.get_position().unwrap() == 10);

	reader.read(2).unwrap();
	assert!(reader.get_position().unwrap() == 12);
}

#[test]
fn file_content_size() {
	let file = "tests/sample_data_file.txt".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let size = reader.get_size().unwrap();
	println!("{:?}", size);
	assert!(size == 20);
}

#[test]
fn http_content_size() {
	let file = "http://www.nomalab.com/".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let size = reader.get_size().unwrap();
	println!("{:?}", size);
	assert!(size == 3399);
}

#[test]
fn http_end_of_content() {
	let file = "http://www.nomalab.com/".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let size = reader.get_size().unwrap();
	let _new_position = reader.seek(SeekFrom::Start(size + 5));

	let result = reader.read(1);
	assert!(result == Err("Out of range: 3404 > 3399".to_string()));
}
<commit_msg>update unit tests<commit_after>extern crate file_api;

use std::io::SeekFrom;

#[test]
fn file_exists() {
	let file = "tests/sample_data_file.txt".to_string();
	assert!(file_api::reader::exists(&file) == true);
}

#[test]
fn file_not_exists() {
	let file = "tests/file_not_exists.toml".to_string();
	assert!(file_api::reader::exists(&file) == false);
}

#[test]
fn url_exists() {
	let file = "http://google.com".to_string();
	assert!(file_api::reader::exists(&file) == true);
}

#[test]
fn url_not_exists() {
	let file = "http://no_dns.bad".to_string();
	assert!(file_api::reader::exists(&file) == false);
}

#[test]
fn read_content() {
	// let file = "http://localhost:4000/api/cards".to_string();
	// let file = "http://google.com/api".to_string();
	// let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/2015_GF_ORF_01_07_05.mxf".to_string();
	let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/freeMXF-mxf1a.mxf".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let data = reader.read(16).unwrap();
	assert!(data.len() == 16);
	assert!(reader.get_position().unwrap() == 16);

	reader.read(2).unwrap();
	assert!(reader.get_position().unwrap() == 18);
}

#[test]
fn seek_content() {
	// let file = "http://localhost:4000/api/cards".to_string();
	// let file = "http://google.com/api".to_string();
	// let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/2015_GF_ORF_01_07_05.mxf".to_string();
	let file = "https://s3-us-west-2.amazonaws.com/ebucvingest/freeMXF-mxf1a.mxf".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let new_position = reader.seek(SeekFrom::Start(16)).unwrap();
	assert!(new_position == 16);
	assert!(reader.get_position().unwrap() == 16);

	let new_position = reader.seek(SeekFrom::Start(8)).unwrap();
	assert!(new_position == 8);
	assert!(reader.get_position().unwrap() == 8);

	let new_position = reader.seek(SeekFrom::Current(2)).unwrap();
	assert!(new_position == 10);
	assert!(reader.get_position().unwrap() == 10);

	reader.read(2).unwrap();
	assert!(reader.get_position().unwrap() == 12);
}

#[test]
fn file_content_size() {
	let file = "tests/sample_data_file.txt".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let size = reader.get_size().unwrap();
	println!("{:?}", size);
	assert!(size == 20);
}

#[test]
fn http_content_size() {
	let file = "http://www.nomalab.com/".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let size = reader.get_size().unwrap();
	println!("{:?}", size);
	assert!(size == 3399);
}

#[test]
fn http_end_of_content() {
	let file = "http://www.nomalab.com/".to_string();

	let mut reader = file_api::reader::open(file).unwrap();

	let size = reader.get_size().unwrap();
	let _new_position = reader.seek(SeekFrom::Start(size + 5));

	let result = reader.read(1);
	assert!(result.is_ok());
	let content = result.unwrap();
	assert!(content.len() == 0);
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

use crate::{api::prelude::*, prelude::*, proc_macros::*, theme::prelude::*};

#[derive(Debug, Clone, PartialEq)]
enum PagerAction {
    Next,
    Previous,
    Navigate(usize),
    Remove(usize),
    Insert(usize, Entity),
}

#[derive(Default, Clone, Debug, AsAny)]
pub struct PagerState {
    stack: Vec<Entity>,
    current_index: usize,
    actions: VecDeque<PagerAction>,
}

impl PagerState {
    /// Navigates to the next child. If the current child is the last in the list nothing will happen.
    pub fn next(&mut self) {
        self.actions.push_front(PagerAction::Next);
    }

    /// Navigates to the previous child. If the current child is the first in the list nothing will happen.
    pub fn previous(&mut self) {
        self.actions.push_front(PagerAction::Previous);
    }

    /// Navigates to the given index. Is the index out of bounds nothing will happen.
    pub fn navigate(&mut self, index: usize) {
        self.actions.push_front(PagerAction::Navigate(index));
    }

    /// Removes the child on the given index. If the index is out of bounds nothing will happen.
    pub fn remove(&mut self, index: usize) {
        self.actions.push_front(PagerAction::Remove(index));
    }

    /// Inserts a child on the given position.
    pub fn insert(&mut self, index: usize, entity: Entity) {
        self.actions.push_front(PagerAction::Insert(index, entity));
    }
}

impl State for PagerState {
    fn init(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        for child in self.stack.pop() {
            ctx.append_child_entity(child);
        }
    }

    fn update(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        if let Some(action) = self.actions.pop_front() {
            match action {
                PagerAction::Next => Pager::next(ctx, ctx.entity),
                PagerAction::Previous => Pager::previous(ctx, ctx.entity),
                PagerAction::Navigate(index) => Pager::navigate(ctx, ctx.entity, index),
                PagerAction::Remove(index) => Pager::remove(ctx, ctx.entity, index),
                PagerAction::Insert(index, entity) => {
                    Pager::insert(ctx, ctx.entity, index, entity)
                }
            }
        }
    }
}

widget!(
    Pager<PagerState> {
        cached: bool,
        current_index: usize
    }
);

impl Pager {
    /// Appends an widget as entity to the navigation stack.
    pub fn push<W: Widget>(mut self, child: Entity) -> Self {
        self.state_mut().stack.push(child);
        self
    }

    /// Navigates to the next child. If the current child is the last in the list nothing will happen.

    // todo use Context as parameter and entity!!!
    pub fn next(ctx: &mut Context, entity: Entity) {
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));

        let current_index = *Pager::current_index_ref(&ctx.get_widget(entity));

        if let Some(count) = ctx.get_widget(entity).children_count() {
            if current_index + 1 >= count {
                return;
            }

            Pager::current_index_set(&mut ctx.get_widget(entity), current_index + 1);
        }
    }

    /// Navigates to the previous child. If the current child is the first in the list nothing will happen.
    pub fn previous(ctx: &mut Context, entity: Entity) {
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));

        let current_index = *Pager::current_index_ref(&ctx.get_widget(entity));

        if current_index == 0 {
            return;
        }

        Pager::current_index_set(&mut ctx.get_widget(entity), current_index - 1);
    }

    /// Navigates to the given index. Is the index out of bounds nothing will happen.
    pub fn navigate(ctx: &mut Context, entity: Entity, index: usize) {
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));
    }

    /// Removes the child on the given index. If the index is out of bounds nothing will happen.
    pub fn remove(ctx: &mut Context, entity: Entity, index: usize) {
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));
    }

    /// Inserts a child on the given position.
    pub fn insert(ctx: &mut Context, entity: Entity, index: usize, child: Entity) {
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));
    }
}

impl Template for Pager {
    fn template(self, _id: Entity, _context: &mut BuildContext) -> Self {
        self.name("Pager").cached(true)
    }
}
<commit_msg>pager udpate (wip)<commit_after>use std::collections::VecDeque;

use crate::{api::prelude::*, prelude::*, proc_macros::*, theme::prelude::*};

#[derive(Debug, Clone, PartialEq)]
enum PagerAction {
    Next,
    Previous,
    Navigate(usize),
    Remove(usize),
    Insert(usize, Entity),
}

#[derive(Default, Clone, Debug, AsAny)]
pub struct PagerState {
    current_index: usize,
    actions: VecDeque<PagerAction>,
}

impl PagerState {
    /// Navigates to the next child. If the current child is the last in the list nothing will happen.
    pub fn next(&mut self) {
        self.actions.push_front(PagerAction::Next);
    }

    /// Navigates to the previous child. If the current child is the first in the list nothing will happen.
    pub fn previous(&mut self) {
        self.actions.push_front(PagerAction::Previous);
    }

    /// Navigates to the given index. Is the index out of bounds nothing will happen.
    pub fn navigate(&mut self, index: usize) {
        self.actions.push_front(PagerAction::Navigate(index));
    }

    /// Removes the child on the given index. If the index is out of bounds nothing will happen.
    pub fn remove(&mut self, index: usize) {
        self.actions.push_front(PagerAction::Remove(index));
    }

    /// Inserts a child on the given position.
    pub fn insert(&mut self, index: usize, entity: Entity) {
        self.actions.push_front(PagerAction::Insert(index, entity));
    }
}

impl State for PagerState {
    fn init(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        // todo check if current_index out of bounds and correct

        if let Some(count) = ctx.widget().children_count() {
            for i in 0..count {
                // make child on the current index always visible (todo: use current_index)
                if i == 0 {
                    if let Some(child) = &mut ctx.try_child_from_index(i) {
                        child.set("visibility", Visibility::Visible);
                    }

                    continue;
                }

                // collapse all other children
                if let Some(child) = &mut ctx.try_child_from_index(i) {
                    child.set("visibility", Visibility::Collapsed);
                }
            }
        }

            // todo initial next previous enabled
    }

    fn update(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        if let Some(action) = self.actions.pop_front() {
            match action {
                PagerAction::Next => Pager::next(ctx, ctx.entity),
                PagerAction::Previous => Pager::previous(ctx, ctx.entity),
                PagerAction::Navigate(index) => Pager::navigate(ctx, ctx.entity, index),
                PagerAction::Remove(index) => Pager::remove(ctx, ctx.entity, index),
                PagerAction::Insert(index, entity) => {
                    Pager::insert(ctx, ctx.entity, index, entity)
                }
            }
        }
    }
}

widget!(
    Pager<PagerState> {
        current_index: usize
    }
);

impl Pager {
    /// Navigates to the next child. If the current child is the last in the list nothing will happen.
    pub fn next(ctx: &mut Context, entity: Entity) {
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));

        let current_index = *Pager::current_index_ref(&ctx.get_widget(entity));
        let next_index = current_index + 1;

        if let Some(count) = ctx.get_widget(entity).children_count() {
            if next_index >= count {
                return;
            }

            // todo next, previous enabled flags or methods
            if let Some(child) = &mut ctx.try_child_from_index(current_index) {
                child.set("visibility", Visibility::Collapsed);
            }

            if let Some(child) = &mut ctx.try_child_from_index(next_index) {
                child.set("visibility", Visibility::Visible);
            }

            Pager::current_index_set(&mut ctx.get_widget(entity), next_index);

            // next index add end set next enabled to false
        }
    }

    /// Navigates to the previous child. If the current child is the first in the list nothing will happen.
    pub fn previous(ctx: &mut Context, entity: Entity) {
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));

        let current_index = *Pager::current_index_ref(&ctx.get_widget(entity));

        if current_index == 0 {
            return;
        }

        Pager::current_index_set(&mut ctx.get_widget(entity), current_index - 1);
    }

    /// Navigates to the given index. Is the index out of bounds nothing will happen.
    pub fn navigate(ctx: &mut Context, entity: Entity, index: usize) {
        // update enabled next / previous
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));
    }

    /// Removes the child on the given index. If the index is out of bounds nothing will happen.
    pub fn remove(ctx: &mut Context, entity: Entity, index: usize) {
        // update enabled next / previous
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));
    }

    /// Inserts a child on the given position.
    pub fn insert(ctx: &mut Context, entity: Entity, index: usize, child: Entity) {
        // update enabled next / previous
        Pager::panics_on_wrong_type(&ctx.get_widget(entity));
    }
}

impl Template for Pager {
    fn template(self, _id: Entity, _context: &mut BuildContext) -> Self {
        self.name("Pager")

        // todo current index changed =>
    }
}
<|endoftext|>


<commit_before>use std::hash::{Hash};
use std::slice::{
    Items,
};
use std::fmt;
use test;

#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct NodeIndex(uint);
#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct EdgeIndex(uint);

const EdgeEnd: EdgeIndex = EdgeIndex(::std::uint::MAX);
//const InvalidNode: NodeIndex = NodeIndex(::std::uint::MAX);

/// Index into the EdgeIndex arrays
enum Dir {
    Out = 0,
    In = 1
}

#[deriving(Show)]
pub struct Node<N> {
    pub data: N,
    next: [EdgeIndex, ..2],
}

#[deriving(Show, Copy)]
pub struct Edge<E> {
    pub data: E,
    next: [EdgeIndex, ..2],
    a: NodeIndex,
    b: NodeIndex,
}

/// **OGraph\<N, E\>** is a directed graph using an adjacency list representation.
///
/// The graph maintains unique indices for nodes and edges, so both node and edge
/// data may be accessed mutably. Removing nodes or edges from the graph is expensive,
/// while adding is very cheap.
///
/// Based upon the graph implementation in rustc.
//#[deriving(Show)]
pub struct OGraph<N> {
    nodes: Vec<Node<N>>,
    edges: Vec<Edge<()>>,
}

impl<N: fmt::Show> fmt::Show for OGraph<N>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for n in self.nodes.iter() {
            try!(writeln!(f, "{}", n));
        }
        for n in self.edges.iter() {
            try!(writeln!(f, "{}", n));
        }
        Ok(())
    }
}

pub enum Pair<'a, T: 'a> {
    Both(&'a mut T, &'a mut T),
    One(&'a mut T),
    None,
}

pub fn index_twice<T>(slc: &mut [T], a: uint, b: uint) -> Pair<T>
{
    if a == b {
        slc.get_mut(a).map_or(Pair::None, Pair::One)
    } else {
        if a >= slc.len() || b >= slc.len() {
            Pair::None
        } else {
            // safe because a, b are in bounds and distinct
            unsafe {
                let ar = &mut *(slc.unsafe_mut(a) as *mut _);
                let br = &mut *(slc.unsafe_mut(b) as *mut _);
                Pair::Both(ar, br)
            }
        }
    }
}

impl<N> OGraph<N>
//where N: fmt::Show
{
    pub fn new() -> OGraph<N>
    {
        OGraph{nodes: Vec::new(), edges: Vec::new()}
    }

    pub fn add_node(&mut self, data: N) -> NodeIndex
    {
        let node = Node{data: data, next: [EdgeEnd, EdgeEnd]};
        let node_idx = NodeIndex(self.nodes.len());
        self.nodes.push(node);
        node_idx
    }

    pub fn node(&self, a: NodeIndex) -> Option<&N>
    {
        self.nodes.get(a.0).map(|n| &n.data)
    }

    pub fn node_mut(&mut self, a: NodeIndex) -> Option<&mut N>
    {
        self.nodes.get_mut(a.0).map(|n| &mut n.data)
    }

    pub fn neighbors(&self, a: NodeIndex) -> Neighbors<N>
    {
        Neighbors{
            graph: self,
            next: match self.nodes.get(a.0) {
                None => EdgeEnd,
                Some(n) => n.next[0],
            }
        }
    }

    pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex) -> EdgeIndex
    {
        let edge_idx = EdgeIndex(self.edges.len());
        match index_twice(self.nodes[mut], a.0, b.0) {
            Pair::None => panic!("NodeIndices out of bounds"),
            Pair::One(an) => {
                let edge = Edge {
                    data: (),
                    a: a,
                    b: b,
                    next: an.next,
                };
                an.next[0] = edge_idx;
                an.next[1] = edge_idx;
                self.edges.push(edge);
            }
            Pair::Both(an, bn) => {
                // a and b are different indices
                let edge = Edge {
                    data: (),
                    a: a,
                    b: b,
                    next: [an.next[0], bn.next[1]],
                };
                an.next[0] = edge_idx;
                bn.next[1] = edge_idx;
                self.edges.push(edge);
            }
        }
        edge_idx
    }

    pub fn remove_node(&mut self, a: NodeIndex) -> Option<N>
    {
        match self.nodes.get(a.0) {
            None => return None,
            _ => {}
        }
        // Remove all edges from and to this node.
        loop {
            let next = self.nodes[a.0].next[0];
            if next == EdgeEnd {
                break
            }
            //println!("Rmove edge {}", next);
            self.remove_edge(next);
        }

        loop {
            let next = self.nodes[a.0].next[1];
            if next == EdgeEnd {
                break
            }
            //println!("Rmove edge {}", next);
            self.remove_edge(next);
        }

        //println!("REMOVED EDGES: {}", self);

        // Adjust all node indices affected
        for edge in self.edges.iter_mut() {
            debug_assert!(edge.a != a);
            debug_assert!(edge.b != a);
            if edge.a > a {
                edge.a.0 -= 1;
            }
            if edge.b > a {
                edge.b.0 -= 1;
            }
        }
        self.nodes.remove(a.0).map(|n|n.data)
    }

    pub fn edge_mut(&mut self, e: EdgeIndex) -> &mut Edge<()>
    {
        &mut self.edges[e.0]
    }

    pub fn remove_edge(&mut self, e: EdgeIndex)
    {
        fn update_edge_list(replace: EdgeIndex, fst: EdgeIndex, edges: &mut [Edge<()>], d: Dir) {
            debug_assert!(fst != replace);
            let k = d as uint;
            let edge_next = edges[replace.0].next[k];
            // walk through edge list
            let mut cur = fst;
            while cur != EdgeEnd {
                let curedge = &mut edges[cur.0];
                if curedge.next[k] == replace {
                    //println!("Have to replace link in {}", curedge);
                    curedge.next[k] = edge_next;
                    break
                } else {
                    cur = curedge.next[k];
                }
            }
        }
        // every edge is part of two lists,
        // outgoing and incoming edges.
        // Remove it from both
        let (edge_a, edge_b, edge_next) = match self.edges.get(e.0) {
            None => return,
            Some(x) => (x.a, x.b, x.next),
        };
        {
            // List out from A
            let node = &mut self.nodes[edge_a.0];
            let fst = node.next[0];
            if fst == e {
                node.next[0] = edge_next[0];
            } else {
                update_edge_list(e, fst, self.edges[mut], Dir::Out);
            }
        }
        {
            // List in to B
            let node = &mut self.nodes[edge_b.0];
            let fst = node.next[1];
            if fst == e {
                node.next[1] = edge_next[1];
            } else {
                update_edge_list(e, fst, self.edges[mut], Dir::In);
            }
        }
        self.remove_edge_adjust_indices(e);
    }

    fn remove_edge_adjust_indices(&mut self, e: EdgeIndex) -> Option<()>
    {
        // Edge lists are fine, so remove the edge and adjust all edge indices
        let edge_data = self.edges.remove(e.0).unwrap().data;
        for node in self.nodes.iter_mut() {
            for &k in [0u, 1].iter() {
                debug_assert!(node.next[k] != e);
                if node.next[k] != EdgeEnd && node.next[k] > e {
                    node.next[k].0 -= 1;
                }
            }
        }
        for edge in self.edges.iter_mut() {
            for &k in [0u, 1].iter() {
                debug_assert!(edge.next[k] != e);
                if edge.next[k] != EdgeEnd && edge.next[k] > e {
                    edge.next[k].0 -= 1;
                }
            }
        }
        Some(edge_data)
    }
}

pub struct Neighbors<'a, N: 'a> {
    graph: &'a OGraph<N>,
    next: EdgeIndex,
}

impl<'a, N> Iterator<NodeIndex> for Neighbors<'a, N>
{
    fn next(&mut self) -> Option<NodeIndex>
    {
        match self.graph.edges.get(self.next.0) {
            None => None,
            Some(edge) => {
                self.next = edge.next[0];
                Some(edge.b)
            }
        }
    }
}

#[bench]
fn bench_inser(b: &mut test::Bencher) {
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    for x in range(1, 125) {
        let n = og.add_node(x);
        og.add_edge(fst, n);
    }
    b.iter(|| {
        og.add_node(1)
    })
}

#[bench]
fn bench_remove(b: &mut test::Bencher) {
    // removal is very slow in a big graph.
    // and this one doesn't even have many nodes.
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    let mut prev = fst;
    for x in range(1, 1250) {
        let n = og.add_node(x);
        og.add_edge(prev, n);
        prev = n;
    }
    b.iter(|| {
        og.remove_node(fst)
    })
}
<commit_msg>Switch order of loops for better code<commit_after>use std::hash::{Hash};
use std::slice::{
    Items,
};
use std::fmt;
use test;

#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct NodeIndex(uint);
#[deriving(Copy, Clone, Show, PartialEq, PartialOrd, Eq, Hash)]
pub struct EdgeIndex(uint);

const EdgeEnd: EdgeIndex = EdgeIndex(::std::uint::MAX);
//const InvalidNode: NodeIndex = NodeIndex(::std::uint::MAX);

/// Index into the EdgeIndex arrays
enum Dir {
    Out = 0,
    In = 1
}

#[deriving(Show)]
pub struct Node<N> {
    pub data: N,
    next: [EdgeIndex, ..2],
}

#[deriving(Show, Copy)]
pub struct Edge<E> {
    pub data: E,
    next: [EdgeIndex, ..2],
    a: NodeIndex,
    b: NodeIndex,
}

/// **OGraph\<N, E\>** is a directed graph using an adjacency list representation.
///
/// The graph maintains unique indices for nodes and edges, so both node and edge
/// data may be accessed mutably. Removing nodes or edges from the graph is expensive,
/// while adding is very cheap.
///
/// Based upon the graph implementation in rustc.
//#[deriving(Show)]
pub struct OGraph<N> {
    nodes: Vec<Node<N>>,
    edges: Vec<Edge<()>>,
}

impl<N: fmt::Show> fmt::Show for OGraph<N>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for n in self.nodes.iter() {
            try!(writeln!(f, "{}", n));
        }
        for n in self.edges.iter() {
            try!(writeln!(f, "{}", n));
        }
        Ok(())
    }
}

pub enum Pair<'a, T: 'a> {
    Both(&'a mut T, &'a mut T),
    One(&'a mut T),
    None,
}

pub fn index_twice<T>(slc: &mut [T], a: uint, b: uint) -> Pair<T>
{
    if a == b {
        slc.get_mut(a).map_or(Pair::None, Pair::One)
    } else {
        if a >= slc.len() || b >= slc.len() {
            Pair::None
        } else {
            // safe because a, b are in bounds and distinct
            unsafe {
                let ar = &mut *(slc.unsafe_mut(a) as *mut _);
                let br = &mut *(slc.unsafe_mut(b) as *mut _);
                Pair::Both(ar, br)
            }
        }
    }
}

impl<N> OGraph<N>
//where N: fmt::Show
{
    pub fn new() -> OGraph<N>
    {
        OGraph{nodes: Vec::new(), edges: Vec::new()}
    }

    pub fn add_node(&mut self, data: N) -> NodeIndex
    {
        let node = Node{data: data, next: [EdgeEnd, EdgeEnd]};
        let node_idx = NodeIndex(self.nodes.len());
        self.nodes.push(node);
        node_idx
    }

    pub fn node(&self, a: NodeIndex) -> Option<&N>
    {
        self.nodes.get(a.0).map(|n| &n.data)
    }

    pub fn node_mut(&mut self, a: NodeIndex) -> Option<&mut N>
    {
        self.nodes.get_mut(a.0).map(|n| &mut n.data)
    }

    pub fn neighbors(&self, a: NodeIndex) -> Neighbors<N>
    {
        Neighbors{
            graph: self,
            next: match self.nodes.get(a.0) {
                None => EdgeEnd,
                Some(n) => n.next[0],
            }
        }
    }

    pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex) -> EdgeIndex
    {
        let edge_idx = EdgeIndex(self.edges.len());
        match index_twice(self.nodes[mut], a.0, b.0) {
            Pair::None => panic!("NodeIndices out of bounds"),
            Pair::One(an) => {
                let edge = Edge {
                    data: (),
                    a: a,
                    b: b,
                    next: an.next,
                };
                an.next[0] = edge_idx;
                an.next[1] = edge_idx;
                self.edges.push(edge);
            }
            Pair::Both(an, bn) => {
                // a and b are different indices
                let edge = Edge {
                    data: (),
                    a: a,
                    b: b,
                    next: [an.next[0], bn.next[1]],
                };
                an.next[0] = edge_idx;
                bn.next[1] = edge_idx;
                self.edges.push(edge);
            }
        }
        edge_idx
    }

    pub fn remove_node(&mut self, a: NodeIndex) -> Option<N>
    {
        match self.nodes.get(a.0) {
            None => return None,
            _ => {}
        }
        // Remove all edges from and to this node.
        loop {
            let next = self.nodes[a.0].next[0];
            if next == EdgeEnd {
                break
            }
            //println!("Rmove edge {}", next);
            self.remove_edge(next);
        }

        loop {
            let next = self.nodes[a.0].next[1];
            if next == EdgeEnd {
                break
            }
            //println!("Rmove edge {}", next);
            self.remove_edge(next);
        }

        //println!("REMOVED EDGES: {}", self);

        // Adjust all node indices affected
        for edge in self.edges.iter_mut() {
            debug_assert!(edge.a != a);
            debug_assert!(edge.b != a);
            if edge.a > a {
                edge.a.0 -= 1;
            }
            if edge.b > a {
                edge.b.0 -= 1;
            }
        }
        self.nodes.remove(a.0).map(|n|n.data)
    }

    pub fn edge_mut(&mut self, e: EdgeIndex) -> &mut Edge<()>
    {
        &mut self.edges[e.0]
    }

    pub fn remove_edge(&mut self, e: EdgeIndex)
    {
        fn update_edge_list(replace: EdgeIndex, fst: EdgeIndex, edges: &mut [Edge<()>], d: Dir) {
            debug_assert!(fst != replace);
            let k = d as uint;
            let edge_next = edges[replace.0].next[k];
            // walk through edge list
            let mut cur = fst;
            while cur != EdgeEnd {
                let curedge = &mut edges[cur.0];
                if curedge.next[k] == replace {
                    //println!("Have to replace link in {}", curedge);
                    curedge.next[k] = edge_next;
                    break
                } else {
                    cur = curedge.next[k];
                }
            }
        }
        // every edge is part of two lists,
        // outgoing and incoming edges.
        // Remove it from both
        let (edge_a, edge_b, edge_next) = match self.edges.get(e.0) {
            None => return,
            Some(x) => (x.a, x.b, x.next),
        };
        {
            // List out from A
            let node = &mut self.nodes[edge_a.0];
            let fst = node.next[0];
            if fst == e {
                node.next[0] = edge_next[0];
            } else {
                update_edge_list(e, fst, self.edges[mut], Dir::Out);
            }
        }
        {
            // List in to B
            let node = &mut self.nodes[edge_b.0];
            let fst = node.next[1];
            if fst == e {
                node.next[1] = edge_next[1];
            } else {
                update_edge_list(e, fst, self.edges[mut], Dir::In);
            }
        }
        self.remove_edge_adjust_indices(e);
    }

    fn remove_edge_adjust_indices(&mut self, e: EdgeIndex) -> Option<()>
    {
        // Edge lists are fine, so remove the edge and adjust all edge indices in nodes
        let edge_data = self.edges.remove(e.0).unwrap().data;
        for &k in [0u, 1].iter() {
            for node in self.nodes.iter_mut() {
                debug_assert!(node.next[k] != e);
                if node.next[k] != EdgeEnd && node.next[k] > e {
                    node.next[k].0 -= 1;
                }
            }
            for edge in self.edges.iter_mut() {
                debug_assert!(edge.next[k] != e);
                if edge.next[k] != EdgeEnd && edge.next[k] > e {
                    edge.next[k].0 -= 1;
                }
            }
        }
        Some(edge_data)
    }
}

pub struct Neighbors<'a, N: 'a> {
    graph: &'a OGraph<N>,
    next: EdgeIndex,
}

impl<'a, N> Iterator<NodeIndex> for Neighbors<'a, N>
{
    fn next(&mut self) -> Option<NodeIndex>
    {
        match self.graph.edges.get(self.next.0) {
            None => None,
            Some(edge) => {
                self.next = edge.next[0];
                Some(edge.b)
            }
        }
    }
}

#[bench]
fn bench_inser(b: &mut test::Bencher) {
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    for x in range(1, 125) {
        let n = og.add_node(x);
        og.add_edge(fst, n);
    }
    b.iter(|| {
        og.add_node(1)
    })
}

#[bench]
fn bench_remove(b: &mut test::Bencher) {
    // removal is very slow in a big graph.
    // and this one doesn't even have many nodes.
    let mut og = OGraph::new();
    let fst = og.add_node(0i);
    let mut prev = fst;
    for x in range(1, 1250) {
        let n = og.add_node(x);
        og.add_edge(prev, n);
        prev = n;
    }
    //println!("{}", og);
    b.iter(|| {
        og.remove_node(fst)
    })
}
<|endoftext|>


<commit_before>#![cfg(target_os = "windows")]

pub mod ffi {
   #![allow(non_snake_case)]

   pub use libc::{
      c_int, c_uint, c_void, uintptr_t, c_long, c_ulong
   };
   pub use libc::types::os::arch::extra::{
      HANDLE, LONG_PTR, LRESULT, HINSTANCE, LPCWSTR, HMODULE, LPVOID, BOOL
   };
   use std::mem;

   pub type HWND = HANDLE;
   pub type HICON = HANDLE;
   pub type HBRUSH = HANDLE;
   pub type HMENU = HANDLE;
   pub type HDC = HANDLE;
   pub type HCURSOR = HICON;

   pub type WPARAM = uintptr_t;
   pub type LPARAM = LONG_PTR;

   pub type ATOM = u16;

   pub type WNDPROC = Option<unsafe extern "system" fn(HWND, c_uint, WPARAM, LPARAM) -> LRESULT>;

   pub const CS_VREDRAW:                 c_ulong = 0x00000001;
   pub const CS_HREDRAW:                 c_ulong = 0x00000002;
   pub const CS_DBLCLKS:                 c_ulong = 0x00000008;
   pub const CS_OWNDC:                   c_ulong = 0x00000020;
   pub const CS_CLASSDC:                 c_ulong = 0x00000040;
   pub const CS_PARENTDC:                c_ulong = 0x00000080;
   pub const CS_NOCLOSE:                 c_ulong = 0x00000200;
   pub const CS_SAVEBITS:                c_ulong = 0x00000800;
   pub const CS_BYTEALIGNCLIENT:         c_ulong = 0x00001000;
   pub const CS_BYTEALIGNWINDOW:         c_ulong = 0x00002000;
   pub const CS_GLOBALCLASS:             c_ulong = 0x00004000;
   pub const CS_IME:                     c_ulong = 0x00010000;
   pub const CS_DROPSHADOW:              c_ulong = 0x00020000;

   pub const WS_OVERLAPPED:              c_ulong = 0x00000000;
   pub const WS_POPUP:                   c_ulong = 0x80000000;
   pub const WS_CHILD:                   c_ulong = 0x40000000;
   pub const WS_MINIMIZE:                c_ulong = 0x20000000;
   pub const WS_VISIBLE:                 c_ulong = 0x10000000;
   pub const WS_DISABLED:                c_ulong = 0x08000000;
   pub const WS_CLIPSIBLINGS:            c_ulong = 0x04000000;
   pub const WS_CLIPCHILDREN:            c_ulong = 0x02000000;
   pub const WS_MAXIMIZE:                c_ulong = 0x01000000;
   pub const WS_CAPTION:                 c_ulong = 0x00C00000;
   pub const WS_BORDER:                  c_ulong = 0x00800000;
   pub const WS_DLGFRAME:                c_ulong = 0x00400000;
   pub const WS_VSCROLL:                 c_ulong = 0x00200000;
   pub const WS_HSCROLL:                 c_ulong = 0x00100000;
   pub const WS_SYSMENU:                 c_ulong = 0x00080000;
   pub const WS_THICKFRAME:              c_ulong = 0x00040000;
   pub const WS_GROUP:                   c_ulong = 0x00020000;
   pub const WS_TABSTOP:                 c_ulong = 0x00010000;
   pub const WS_MINIMIZEBOX:             c_ulong = 0x00020000;
   pub const WS_MAXIMIZEBOX:             c_ulong = 0x00010000;
   pub const WS_TILED:                   c_ulong = WS_OVERLAPPED;
   pub const WS_ICONIC:                  c_ulong = WS_MINIMIZE;
   pub const WS_SIZEBOX:                 c_ulong = WS_THICKFRAME;
   pub const WS_TILEDWINDOW:             c_ulong = WS_OVERLAPPEDWINDOW;
   pub const WS_OVERLAPPEDWINDOW:        c_ulong = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
   pub const WS_POPUPWINDOW:             c_ulong = WS_POPUP | WS_BORDER | WS_SYSMENU;
   pub const WS_CHILDWINDOW:             c_ulong = WS_CHILD;

   pub const WS_EX_DLGMODALFRAME:        c_ulong = 0x00000001;
   pub const WS_EX_NOPARENTNOTIFY:       c_ulong = 0x00000004;
   pub const WS_EX_TOPMOST:              c_ulong = 0x00000008;
   pub const WS_EX_ACCEPTFILES:          c_ulong = 0x00000010;
   pub const WS_EX_TRANSPARENT:          c_ulong = 0x00000020;
   pub const WS_EX_MDICHILD:             c_ulong = 0x00000040;
   pub const WS_EX_TOOLWINDOW:           c_ulong = 0x00000080;
   pub const WS_EX_WINDOWEDGE:           c_ulong = 0x00000100;
   pub const WS_EX_CLIENTEDGE:           c_ulong = 0x00000200;
   pub const WS_EX_CONTEXTHELP:          c_ulong = 0x00000400;
   pub const WS_EX_RIGHT:                c_ulong = 0x00001000;
   pub const WS_EX_LEFT:                 c_ulong = 0x00000000;
   pub const WS_EX_RTLREADING:           c_ulong = 0x00002000;
   pub const WS_EX_LTRREADING:           c_ulong = 0x00000000;
   pub const WS_EX_LEFTSCROLLBAR:        c_ulong = 0x00004000;
   pub const WS_EX_RIGHTSCROLLBAR:       c_ulong = 0x00000000;
   pub const WS_EX_CONTROLPARENT:        c_ulong = 0x00010000;
   pub const WS_EX_STATICEDGE:           c_ulong = 0x00020000;
   pub const WS_EX_APPWINDOW:            c_ulong = 0x00040000;
   pub const WS_EX_OVERLAPPEDWINDOW:     c_ulong = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE;
   pub const WS_EX_PALETTEWINDOW:        c_ulong = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
   pub const WS_EX_LAYERED:              c_ulong = 0x00080000;
   pub const WS_EX_NOINHERITLAYOUT:      c_ulong = 0x00100000;
   pub const WS_EX_NOREDIRECTIONBITMAP:  c_ulong = 0x00200000;
   pub const WS_EX_LAYOUTRTL:            c_ulong = 0x00400000;
   pub const WS_EX_COMPOSITED:           c_ulong = 0x02000000;
   pub const WS_EX_NOACTIVATE:           c_ulong = 0x08000000;

   pub const CW_USEDEFAULT:                c_int = 0x80000000u32 as c_int;

   pub const WM_CLOSE:                    c_uint = 16;

   pub const SW_SHOWNORMAL:                c_int = 1;
   pub const SW_SHOWMINIMIZED:             c_int = 2;
   pub const SW_MAXIMIZE:                  c_int = 3;
   pub const SW_SHOWMAXIMIZED:             c_int = 3;
   pub const SW_SHOWNOACTIVATE:            c_int = 4;
   pub const SW_SHOW:                      c_int = 5;
   pub const SW_MINIMIZE:                  c_int = 6;
   pub const SW_SHOWMINNOACTIVE:           c_int = 7;
   pub const SW_SHOWNA:                    c_int = 8;
   pub const SW_RESTORE:                   c_int = 9;
   pub const SW_SHOWDEFAULT:               c_int = 10;
   pub const SW_FORCEMINIMIZE:             c_int = 11;

   #[repr(C)]
   #[derive(Copy)]
   pub struct WNDCLASSEXW {
      pub cbSize: c_uint,
      pub style: c_uint,
      pub lpfnWndProc: WNDPROC,
      pub cbClsExtra: c_int,
      pub cbWndExtra: c_int,
      pub hInstance: HINSTANCE,
      pub hIcon: HICON,
      pub hCursor: HCURSOR,
      pub hbrBackground: HBRUSH,
      pub lpszMenuName: LPCWSTR,
      pub lpszClassName: LPCWSTR,
      pub hIconSm: HICON,
   }
   impl Clone for WNDCLASSEXW {
      fn clone(&self) -> Self { *self }
   }
   impl Default for WNDCLASSEXW {
      fn default() -> Self { unsafe { mem::zeroed() } }
   }

   #[repr(C)]
   #[derive(Copy)]
   pub struct POINT {
      pub x: c_long,
      pub y: c_long,
   }
   impl Clone for POINT {
      fn clone(&self) -> Self { *self }
   }
   impl Default for POINT {
      fn default() -> Self { unsafe { mem::zeroed() } }
   }

   #[repr(C)]
   #[derive(Copy)]
   pub struct MSG {
      pub hwnd: HWND,
      pub message: c_uint,
      pub wParam: WPARAM,
      pub lParam: LPARAM,
      pub time: c_ulong,
      pub pt: POINT,
   }
   impl Clone for MSG {
      fn clone(&self) -> Self { *self }
   }
   impl Default for MSG {
      fn default() -> Self { unsafe { mem::zeroed() } }
   }

   extern "system" {
      pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;

      pub fn RegisterClassExW(lpWndClass: *const WNDCLASSEXW) -> ATOM;

      pub fn ShowWindow(hwnd: HWND, nCmdShow: c_int) -> BOOL;

      pub fn PostQuitMessage(nExitCode: c_int);

      pub fn TranslateMessage(lpmsg: *const MSG) -> BOOL;

      pub fn DispatchMessageW(lpmsg: *const MSG) -> LRESULT;

      pub fn GetDC(hwnd: HWND) -> HDC;

      pub fn DefWindowProcW(
         hwnd: HWND,
         Msg: c_uint,
         wParam: WPARAM,
         lParam: LPARAM
      ) -> LRESULT;

      pub fn CreateWindowExW(
         dwExStyle: c_ulong,
         lpClassName: LPCWSTR,
         lpWindowName: LPCWSTR,
         dwStyle: c_ulong,
         x: c_int,
         y: c_int,
         nWidth: c_int,
         nHeight: c_int,
         hWndParent: HWND,
         hMenu: HMENU,
         hInstance: HINSTANCE,
         lpParam: LPVOID,
      ) -> HWND;

      pub fn GetMessageW(
         lpMsg: *const MSG,
         hwnd: HWND,
         wMsgFilterMin: c_uint,
         wMsgFilterMax: c_uint
      ) -> BOOL;
   }
}

use std::ptr;
use std::mem;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;

fn to_utf16_os<S: AsRef<OsStr> + ?Sized>(s: &S) -> Vec<u16> {
   let mut v: Vec<_> = OsStr::new(s).encode_wide().collect();
   v.push(0);
   v
}

pub fn register_window_class<S: AsRef<OsStr> + ?Sized>(class_name: &S) {
   unsafe extern "system" fn wnd_proc(
      hwnd: ffi::HWND,
      msg: ffi::c_uint,
      wparam: ffi::WPARAM,
      lparam: ffi::LPARAM
   ) -> ffi::LRESULT {
      match msg {
         ffi::WM_CLOSE => {
            ffi::PostQuitMessage(0);
         },
         _ => {
            return ffi::DefWindowProcW(hwnd, msg, wparam, lparam);
         }
      }

      return 0;
   }

   unsafe {
      let wnd_class = ffi::WNDCLASSEXW {
         cbSize: mem::size_of::<ffi::WNDCLASSEXW>() as ffi::c_uint,
         style: ffi::CS_HREDRAW | ffi::CS_VREDRAW | ffi::CS_OWNDC,
         lpfnWndProc: Some(wnd_proc),
         cbClsExtra: 0,
         cbWndExtra: 0,
         hInstance: ffi::GetModuleHandleW(ptr::null()),
         hIcon: ptr::null_mut(),
         hCursor: ptr::null_mut(),
         hbrBackground: ptr::null_mut(),
         lpszMenuName: ptr::null(),
         lpszClassName: to_utf16_os(class_name).as_ptr(),
         hIconSm: ptr::null_mut(),
      };

      ffi::RegisterClassExW(&wnd_class);
   }
}

pub struct Window {
   pub hwnd: ffi::HWND
}

impl Window {
   pub fn create<S: AsRef<OsStr> + ?Sized>(width: ffi::c_int, height: ffi::c_int, title: &S, class_name: &S) -> Self {
      let hwnd = unsafe {
         ffi::CreateWindowExW(
            ffi::WS_EX_CLIENTEDGE,
            to_utf16_os(class_name).as_ptr(),
            to_utf16_os(title).as_ptr(),
            ffi::WS_OVERLAPPEDWINDOW | ffi::WS_VISIBLE,
            ffi::CW_USEDEFAULT, ffi::CW_USEDEFAULT,
            width, height,
            ptr::null_mut(),
            ptr::null_mut(),
            ffi::GetModuleHandleW(ptr::null()),
            ptr::null_mut()
         )
      };

      Window {
         hwnd: hwnd
      }
   }

   pub fn dc(&self) -> ffi::HDC {
      unsafe {
         ffi::GetDC(self.hwnd)
      }
   }

   pub fn show_normal(&self) {
      unsafe {
         ffi::ShowWindow(self.hwnd, ffi::SW_SHOWNORMAL)
      };
   }
}

pub struct Message {
   msg: ffi::MSG
}

impl Message {
   pub fn get() -> Option<Self> {
      let mut msg = unsafe { mem::uninitialized() };

      match unsafe { ffi::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) } {
         0 => None,
         _ => Some(Message {
            msg: msg
         })
      }
   }

   pub fn translate(&self) {
      unsafe {
         ffi::TranslateMessage(&self.msg)
      };
   }

   pub fn dispatch(&self) {
      unsafe {
         ffi::DispatchMessageW(&self.msg)
      };
   }
}
<commit_msg>Simplifying by using &str directly<commit_after>#![cfg(target_os = "windows")]

pub mod ffi {
   #![allow(non_snake_case)]

   pub use libc::{
      c_int, c_uint, c_void, uintptr_t, c_long, c_ulong
   };
   pub use libc::types::os::arch::extra::{
      HANDLE, LONG_PTR, LRESULT, HINSTANCE, LPCWSTR, HMODULE, LPVOID, BOOL
   };
   use std::mem;

   pub type HWND = HANDLE;
   pub type HICON = HANDLE;
   pub type HBRUSH = HANDLE;
   pub type HMENU = HANDLE;
   pub type HDC = HANDLE;
   pub type HCURSOR = HICON;

   pub type WPARAM = uintptr_t;
   pub type LPARAM = LONG_PTR;

   pub type ATOM = u16;

   pub type WNDPROC = Option<unsafe extern "system" fn(HWND, c_uint, WPARAM, LPARAM) -> LRESULT>;

   pub const CS_VREDRAW:                 c_ulong = 0x00000001;
   pub const CS_HREDRAW:                 c_ulong = 0x00000002;
   pub const CS_DBLCLKS:                 c_ulong = 0x00000008;
   pub const CS_OWNDC:                   c_ulong = 0x00000020;
   pub const CS_CLASSDC:                 c_ulong = 0x00000040;
   pub const CS_PARENTDC:                c_ulong = 0x00000080;
   pub const CS_NOCLOSE:                 c_ulong = 0x00000200;
   pub const CS_SAVEBITS:                c_ulong = 0x00000800;
   pub const CS_BYTEALIGNCLIENT:         c_ulong = 0x00001000;
   pub const CS_BYTEALIGNWINDOW:         c_ulong = 0x00002000;
   pub const CS_GLOBALCLASS:             c_ulong = 0x00004000;
   pub const CS_IME:                     c_ulong = 0x00010000;
   pub const CS_DROPSHADOW:              c_ulong = 0x00020000;

   pub const WS_OVERLAPPED:              c_ulong = 0x00000000;
   pub const WS_POPUP:                   c_ulong = 0x80000000;
   pub const WS_CHILD:                   c_ulong = 0x40000000;
   pub const WS_MINIMIZE:                c_ulong = 0x20000000;
   pub const WS_VISIBLE:                 c_ulong = 0x10000000;
   pub const WS_DISABLED:                c_ulong = 0x08000000;
   pub const WS_CLIPSIBLINGS:            c_ulong = 0x04000000;
   pub const WS_CLIPCHILDREN:            c_ulong = 0x02000000;
   pub const WS_MAXIMIZE:                c_ulong = 0x01000000;
   pub const WS_CAPTION:                 c_ulong = 0x00C00000;
   pub const WS_BORDER:                  c_ulong = 0x00800000;
   pub const WS_DLGFRAME:                c_ulong = 0x00400000;
   pub const WS_VSCROLL:                 c_ulong = 0x00200000;
   pub const WS_HSCROLL:                 c_ulong = 0x00100000;
   pub const WS_SYSMENU:                 c_ulong = 0x00080000;
   pub const WS_THICKFRAME:              c_ulong = 0x00040000;
   pub const WS_GROUP:                   c_ulong = 0x00020000;
   pub const WS_TABSTOP:                 c_ulong = 0x00010000;
   pub const WS_MINIMIZEBOX:             c_ulong = 0x00020000;
   pub const WS_MAXIMIZEBOX:             c_ulong = 0x00010000;
   pub const WS_TILED:                   c_ulong = WS_OVERLAPPED;
   pub const WS_ICONIC:                  c_ulong = WS_MINIMIZE;
   pub const WS_SIZEBOX:                 c_ulong = WS_THICKFRAME;
   pub const WS_TILEDWINDOW:             c_ulong = WS_OVERLAPPEDWINDOW;
   pub const WS_OVERLAPPEDWINDOW:        c_ulong = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
   pub const WS_POPUPWINDOW:             c_ulong = WS_POPUP | WS_BORDER | WS_SYSMENU;
   pub const WS_CHILDWINDOW:             c_ulong = WS_CHILD;

   pub const WS_EX_DLGMODALFRAME:        c_ulong = 0x00000001;
   pub const WS_EX_NOPARENTNOTIFY:       c_ulong = 0x00000004;
   pub const WS_EX_TOPMOST:              c_ulong = 0x00000008;
   pub const WS_EX_ACCEPTFILES:          c_ulong = 0x00000010;
   pub const WS_EX_TRANSPARENT:          c_ulong = 0x00000020;
   pub const WS_EX_MDICHILD:             c_ulong = 0x00000040;
   pub const WS_EX_TOOLWINDOW:           c_ulong = 0x00000080;
   pub const WS_EX_WINDOWEDGE:           c_ulong = 0x00000100;
   pub const WS_EX_CLIENTEDGE:           c_ulong = 0x00000200;
   pub const WS_EX_CONTEXTHELP:          c_ulong = 0x00000400;
   pub const WS_EX_RIGHT:                c_ulong = 0x00001000;
   pub const WS_EX_LEFT:                 c_ulong = 0x00000000;
   pub const WS_EX_RTLREADING:           c_ulong = 0x00002000;
   pub const WS_EX_LTRREADING:           c_ulong = 0x00000000;
   pub const WS_EX_LEFTSCROLLBAR:        c_ulong = 0x00004000;
   pub const WS_EX_RIGHTSCROLLBAR:       c_ulong = 0x00000000;
   pub const WS_EX_CONTROLPARENT:        c_ulong = 0x00010000;
   pub const WS_EX_STATICEDGE:           c_ulong = 0x00020000;
   pub const WS_EX_APPWINDOW:            c_ulong = 0x00040000;
   pub const WS_EX_OVERLAPPEDWINDOW:     c_ulong = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE;
   pub const WS_EX_PALETTEWINDOW:        c_ulong = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
   pub const WS_EX_LAYERED:              c_ulong = 0x00080000;
   pub const WS_EX_NOINHERITLAYOUT:      c_ulong = 0x00100000;
   pub const WS_EX_NOREDIRECTIONBITMAP:  c_ulong = 0x00200000;
   pub const WS_EX_LAYOUTRTL:            c_ulong = 0x00400000;
   pub const WS_EX_COMPOSITED:           c_ulong = 0x02000000;
   pub const WS_EX_NOACTIVATE:           c_ulong = 0x08000000;

   pub const CW_USEDEFAULT:                c_int = 0x80000000u32 as c_int;

   pub const WM_CLOSE:                    c_uint = 16;

   pub const SW_SHOWNORMAL:                c_int = 1;
   pub const SW_SHOWMINIMIZED:             c_int = 2;
   pub const SW_MAXIMIZE:                  c_int = 3;
   pub const SW_SHOWMAXIMIZED:             c_int = 3;
   pub const SW_SHOWNOACTIVATE:            c_int = 4;
   pub const SW_SHOW:                      c_int = 5;
   pub const SW_MINIMIZE:                  c_int = 6;
   pub const SW_SHOWMINNOACTIVE:           c_int = 7;
   pub const SW_SHOWNA:                    c_int = 8;
   pub const SW_RESTORE:                   c_int = 9;
   pub const SW_SHOWDEFAULT:               c_int = 10;
   pub const SW_FORCEMINIMIZE:             c_int = 11;

   #[repr(C)]
   #[derive(Copy)]
   pub struct WNDCLASSEXW {
      pub cbSize: c_uint,
      pub style: c_uint,
      pub lpfnWndProc: WNDPROC,
      pub cbClsExtra: c_int,
      pub cbWndExtra: c_int,
      pub hInstance: HINSTANCE,
      pub hIcon: HICON,
      pub hCursor: HCURSOR,
      pub hbrBackground: HBRUSH,
      pub lpszMenuName: LPCWSTR,
      pub lpszClassName: LPCWSTR,
      pub hIconSm: HICON,
   }
   impl Clone for WNDCLASSEXW {
      fn clone(&self) -> Self { *self }
   }
   impl Default for WNDCLASSEXW {
      fn default() -> Self { unsafe { mem::zeroed() } }
   }

   #[repr(C)]
   #[derive(Copy)]
   pub struct POINT {
      pub x: c_long,
      pub y: c_long,
   }
   impl Clone for POINT {
      fn clone(&self) -> Self { *self }
   }
   impl Default for POINT {
      fn default() -> Self { unsafe { mem::zeroed() } }
   }

   #[repr(C)]
   #[derive(Copy)]
   pub struct MSG {
      pub hwnd: HWND,
      pub message: c_uint,
      pub wParam: WPARAM,
      pub lParam: LPARAM,
      pub time: c_ulong,
      pub pt: POINT,
   }
   impl Clone for MSG {
      fn clone(&self) -> Self { *self }
   }
   impl Default for MSG {
      fn default() -> Self { unsafe { mem::zeroed() } }
   }

   extern "system" {
      pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;

      pub fn RegisterClassExW(lpWndClass: *const WNDCLASSEXW) -> ATOM;

      pub fn ShowWindow(hwnd: HWND, nCmdShow: c_int) -> BOOL;

      pub fn PostQuitMessage(nExitCode: c_int);

      pub fn TranslateMessage(lpmsg: *const MSG) -> BOOL;

      pub fn DispatchMessageW(lpmsg: *const MSG) -> LRESULT;

      pub fn GetDC(hwnd: HWND) -> HDC;

      pub fn DefWindowProcW(
         hwnd: HWND,
         Msg: c_uint,
         wParam: WPARAM,
         lParam: LPARAM
      ) -> LRESULT;

      pub fn CreateWindowExW(
         dwExStyle: c_ulong,
         lpClassName: LPCWSTR,
         lpWindowName: LPCWSTR,
         dwStyle: c_ulong,
         x: c_int,
         y: c_int,
         nWidth: c_int,
         nHeight: c_int,
         hWndParent: HWND,
         hMenu: HMENU,
         hInstance: HINSTANCE,
         lpParam: LPVOID,
      ) -> HWND;

      pub fn GetMessageW(
         lpMsg: *const MSG,
         hwnd: HWND,
         wMsgFilterMin: c_uint,
         wMsgFilterMax: c_uint
      ) -> BOOL;
   }
}

use std::ptr;
use std::mem;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;

fn to_utf16_os(s: &str) -> Vec<u16> {
   let mut v: Vec<_> = OsStr::new(s).encode_wide().collect();
   v.push(0);
   v
}

pub fn register_window_class(class_name: &str) {
   unsafe extern "system" fn wnd_proc(
      hwnd: ffi::HWND,
      msg: ffi::c_uint,
      wparam: ffi::WPARAM,
      lparam: ffi::LPARAM
   ) -> ffi::LRESULT {
      match msg {
         ffi::WM_CLOSE => {
            ffi::PostQuitMessage(0);
         },
         _ => {
            return ffi::DefWindowProcW(hwnd, msg, wparam, lparam);
         }
      }

      return 0;
   }

   unsafe {
      let wnd_class = ffi::WNDCLASSEXW {
         cbSize: mem::size_of::<ffi::WNDCLASSEXW>() as ffi::c_uint,
         style: ffi::CS_HREDRAW | ffi::CS_VREDRAW | ffi::CS_OWNDC,
         lpfnWndProc: Some(wnd_proc),
         cbClsExtra: 0,
         cbWndExtra: 0,
         hInstance: ffi::GetModuleHandleW(ptr::null()),
         hIcon: ptr::null_mut(),
         hCursor: ptr::null_mut(),
         hbrBackground: ptr::null_mut(),
         lpszMenuName: ptr::null(),
         lpszClassName: to_utf16_os(class_name).as_ptr(),
         hIconSm: ptr::null_mut(),
      };

      ffi::RegisterClassExW(&wnd_class);
   }
}

pub struct Window {
   pub hwnd: ffi::HWND
}

impl Window {
   pub fn create(width: ffi::c_int, height: ffi::c_int, title: &str, class_name: &str) -> Self {
      let hwnd = unsafe {
         ffi::CreateWindowExW(
            ffi::WS_EX_CLIENTEDGE,
            to_utf16_os(class_name).as_ptr(),
            to_utf16_os(title).as_ptr(),
            ffi::WS_OVERLAPPEDWINDOW | ffi::WS_VISIBLE,
            ffi::CW_USEDEFAULT, ffi::CW_USEDEFAULT,
            width, height,
            ptr::null_mut(),
            ptr::null_mut(),
            ffi::GetModuleHandleW(ptr::null()),
            ptr::null_mut()
         )
      };

      Window {
         hwnd: hwnd
      }
   }

   pub fn dc(&self) -> ffi::HDC {
      unsafe {
         ffi::GetDC(self.hwnd)
      }
   }

   pub fn show_normal(&self) {
      unsafe {
         ffi::ShowWindow(self.hwnd, ffi::SW_SHOWNORMAL)
      };
   }
}

pub struct Message {
   msg: ffi::MSG
}

impl Message {
   pub fn get() -> Option<Self> {
      let mut msg = unsafe { mem::uninitialized() };

      match unsafe { ffi::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) } {
         0 => None,
         _ => Some(Message {
            msg: msg
         })
      }
   }

   pub fn translate(&self) {
      unsafe {
         ffi::TranslateMessage(&self.msg)
      };
   }

   pub fn dispatch(&self) {
      unsafe {
         ffi::DispatchMessageW(&self.msg)
      };
   }
}
<|endoftext|>


<commit_before>use crate::common::State;
use crate::http::{http_main_server, http_redirect_server};
use anyhow::Result;
use extendedmind_engine::tcp;
use std::path::PathBuf;
use tide_acme::rustls_acme::caches::DirCache;
use tide_acme::{AcmeConfig, TideRustlsExt};

pub async fn start_server(
    initial_state: State,
    static_root_dir: Option<PathBuf>,
    http_port: Option<u16>,
    https_port: Option<u16>,
    domain: Option<String>,
    acme_email: Option<String>,
    acme_dir: Option<String>,
    acme_production: bool,
    hsts_max_age: Option<u64>,
    hsts_permanent_redirect: bool,
    hsts_preload: bool,
    tcp_port: Option<u16>,
    skip_compress_mime: Option<Vec<String>>,
    cache_ttl_sec: Option<u64>,
    cache_tti_sec: Option<u64>,
    inline_css_path: Option<Vec<String>>,
    immutable_path: Option<Vec<String>>,
    metrics_endpoint: Option<String>,
    metrics_dir: Option<PathBuf>,
    metrics_secret: Option<String>,
) -> Result<()> {
    let engine = initial_state.engine.clone();

    if let Some(http_port) = http_port {
        let main_server = http_main_server(
            initial_state,
            static_root_dir,
            skip_compress_mime,
            cache_ttl_sec,
            cache_tti_sec,
            inline_css_path,
            immutable_path,
            hsts_max_age,
            hsts_preload,
            metrics_endpoint,
            metrics_dir,
            metrics_secret,
        )
        .unwrap();
        if let Some(https_port) = https_port {
            if domain.is_none() || acme_dir.is_none() || acme_email.is_none() {
                anyhow::bail!(
                    "With --https-port also --domain, --acme-email and --acme-dir are required"
                );
            }
            let domain = domain.unwrap();
            let acme_dir = acme_dir.unwrap();
            let acme_email = acme_email.unwrap();
            let redirect_server = http_redirect_server(
                format!("https://{}:{}", &domain, &https_port).as_str(),
                hsts_permanent_redirect,
            )
            .unwrap();
            let redirect_listener = redirect_server.listen(format!("0.0.0.0:{}", &http_port));
            let main_listener = main_server.listen(
                tide_rustls::TlsListener::build()
                    .addrs(format!("0.0.0.0:{}", &https_port))
                    .acme(
                        AcmeConfig::new(vec![domain])
                            .contact_push(format!("mailto:{}", acme_email))
                            .cache(DirCache::new(acme_dir))
                            .directory_lets_encrypt(acme_production),
                    ),
            );

            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, redirect_listener, tcp_listener)?;
            } else {
                futures::try_join!(main_listener, redirect_listener)?;
            }
        } else {
            let main_listener = main_server.listen(format!("0.0.0.0:{}", &http_port));
            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, tcp_listener)?;
            } else {
                main_listener.await?;
            }
        }
    } else {
        if let Some(tcp_port) = tcp_port {
            tcp::listen(format!("0.0.0.0:{}", tcp_port), engine).await?;
        } else {
            anyhow::bail!("Either --tcp-port or --http-port is mandatory");
        };
    }

    Ok(())
}
<commit_msg>Try to set TCP_NODELAY to TLS server<commit_after>use crate::common::State;
use crate::http::{http_main_server, http_redirect_server};
use anyhow::Result;
use extendedmind_engine::tcp;
use std::path::PathBuf;
use tide_acme::rustls_acme::caches::DirCache;
use tide_acme::{AcmeConfig, TideRustlsExt};

pub async fn start_server(
    initial_state: State,
    static_root_dir: Option<PathBuf>,
    http_port: Option<u16>,
    https_port: Option<u16>,
    domain: Option<String>,
    acme_email: Option<String>,
    acme_dir: Option<String>,
    acme_production: bool,
    hsts_max_age: Option<u64>,
    hsts_permanent_redirect: bool,
    hsts_preload: bool,
    tcp_port: Option<u16>,
    skip_compress_mime: Option<Vec<String>>,
    cache_ttl_sec: Option<u64>,
    cache_tti_sec: Option<u64>,
    inline_css_path: Option<Vec<String>>,
    immutable_path: Option<Vec<String>>,
    metrics_endpoint: Option<String>,
    metrics_dir: Option<PathBuf>,
    metrics_secret: Option<String>,
) -> Result<()> {
    let engine = initial_state.engine.clone();

    if let Some(http_port) = http_port {
        let main_server = http_main_server(
            initial_state,
            static_root_dir,
            skip_compress_mime,
            cache_ttl_sec,
            cache_tti_sec,
            inline_css_path,
            immutable_path,
            hsts_max_age,
            hsts_preload,
            metrics_endpoint,
            metrics_dir,
            metrics_secret,
        )
        .unwrap();
        if let Some(https_port) = https_port {
            if domain.is_none() || acme_dir.is_none() || acme_email.is_none() {
                anyhow::bail!(
                    "With --https-port also --domain, --acme-email and --acme-dir are required"
                );
            }
            let domain = domain.unwrap();
            let acme_dir = acme_dir.unwrap();
            let acme_email = acme_email.unwrap();
            let redirect_server = http_redirect_server(
                format!("https://{}:{}", &domain, &https_port).as_str(),
                hsts_permanent_redirect,
            )
            .unwrap();
            let redirect_listener = redirect_server.listen(format!("0.0.0.0:{}", &http_port));
            let main_listener = main_server.listen(
                tide_rustls::TlsListener::build()
                    .addrs(format!("0.0.0.0:{}", &https_port))
                    .tcp_nodelay(true)
                    .tcp_ttl(60)
                    .acme(
                        AcmeConfig::new(vec![domain])
                            .contact_push(format!("mailto:{}", acme_email))
                            .cache(DirCache::new(acme_dir))
                            .directory_lets_encrypt(acme_production),
                    ),
            );

            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, redirect_listener, tcp_listener)?;
            } else {
                futures::try_join!(main_listener, redirect_listener)?;
            }
        } else {
            let main_listener = main_server.listen(format!("0.0.0.0:{}", &http_port));
            if let Some(tcp_port) = tcp_port {
                let tcp_listener = tcp::listen(format!("0.0.0.0:{}", tcp_port), engine);
                futures::try_join!(main_listener, tcp_listener)?;
            } else {
                main_listener.await?;
            }
        }
    } else {
        if let Some(tcp_port) = tcp_port {
            tcp::listen(format!("0.0.0.0:{}", tcp_port), engine).await?;
        } else {
            anyhow::bail!("Either --tcp-port or --http-port is mandatory");
        };
    }

    Ok(())
}
<|endoftext|>