<commit_before>#![allow(dead_code)]

use super::events::*;
use super::events::append_entries::{AppendEntriesReq, AppendEntriesRes};
use super::events::{VoteReq, VoteRes};
use super::follower::Follower;   // A trait with impl for RaftServerState
use super::candidate::Candidate; // A trait with impl for RaftServerState
use super::leader::Leader;       // A trait with impl for RaftServerState

use std::comm::*;
use std::io::timer::Timer;
use std::vec::Vec;
use rand::{task_rng,Rng};

// Some ideas of how to use with Application Tasks:
//
// let (to_app_send, to_app_rec) = channel();
// let (from_app_send, from_app_rec) = channel();
//
// spawn_app_task(to_app_rec, from_app_send);
//
// let (from_endpoint_send, from_endpoint_rec) = channel();
//
// spawn_main_app_endpoint_task(from_endpoint_send);
//
// rs = RaftServer::new(...opts...); // TODO
// rs.add_peer(...peercfg...); // TODO
// RaftServer::spawn(rs, to_app_send, from_app_rec, from_endpoint_rec);

// STATE MACHINE
/* NOTES
    pass as <~str>
    LockReq -> LockResp(bool, id)  //using Option<int>
    UnlockReq(<id>) -> UnlockResp(bool)

loop {
next_msg = Rec(Msg).recv();
let mut state = Option<id>;
match state {
    None => state = Unlocked;
    Some<id> => state = Locked(id: ---)
}
*/

pub enum State {
    Locked,
    Unlocked
}
pub struct Locked {
    locked: bool,
    state: Option<int>,
    id: Option<int>
}

pub struct Unlocked {
    unlocked: bool,
    state: Option<int>,
    id: Option<int>
}

pub struct LockRes {
    new_state: Option<State>,
    status: bool,
    id: Option<int>
}

pub struct UnlockRes {
    new_state: Option<State>,
    status: bool,
    id: Option<int>
}

impl Unlocked {

    fn lock_request(object: &mut Unlocked) -> LockRes {
        if object.unlocked {

            //update with some deterministic but RANDOM NUMBER
            let mut rand_num = task_rng().gen::<int>();
            // if rand_num.gen() {
            // 	println!("int: {}, uint: {}", rng.gen::<int>(), rng.gen::<uint>())
            // }

            let result = LockRes { new_state: Some(Locked), status: true, id: Some(rand_num) };
            result
            //- send lock msg on a Chan
            //endp_send.send(result);
            //- send "true" and <id> on Chan
        }

        else if !object.unlocked {
            //already locked do nothing
        }
    }

    fn unlock_request(object: &mut Unlocked) -> UnlockRes {
        if object.unlocked {
            //already unlocked
            }
        // else if !self.locked {
        //     Continue //already unlocked do nothing
        // }
    }
}

impl Locked {

    fn unlock_request(object: &mut Locked, request_id: Option<int>) -> UnlockRes {
        if object.locked {
            match request_id {
            	Some(id) => { let result = UnlockRes {
            							new_state: Some(Unlocked),
            							status: true,
            							id: None};
            							result
            						},

            	None => {}//do nothing

                //id sent by request does not match, continue/do nothing
				//if unlock request (with same <id>) send unlock msg on Chan + "true" and <id> on Chan
            }
        }

        else if !object.locked {
            //already unlocked, do nothing
        }
    }

    fn lock_request(object: &mut Locked) -> LockRes {
        if object.locked {
            //already locked
        }

        // else if !self.locked {
        //     Continue //already locked do nothing
        // }
	}
}

<commit_msg>WIP state machine updates<commit_after>#![allow(dead_code)]

use super::events::*;
use super::events::append_entries::{AppendEntriesReq, AppendEntriesRes};
use super::events::{VoteReq, VoteRes};
use super::follower::Follower;   // A trait with impl for RaftServerState
use super::candidate::Candidate; // A trait with impl for RaftServerState
use super::leader::Leader;       // A trait with impl for RaftServerState

use std::comm::*;
use std::io::timer::Timer;
use std::vec::Vec;
use rand::{task_rng,Rng};

// Some ideas of how to use with Application Tasks:
//
// let (to_app_send, to_app_rec) = channel();
// let (from_app_send, from_app_rec) = channel();
//
// spawn_app_task(to_app_rec, from_app_send);
//
// let (from_endpoint_send, from_endpoint_rec) = channel();
//
// spawn_main_app_endpoint_task(from_endpoint_send);
//
// rs = RaftServer::new(...opts...); // TODO
// rs.add_peer(...peercfg...); // TODO
// RaftServer::spawn(rs, to_app_send, from_app_rec, from_endpoint_rec);

// STATE MACHINE
/* NOTES
    pass as <~str>
    LockReq -> LockResp(bool, id)  //using Option<int>
    UnlockReq(<id>) -> UnlockResp(bool)

loop {
next_msg = Rec(Msg).recv();
let mut state = Option<id>;
match state {
    None => state = Unlocked;
    Some<id> => state = Locked(id: ---)
}
*/

// pub enum LockState {
//     Locked,
//     Unlocked
// }

// pub struct Locked {
//     locked: bool,
//     state: Option<int>,
//     id: Option<int>
// }

// pub struct Unlocked {
//     unlocked: bool,
//     state: Option<int>,
//     id: Option<int>
// }

pub struct LockState {
	locked: bool,
	counter: uint
}

// pub struct LockRes {
//     new_state: Option<State>,
//     status: bool,
//     id: Option<int>
// }

// pub struct UnlockRes {
//     new_state: Option<State>,
//     status: bool,
//     id: Option<int>
// }

impl LockState {
  pub fn spawn(recv: Receiver<(ClientCmdReq, Sender<ClientCmdRes>)>) {
    spawn(proc() {
      ls = LockState::new();

      ls.loop(recv);
    });
  }

  fn new() -> LockState {
    LockState {
      locked: false,
      counter: 0
    }
  }

  fn loop(&mut self, recv: Receiver<(ClientCmdReq, Sender<ClientCmdRes>)) {
    loop {
      let (cmd, sender) = recv.recv();

      //call self.lock() and self.unlock(uint) here

    }
  }

  fn lock(&mut self) -> Option<uint> {
    if !self.locked {
      self.locked = true;
      Some(self.counter)
    } else {
      None
    }
  }

  fn unlock(&mut self, lockval: uint) -> bool {
    if self.locked  && self.counter == lockval {
      self.locked = false;
      self.counter += 1;
      true
    }
    else {
      false
    }
  }
}

// impl LockState {

//     fn lock_request(object: &mut LockState) -> LockRes {
//         if !object.locked { //if unlocked

//             //update with some deterministic but RANDOM NUMBER
//             // let mut rand_num = task_rng().gen::<int>();
//             // if rand_num.gen() {
//             // 	println!("int: {}, uint: {}", rng.gen::<int>(), rng.gen::<uint>())
//             // }

//             let result = LockRes {new_state: Some(Locked), status: true, id: Some(rand_num) };
//             result
//             //- send lock msg on a Chan
//             //endp_send.send(result);
//             //- send "true" and <id> on Chan
//         }

//         else if object.locked { //if already locked
//             //do nothing
//         }
//     }

//     fn unlock_request(object: &mut Unlocked) -> UnlockRes {
//         if object.locked { //if locked
//             let result = UnlockRes {new_state: Some(Unlocked), status: false, id: None};
//             result
//             }
//         // else if !self.locked {
//         //     Continue //already unlocked do nothing
//         // }
//     }
// }

// impl Locked {

//     fn unlock_request(object: &mut Locked, request_id: Option<int>) -> UnlockRes {
//         if object.locked {
//             match request_id {
//             	Some(id) => { let result = UnlockRes {
//             							new_state: Some(Unlocked),
//             							status: true,
//             							id: None};
//             							result
//             						},

//             	None => {}//do nothing

//                 //id sent by request does not match, continue/do nothing
// 				//if unlock request (with same <id>) send unlock msg on Chan + "true" and <id> on Chan
//             }
//         }

//         else if !object.locked {
//             //already unlocked, do nothing
//         }
//     }

//     fn lock_request(object: &mut Locked) -> LockRes {
//         if object.locked {
//             //already locked
//         }

//         // else if !self.locked {
//         //     Continue //already locked do nothing
//         // }
// 	}
// }

<|endoftext|>


<commit_before>use std::rc::*;
use std::result::Result;
use serde_json::*;

use super::script::*;
use super::bound_script::*;
use super::script_interpreter::*;

///
/// Creates an unquoted version of a string
///
fn unquote_string(string: &str) -> String {
    let chars: Vec<char>    = string.chars().collect();
    let mut result          = String::new();
    let mut index           = 1;
    while index < chars.len()-1 {
        // Push character
        let chr = chars[index];

        match chr {
            '\\' => {
                let quoted = chars[index+1];
                index += 1;
                match quoted {
                    'n' => result.push('\n'),
                    'r' => result.push('\r'),
                    't' => result.push('\t'),
                    quoted => result.push(quoted)
                }
            },
            chr => result.push(chr)
        }

        // Next character
        index += 1;
    }

    result
}

///
/// Parses a number string
///
fn parse_number(number: &str) -> Value {
    if number.contains('.') || number.contains('e') || number.contains('E') {
        json![ number.parse::<f64>().unwrap() ]
    } else if number.starts_with("0x") {
        json![ i64::from_str_radix(&number[2..], 16).unwrap() ]
    } else {
        json![ number.parse::<i64>().unwrap() ]
    }
}

///
/// Creates an execution error relating to an expression
///
fn generate_expression_error(error: ScriptEvaluationError, expr: &Expression) -> Value {
    json![{
        "error":                error,
        "failed-expression":    expr
    }]
}

///
/// Generates a tool binding
///
pub fn bind_tool(tool_name: &ScriptToken, expr: &Expression, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    if let Ok(tool) = execution_environment.get_json_tool(&tool_name.matched) {
        Ok(BoundExpression::Tool(Rc::new(tool), tool_name.clone()))
    } else {
        Err(generate_expression_error(ScriptEvaluationError::ExpressionDoesNotEvaluateToTool, expr))
    }
}

///
/// Binds an expression to an environment
///
pub fn bind_expression(expr: &Expression, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    match expr {
        &Expression::String(ref s)          => Ok(BoundExpression::Value(Value::String(unquote_string(&s.matched)), s.clone())),
        &Expression::Number(ref n)          => Ok(BoundExpression::Value(parse_number(&n.matched), n.clone())),

        &Expression::Identifier(ref id)     => bind_tool(id, expr, execution_environment),

        _ => unimplemented!()
    }
}

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

    #[test]
    fn can_bind_string() {
        let string_expr         = Expression::string("\"Foo\"");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);

        assert!(match bind_expression(&string_expr, &mut env) { Ok(BoundExpression::Value(Value::String(s), _)) => s == "Foo", _ => false });
    }

    #[test]
    fn can_bind_number() {
        let string_expr         = Expression::number("42");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);

        assert!(match bind_expression(&string_expr, &mut env) { Ok(BoundExpression::Value(num, _)) => num == json![ 42 ], _ => false });
    }

    #[test]
    fn can_bind_tool() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = bind_expression(&tool_expr, &mut env);

        assert!(match result { Ok(BoundExpression::Tool(_, _)) => true, _ => false });
    }
}
<commit_msg>Add array and map binding<commit_after>use std::rc::*;
use std::result::Result;
use serde_json::*;

use super::script::*;
use super::bound_script::*;
use super::script_interpreter::*;

///
/// Creates an unquoted version of a string
///
fn unquote_string(string: &str) -> String {
    let chars: Vec<char>    = string.chars().collect();
    let mut result          = String::new();
    let mut index           = 1;
    while index < chars.len()-1 {
        // Push character
        let chr = chars[index];

        match chr {
            '\\' => {
                let quoted = chars[index+1];
                index += 1;
                match quoted {
                    'n' => result.push('\n'),
                    'r' => result.push('\r'),
                    't' => result.push('\t'),
                    quoted => result.push(quoted)
                }
            },
            chr => result.push(chr)
        }

        // Next character
        index += 1;
    }

    result
}

///
/// Parses a number string
///
fn parse_number(number: &str) -> Value {
    if number.contains('.') || number.contains('e') || number.contains('E') {
        json![ number.parse::<f64>().unwrap() ]
    } else if number.starts_with("0x") {
        json![ i64::from_str_radix(&number[2..], 16).unwrap() ]
    } else {
        json![ number.parse::<i64>().unwrap() ]
    }
}

///
/// Creates an execution error relating to an expression
///
fn generate_expression_error(error: ScriptEvaluationError, expr: &Expression) -> Value {
    json![{
        "error":                error,
        "failed-expression":    expr
    }]
}

///
/// Generates a tool binding
///
pub fn bind_tool(tool_name: &ScriptToken, expr: &Expression, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    if let Ok(tool) = execution_environment.get_json_tool(&tool_name.matched) {
        Ok(BoundExpression::Tool(Rc::new(tool), tool_name.clone()))
    } else {
        Err(generate_expression_error(ScriptEvaluationError::ExpressionDoesNotEvaluateToTool, expr))
    }
}

///
/// Binds a sequence of elements
///
fn bind_sequence(items: &Vec<Expression>, execution_environment: &ScriptExecutionEnvironment) -> Result<Vec<BoundExpression>, Value> {
    let mut result = vec![];

    for expr in items {
        match bind_expression(expr, execution_environment) {
            Ok(val)     => result.push(val),
            Err(erm)    => return Err(erm)
        }
    }

    Ok(result)
}

///
/// Generates an array binding
///
pub fn bind_array(items: &Vec<Expression>, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    bind_sequence(items, execution_environment)
        .map(|array_items| BoundExpression::Array(array_items))
}

///
/// Generates a tuple binding
///
pub fn bind_tuple(items: &Vec<Expression>, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    bind_sequence(items, execution_environment)
        .map(|tuple_items| BoundExpression::Tuple(tuple_items))
}

///
/// Generates a map binding
///
pub fn bind_map(items: &Vec<(Expression, Expression)>, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    let mut result = vec![];

    for &(ref lexpr, ref rexpr) in items {
        let lbound = match bind_expression(lexpr, execution_environment) {
            Ok(val)     => val,
            Err(erm)    => return Err(erm)
        };

        let rbound = match bind_expression(rexpr, execution_environment) {
            Ok(val)     => val,
            Err(erm)    => return Err(erm)
        };

        result.push((lbound, rbound));
    }

    Ok(BoundExpression::Map(result))
}

///
/// Binds an index expression (a[b])
///
pub fn bind_index(index: &Box<(Expression, Expression)>, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    unimplemented!();
}

///
/// Binds a field access expression (a.b)
///
pub fn bind_field_access(field_access: &Box<(Expression, Expression)>, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    unimplemented!();
}

///
/// Binds an apply expression (a(parameters))
///
pub fn bind_apply(apply: &Box<(Expression, Expression)>, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    unimplemented!();
}

///
/// Binds an expression to an environment
///
pub fn bind_expression(expr: &Expression, execution_environment: &ScriptExecutionEnvironment) -> Result<BoundExpression, Value> {
    match expr {
        &Expression::String(ref s)              => Ok(BoundExpression::Value(Value::String(unquote_string(&s.matched)), s.clone())),
        &Expression::Number(ref n)              => Ok(BoundExpression::Value(parse_number(&n.matched), n.clone())),

        &Expression::Array(ref items)           => bind_array(items, execution_environment),
        &Expression::Tuple(ref items)           => bind_tuple(items, execution_environment),
        &Expression::Map(ref items)             => bind_map(items, execution_environment),

        &Expression::Identifier(ref id)         => bind_tool(id, expr, execution_environment),
        &Expression::Index(ref indexer)         => bind_index(indexer, execution_environment),
        &Expression::FieldAccess(ref accessor)  => bind_field_access(accessor, execution_environment),
        &Expression::Apply(ref application)     => bind_apply(application, execution_environment),
    }
}

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

    #[test]
    fn can_bind_string() {
        let string_expr         = Expression::string("\"Foo\"");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);

        assert!(match bind_expression(&string_expr, &mut env) { Ok(BoundExpression::Value(Value::String(s), _)) => s == "Foo", _ => false });
    }

    #[test]
    fn can_bind_number() {
        let string_expr         = Expression::number("42");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);

        assert!(match bind_expression(&string_expr, &mut env) { Ok(BoundExpression::Value(num, _)) => num == json![ 42 ], _ => false });
    }

    #[test]
    fn can_bind_tool() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = bind_expression(&tool_expr, &mut env);

        assert!(match result { Ok(BoundExpression::Tool(_, _)) => true, _ => false });
    }

    #[test]
    fn can_bind_array() {
        let array_expr          = Expression::Array(vec![Expression::identifier("test"), Expression::number("1")]);
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = bind_expression(&array_expr, &mut env);

        assert!(match result { Ok(BoundExpression::Array(_)) => true, _ => false });

        let array = match result { Ok(BoundExpression::Array(x)) => x, _ => vec![] };
        assert!(match array[0] { BoundExpression::Tool(_, _) => true, _ => false });
        assert!(match array[1] { BoundExpression::Value(_, _) => true, _ => false });
    }

    #[test]
    fn can_bind_tuple() {
        let tuple_expr          = Expression::Tuple(vec![Expression::identifier("test"), Expression::number("1")]);
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = bind_expression(&tuple_expr, &mut env);

        assert!(match result { Ok(BoundExpression::Tuple(_)) => true, _ => false });

        let tuple = match result { Ok(BoundExpression::Tuple(x)) => x, _ => vec![] };
        assert!(match tuple[0] { BoundExpression::Tool(_, _) => true, _ => false });
        assert!(match tuple[1] { BoundExpression::Value(_, _) => true, _ => false });
    }

    #[test]
    fn can_bind_map() {
        let map_expr            = Expression::Map(vec![(Expression::string("\"test\""), Expression::identifier("test"))]);
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = bind_expression(&map_expr, &mut env);

        assert!(match result { Ok(BoundExpression::Map(_)) => true, _ => false });

        let map = match result { Ok(BoundExpression::Map(x)) => x, _ => vec![] };
        assert!(match map[0] { (BoundExpression::Value(_, _), BoundExpression::Tool(_, _)) => true, _ => false });
    }
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! char_class {
    ($c: expr, [$head:expr]) => ($c == $head);
    ($c: expr, [$head:expr $(, $cs:expr)+]) => ($c == $head || $crate::char_class!($c, [$($cs),*]));
}

#[macro_export]
macro_rules! alpha_char {
    ($c: expr) => {
        match $c {
            'a'..='z' => true,
            'A'..='Z' => true,
            '_' => true,
            '\u{00A0}'..='\u{00BF}' => true,
            '\u{00C0}'..='\u{00D6}' => true,
            '\u{00D8}'..='\u{00F6}' => true,
            '\u{00F8}'..='\u{00FF}' => true,
            '\u{0100}'..='\u{017F}' => true, // Latin Extended-A
            '\u{0180}'..='\u{024F}' => true, // Latin Extended-B
            '\u{0250}'..='\u{02AF}' => true, // IPA Extensions
            '\u{02B0}'..='\u{02FF}' => true, // Spacing Modifier Letters
            '\u{0300}'..='\u{036F}' => true, // Combining Diacritical Marks
            '\u{0370}'..='\u{03FF}' => true, // Greek/Coptic
            '\u{0400}'..='\u{04FF}' => true, // Cyrillic
            '\u{0500}'..='\u{052F}' => true, // Cyrillic Supplement
            '\u{0530}'..='\u{058F}' => true, // Armenian
            '\u{0590}'..='\u{05FF}' => true, // Hebrew
            '\u{0600}'..='\u{06FF}' => true, // Arabic
            '\u{0700}'..='\u{074F}' => true, // Syriac
            _ => false,
        }
    };
}

#[macro_export]
macro_rules! alpha_numeric_char {
    ($c: expr) => {
        $crate::alpha_char!($c) || $crate::decimal_digit_char!($c)
    };
}

#[macro_export]
macro_rules! backslash_char {
    ($c: expr) => {
        $c == '\\'
    };
}

#[macro_export]
macro_rules! back_quote_char {
    ($c: expr) => {
        $c == '`'
    };
}

#[macro_export]
macro_rules! octet_char {
    ($c: expr) => {
        ('\u{0000}'..='\u{00FF}').contains(&$c)
    };
}

#[macro_export]
macro_rules! capital_letter_char {
    ($c: expr) => {
        ('A'..='Z').contains(&$c)
    };
}

#[macro_export]
macro_rules! comment_1_char {
    ($c: expr) => {
        $c == '/'
    };
}

#[macro_export]
macro_rules! comment_2_char {
    ($c: expr) => {
        $c == '*'
    };
}

#[macro_export]
macro_rules! cut_char {
    ($c: expr) => {
        $c == '!'
    };
}

#[macro_export]
macro_rules! decimal_digit_char {
    ($c: expr) => {
        ('0'..='9').contains(&$c)
    };
}

#[macro_export]
macro_rules! decimal_point_char {
    ($c: expr) => {
        $c == '.'
    };
}

#[macro_export]
macro_rules! double_quote_char {
    ($c: expr) => {
        $c == '"'
    };
}

#[macro_export]
macro_rules! end_line_comment_char {
    ($c: expr) => {
        $c == '%'
    };
}

#[macro_export]
macro_rules! exponent_char {
    ($c: expr) => {
        $c == 'e' || $c == 'E'
    };
}

#[macro_export]
macro_rules! graphic_char {
    ($c: expr) => ($crate::char_class!($c, ['#', '$', '&', '*', '+', '-', '.', '/', ':',
                                    '<', '=', '>', '?', '@', '^', '~']))
}

#[macro_export]
macro_rules! graphic_token_char {
    ($c: expr) => {
        $crate::graphic_char!($c) || $crate::backslash_char!($c)
    };
}

#[macro_export]
macro_rules! hexadecimal_digit_char {
    ($c: expr) => {
        ('0'..='9').contains(&$c) || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
    };
}

#[macro_export]
macro_rules! layout_char {
    ($c: expr) => {
        $crate::char_class!($c, [' ', '\n', '\t', '\u{0B}', '\u{0C}'])
    };
}

#[macro_export]
macro_rules! meta_char {
    ($c: expr) => {
        $crate::char_class!($c, ['\\', '\'', '"', '`'])
    };
}

#[macro_export]
macro_rules! new_line_char {
    ($c: expr) => {
        $c == '\n'
    };
}

#[macro_export]
macro_rules! octal_digit_char {
    ($c: expr) => {
        ('0'..='7').contains(&$c)
    };
}

#[macro_export]
macro_rules! binary_digit_char {
    ($c: expr) => {
        $c >= '0' && $c <= '1'
    };
}

#[macro_export]
macro_rules! prolog_char {
    ($c: expr) => {
        $crate::graphic_char!($c)
            || $crate::alpha_numeric_char!($c)
            || $crate::solo_char!($c)
            || $crate::layout_char!($c)
            || $crate::meta_char!($c)
    };
}

#[macro_export]
macro_rules! semicolon_char {
    ($c: expr) => {
        $c == ';'
    };
}

#[macro_export]
macro_rules! sign_char {
    ($c: expr) => {
        $c == '-' || $c == '+'
    };
}

#[macro_export]
macro_rules! single_quote_char {
    ($c: expr) => {
        $c == '\''
    };
}

#[macro_export]
macro_rules! small_letter_char {
    ($c: expr) => {
        ('a'..='z').contains(&$c)
    };
}

#[macro_export]
macro_rules! solo_char {
    ($c: expr) => {
        $crate::char_class!($c, ['!', '(', ')', ',', ';', '[', ']', '{', '}', '|', '%'])
    };
}

#[macro_export]
macro_rules! space_char {
    ($c: expr) => {
        $c == ' '
    };
}

#[macro_export]
macro_rules! symbolic_control_char {
    ($c: expr) => {
        $crate::char_class!($c, ['a', 'b', 'f', 'n', 'r', 't', 'v', '0'])
    };
}

#[macro_export]
macro_rules! symbolic_hexadecimal_char {
    ($c: expr) => {
        $c == 'x'
    };
}

#[macro_export]
macro_rules! variable_indicator_char {
    ($c: expr) => {
        $c == '_'
    };
}
<commit_msg>accept remaining UTF-8 codepoints as alphabetic characters (#1355)<commit_after>#[macro_export]
macro_rules! char_class {
    ($c: expr, [$head:expr]) => ($c == $head);
    ($c: expr, [$head:expr $(, $cs:expr)+]) => ($c == $head || $crate::char_class!($c, [$($cs),*]));
}

#[macro_export]
macro_rules! alpha_char {
    ($c: expr) => {
        match $c {
            'a'..='z' => true,
            'A'..='Z' => true,
            '_' => true,
            '\u{00A0}'..='\u{00BF}' => true,
            '\u{00C0}'..='\u{00D6}' => true,
            '\u{00D8}'..='\u{00F6}' => true,
            '\u{00F8}'..='\u{00FF}' => true,
            '\u{0100}'..='\u{FFFF}' => true, /* skip some control
 characters but admit the remaining code points as alphabetic
 characters. */
            _ => false,
        }
    };
}

#[macro_export]
macro_rules! alpha_numeric_char {
    ($c: expr) => {
        $crate::alpha_char!($c) || $crate::decimal_digit_char!($c)
    };
}

#[macro_export]
macro_rules! backslash_char {
    ($c: expr) => {
        $c == '\\'
    };
}

#[macro_export]
macro_rules! back_quote_char {
    ($c: expr) => {
        $c == '`'
    };
}

#[macro_export]
macro_rules! octet_char {
    ($c: expr) => {
        ('\u{0000}'..='\u{00FF}').contains(&$c)
    };
}

#[macro_export]
macro_rules! capital_letter_char {
    ($c: expr) => {
        ('A'..='Z').contains(&$c)
    };
}

#[macro_export]
macro_rules! comment_1_char {
    ($c: expr) => {
        $c == '/'
    };
}

#[macro_export]
macro_rules! comment_2_char {
    ($c: expr) => {
        $c == '*'
    };
}

#[macro_export]
macro_rules! cut_char {
    ($c: expr) => {
        $c == '!'
    };
}

#[macro_export]
macro_rules! decimal_digit_char {
    ($c: expr) => {
        ('0'..='9').contains(&$c)
    };
}

#[macro_export]
macro_rules! decimal_point_char {
    ($c: expr) => {
        $c == '.'
    };
}

#[macro_export]
macro_rules! double_quote_char {
    ($c: expr) => {
        $c == '"'
    };
}

#[macro_export]
macro_rules! end_line_comment_char {
    ($c: expr) => {
        $c == '%'
    };
}

#[macro_export]
macro_rules! exponent_char {
    ($c: expr) => {
        $c == 'e' || $c == 'E'
    };
}

#[macro_export]
macro_rules! graphic_char {
    ($c: expr) => ($crate::char_class!($c, ['#', '$', '&', '*', '+', '-', '.', '/', ':',
                                    '<', '=', '>', '?', '@', '^', '~']))
}

#[macro_export]
macro_rules! graphic_token_char {
    ($c: expr) => {
        $crate::graphic_char!($c) || $crate::backslash_char!($c)
    };
}

#[macro_export]
macro_rules! hexadecimal_digit_char {
    ($c: expr) => {
        ('0'..='9').contains(&$c) || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
    };
}

#[macro_export]
macro_rules! layout_char {
    ($c: expr) => {
        $crate::char_class!($c, [' ', '\n', '\t', '\u{0B}', '\u{0C}'])
    };
}

#[macro_export]
macro_rules! meta_char {
    ($c: expr) => {
        $crate::char_class!($c, ['\\', '\'', '"', '`'])
    };
}

#[macro_export]
macro_rules! new_line_char {
    ($c: expr) => {
        $c == '\n'
    };
}

#[macro_export]
macro_rules! octal_digit_char {
    ($c: expr) => {
        ('0'..='7').contains(&$c)
    };
}

#[macro_export]
macro_rules! binary_digit_char {
    ($c: expr) => {
        $c >= '0' && $c <= '1'
    };
}

#[macro_export]
macro_rules! prolog_char {
    ($c: expr) => {
        $crate::graphic_char!($c)
            || $crate::alpha_numeric_char!($c)
            || $crate::solo_char!($c)
            || $crate::layout_char!($c)
            || $crate::meta_char!($c)
    };
}

#[macro_export]
macro_rules! semicolon_char {
    ($c: expr) => {
        $c == ';'
    };
}

#[macro_export]
macro_rules! sign_char {
    ($c: expr) => {
        $c == '-' || $c == '+'
    };
}

#[macro_export]
macro_rules! single_quote_char {
    ($c: expr) => {
        $c == '\''
    };
}

#[macro_export]
macro_rules! small_letter_char {
    ($c: expr) => {
        ('a'..='z').contains(&$c)
    };
}

#[macro_export]
macro_rules! solo_char {
    ($c: expr) => {
        $crate::char_class!($c, ['!', '(', ')', ',', ';', '[', ']', '{', '}', '|', '%'])
    };
}

#[macro_export]
macro_rules! space_char {
    ($c: expr) => {
        $c == ' '
    };
}

#[macro_export]
macro_rules! symbolic_control_char {
    ($c: expr) => {
        $crate::char_class!($c, ['a', 'b', 'f', 'n', 'r', 't', 'v', '0'])
    };
}

#[macro_export]
macro_rules! symbolic_hexadecimal_char {
    ($c: expr) => {
        $c == 'x'
    };
}

#[macro_export]
macro_rules! variable_indicator_char {
    ($c: expr) => {
        $c == '_'
    };
}
<|endoftext|>


<commit_before>#[feature(macro_rules)];
#[feature(globs)];

extern mod glfw;
extern mod gl;
extern mod snowmew;
extern mod cgmath;

use snowmew::core::FrameBuffer;
use cgmath::matrix::*;

static VS_SRC: &'static str =
"#version 150\n\
uniform mat4 mat; \n\
in vec3 position;\n\
out vec2 UV;\n\
void main() {\n\
    gl_Position = mat * vec4(position, 1.);\n\
    UV = vec2(position.x, position.y); \n\
}";

static FS_SRC: &'static str =
"#version 150\n\
out vec4 out_color;\n\
in vec2 UV;\n \
void main() {\n\
    gl_FragColor = vec4(UV.x, UV.y, 1, 0);\n\
}";

static VERTEX_DATA: [f32, ..24] = [
    -1., -1., -1.,
    -1.,  1., -1.,
     1., -1., -1.,
     1.,  1., -1.,
    -1., -1.,  1.,
    -1.,  1.,  1.,
     1., -1.,  1.,
     1.,  1.,  1.,
];

static INDEX_DATA: [u16, ..36] = [
    // top
    0, 1, 2,
    1, 2, 3,

    // bottom
    4, 5, 6,
    5, 6, 7,

    // right
    1, 3, 5,
    3, 5, 7,

    // left
    0, 2, 4,
    2, 4, 6,

    // front
    2, 3, 6,
    3, 6, 7,

    // back
    0, 1, 4,
    1, 4, 5
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn main() {
    do glfw::set_error_callback |_, description| {
        print(format!("GLFW Error: {}", description));
    }

    do glfw::start {
        glfw::window_hint::context_version(4, 0);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 1024, "OpenGL", glfw::Windowed).unwrap();
        window.make_context_current();

        gl::load_with(glfw::get_proc_address);

        let geo = snowmew::geometry::Geometry::triangles(VERTEX_DATA, INDEX_DATA);
        let shader = snowmew::shader::Shader::new(VS_SRC, FS_SRC, (gl::ONE, gl::ZERO));
        let mut fb = snowmew::coregl::FrameBuffer {
            id: 0,
            width: 1024,
            height: 1024
        };

        let mat: Mat4<f32> = cgmath::matrix::Mat4::identity();

        let projection = cgmath::projection::perspective(
            cgmath::angle::deg(90f32), 1f32, 0.01f32, 10f32
        );

        gl::Enable(gl::SCISSOR_TEST);
        gl::Enable(gl::DEPTH_TEST);

        let mut rot = 0f32;

        while !window.should_close() {
            glfw::poll_events();

            let transform = cgmath::matrix::Mat4::scale(0.5f32, 0.5f32, 0.5f32);
            let transform = cgmath::matrix::Mat3::from_angle_x(cgmath::angle::rad(rot)).to_mat4().mul_m(&transform);
            let transform = cgmath::matrix::Mat3::from_angle_y(cgmath::angle::rad(rot)).to_mat4().mul_m(&transform);
            let transform = cgmath::matrix::Mat3::from_angle_z(cgmath::angle::rad(rot)).to_mat4().mul_m(&transform);
            let transform = cgmath::matrix::Mat4::translate(0f32, 0f32, -5f32).mul_m(&transform);
            let transform = projection.mul_m(&transform);

            do fb.viewport((0, 0), (1024, 1024)) |dt| {
                gl::ClearColor(0.3, 0.3, 0.3, 1.0);
                gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
                dt.draw(&shader, &geo, &[(&"mat", &transform as &snowmew::coregl::Uniforms)], &[]);
            }
            window.swap_buffers();
            rot += 0.01f32;
        }
    }

}<commit_msg>fixed faces<commit_after>#[feature(macro_rules)];
#[feature(globs)];

extern mod glfw;
extern mod gl;
extern mod snowmew;
extern mod cgmath;

use snowmew::core::FrameBuffer;
use cgmath::matrix::*;

static VS_SRC: &'static str =
"#version 150\n\
uniform mat4 mat; \n\
in vec3 position;\n\
out vec3 UV;\n\
void main() {\n\
    gl_Position = mat * vec4(position, 1.);\n\
    UV = vec3(position.x, position.y, position.z); \n\
}";

static FS_SRC: &'static str =
"#version 150\n\
out vec4 out_color;\n\
in vec3 UV;\n \
void main() {\n\
    gl_FragColor = vec4(UV.x, UV.y, UV.z, 0);\n\
}";

static VERTEX_DATA: [f32, ..24] = [
    -1., -1.,  1.,
    -1.,  1.,  1.,
     1., -1.,  1.,
     1.,  1.,  1.,
    -1., -1., -1.,
    -1.,  1., -1.,
     1., -1., -1.,
     1.,  1., -1.,
];

static INDEX_DATA: [u16, ..36] = [
    // top
    0, 2, 1,
    2, 3, 1,

    // bottom
    5, 7, 4,
    7, 6, 4,

    // right
    1, 3, 5,
    3, 7, 5,

    // left
    4, 6, 0,
    6, 2, 0,

    // front
    4, 0, 5,
    0, 1, 5,

    // back
    2, 6, 3,
    6, 7, 3,
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn main() {
    do glfw::set_error_callback |_, description| {
        print(format!("GLFW Error: {}", description));
    }

    do glfw::start {
        glfw::window_hint::context_version(4, 0);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 1024, "OpenGL", glfw::Windowed).unwrap();
        window.make_context_current();

        gl::load_with(glfw::get_proc_address);

        let geo = snowmew::geometry::Geometry::triangles(VERTEX_DATA, INDEX_DATA);
        let shader = snowmew::shader::Shader::new(VS_SRC, FS_SRC, (gl::ONE, gl::ZERO));
        let mut fb = snowmew::coregl::FrameBuffer {
            id: 0,
            width: 1024,
            height: 1024
        };

        let mat: Mat4<f32> = cgmath::matrix::Mat4::identity();

        let projection = cgmath::projection::perspective(
            cgmath::angle::deg(90f32), 1f32, 0.01f32, 10f32
        );

        gl::Enable(gl::SCISSOR_TEST);
        gl::Enable(gl::DEPTH_TEST);
        gl::Enable(gl::CULL_FACE);
        gl::CullFace(gl::BACK);

        let mut rot = 0f32;

        while !window.should_close() {
            glfw::poll_events();

            let transform = cgmath::matrix::Mat4::scale(1f32, 1f32, 1f32);
            let transform = cgmath::matrix::Mat3::from_angle_x(cgmath::angle::rad(rot)).to_mat4().mul_m(&transform);
            let transform = cgmath::matrix::Mat3::from_angle_y(cgmath::angle::rad(rot)).to_mat4().mul_m(&transform);
            let transform = cgmath::matrix::Mat3::from_angle_z(cgmath::angle::rad(rot)).to_mat4().mul_m(&transform);
            let transform = cgmath::matrix::Mat4::translate(0f32, 0f32, -5f32).mul_m(&transform);
            let transform = projection.mul_m(&transform);

            do fb.viewport((0, 0), (1024, 1024)) |dt| {
                gl::ClearColor(0.3, 0.3, 0.3, 1.0);
                gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
                dt.draw(&shader, &geo, &[(&"mat", &transform as &snowmew::coregl::Uniforms)], &[]);
            }
            window.swap_buffers();
            rot += 0.005f32;
        }
    }

}<|endoftext|>


<commit_before>use slack_api as slack;
use tokio_tungstenite as tungstenite;

use futures_util::{SinkExt, StreamExt};

use tokio::sync::mpsc;

use serde::Deserialize;
use serde_json::json;

use atomic_counter::AtomicCounter;
use atomic_counter::RelaxedCounter;

use std::sync::Arc;
use std::default::Default;
use std::env;
use std::result::Result;

use chrono::prelude::{Utc, DateTime};
use std::time::Duration;
use humantime::format_duration;

// SQlite worker thread
use std::thread;
use futures::executor::block_on_stream;

use rusqlite as rs;
use std::path::Path;

// Use of a mod or pub mod is not actually necessary.
pub mod build_info {
   // The file has been placed there by the build script.
   include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

// Type alias for msg_id
type MsgId = Arc<RelaxedCounter>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Shared integer counter for message ids
    let msg_id = Arc::new(RelaxedCounter::new(0));

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // Post a message
    let msg = slack::chat::PostMessageRequest {
        channel: "karmator-devs",
        text: "Pogger, i'm starting up!",
        as_user: Some(true),
        ..Default::default()
    };
    slack::chat::post_message(&client, &token, &msg).await;

    // Work to establish the WS connection
    let response = slack::rtm::connect(&client, &token).await.map_err(|e| format!("Control - {:?}", e))?;
    println!("Control - Got an ok reply");

    let ws_url = response.url.ok_or(format!("Control - \tNo Ws url"))?;
    println!("Control - \tGot a WS url: {:?}", ws_url);

    let (ws_stream, _) = tungstenite::connect_async(ws_url).await.map_err(|e| format!("Control - \t\t{:?}", e))?;
    println!("Control - \t\tWS connection established");

    // Setup the mpsc channel
    let (tx, mut rx) = mpsc::channel(32);

    // Split the stream
    let (mut ws_write, mut ws_read) = ws_stream.split();

    // Setup the mpsc channel for sqlite and spawn the sqlite worker thread
    // TODO: define a data format for asking the db to query something on your behalf, for now
    // pass in the raw query string?
    let (sql_tx, sql_rx) = mpsc::channel(32);

    let sql_worker = thread::spawn(move || {
        println!("Sql Worker - Launching");
        process_queries(sql_rx);
        println!("Sql Worker - Exiting");
    });

    // Spawn the inbound ws stream processor
    let inbound = tokio::spawn(async move {
        loop {
            if let Some(Ok(ws_msg)) = ws_read.next().await {
                let msg_id = msg_id.clone();
                let tx2 = tx.clone();
                let sql_tx2 = sql_tx.clone();

                tokio::spawn(async move {
                    process_inbound_message(msg_id, ws_msg, tx2, sql_tx2, start_time).await;
                });

            }
        }
    });

    // Spawn outbound ws processor
    // TODO: need to make sure to wait on sending till we recieve the hello event
    // could be a oneshot or some sort of flag which it waits till inbound has
    // gotten the hello
    let outbound = tokio::spawn(async move {
        loop {
            while let Some(message) = rx.recv().await {
                println!("Outbound - {:?}", message);

                // TODO: present some way to do plain vs fancy message, and if
                // fancy do a webapi post, otherwise dump into the WS
                //
                // TODO: look into tracking the sent message with a confirmation
                // that it was sent (via msg id) and if it fails, resend with
                // backoff
                //
                // TODO: find a way to handle the ping/pong cycle and monitor
                ws_write.send(message).await;
            }
        }
    });

    // Wait till either exits (error) then begin recovery
    match tokio::try_join!(inbound, outbound) {
        Ok((first, second)) => println!("Control - \t\t\tBoth exited fine"),
        Err(err) => println!("Control - \t\t\tSomething failed: {:?}", err),
    }

    // The sql_tx got moved into the inbound tokio async, so when that dies....
    let res = sql_worker.join();
    println!("Control - \t\t\tSql worker: {:?}", res);

    Ok(())
}

fn process_queries(
    sql_rx: mpsc::Receiver<runQuery>
) {
    let mut block_sql_rx = block_on_stream(sql_rx);

    //let filename = "db.sqlite";
    //let conn = rs::Connection::open_with_flags(
    //    Path::new(filename),
    //    rs::OpenFlags::SQLITE_OPEN_READ_WRITE
    //).expect(&format!("Connection error: {}", filename));
    let conn = rs::Connection::open_in_memory().expect("Failure");

    // Just create a db table
    let res = conn.execute(
        "CREATE TABLE votes (
            for TEXT NOT NULL,
            what TEXT NOT NULL
        )",
        rs::params![],
    );
    println!("Sql Worker - Table - {:?}", res);

    // Listen for inbound query
    while let Some(query) = block_sql_rx.next() {
        match query {
            runQuery::Query => {
                println!("Sql Worker - Query");
                let mut stmt = conn.prepare("SELECT * FROM votes").unwrap();

                let mut vote_iter = stmt.query_map(rs::params![], |row| {
                    let a: String = row.get(0).unwrap();
                    let b: String = row.get(1).unwrap();

                    Ok(format!("{} {}", a, b))
                }).unwrap();

                // Result
                if let Some(Ok(res)) = vote_iter.next() {
                    println!("Sql Worker - Query - {:?}", res);
                } else {
                    println!("Sql Worker - Query - Fail");
                }
            },
            runQuery::Insert(f, w) => {
                println!("Sql Worker - Insert");
                let mut stmt = conn.prepare("INSERT INTO votes (for, what) VALUES (?, ?)").unwrap();
                let res = stmt.execute(&[f, w]);

                println!("Sql Worker - Insert - {:?}", res);
            }
        }
    }
}

#[derive(Debug)]
enum runQuery {
    Insert(String, String),
    Query,
}

#[derive(Debug)]
enum resQuery {
    Insert(usize),
    Query(String),
}

async fn process_inbound_message(
    msg_id: MsgId,
    msg: tungstenite::tungstenite::Message,
    mut tx: mpsc::Sender<tungstenite::tungstenite::Message>,
    mut sql_tx: mpsc::Sender<runQuery>,
    start_time: DateTime<Utc>
) -> Result<(), Box<dyn std::error::Error>>
{
    let id = msg_id.inc();

    println!("Inbound - id = {:?}", id);

    // Parse incoming message
    let raw_msg = match msg {
        tungstenite::tungstenite::Message::Text(x) => Some(x),
        _ => {
            println!("Inbound - \tUnsupported ws message type - {:?}", msg);
            None
        },
    };
    println!("Inbound - raw event = {:?}", raw_msg);

    if let Some(e) = raw_msg.and_then(parse_event) {
        match e {
            Event::UserEvent(event) => {
                // Check if its a message/certain string, if so, reply
                match event {
                    // !version
                    UserEvent::Message {
                        channel: Some(c),
                        text: t,
                        subtype: _,
                        hidden: _,
                        user: u,
                        ts: _,
                    } if c == "CAF6S4TRT".to_string() => {
                        if t == "!version".to_string() {
                            let ver = build_info::PKG_VERSION;
                            let dat = build_info::BUILT_TIME_UTC;
                            let sha = build_info::GIT_COMMIT_HASH.unwrap_or("Unknown");

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Cargo Version: {} - Build Date: {}, - Build SHA: {}", u, ver, dat, sha),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!version");

                        } else if t == "!uptime".to_string() {
                            let end_time: DateTime<Utc> = Utc::now();
                            let durt = end_time.signed_duration_since(start_time).to_std().unwrap_or(
                                Duration::from_secs(3122064000));

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, format_duration(durt).to_string()),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!uptime");

                        } else if t == "!help".to_string() {
                            let help = "Available commands: !uptime !version !github !sidevotes !karma !givers !rank !ranksidevote";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, help),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!help");

                        } else if t == "!github".to_string() {
                            let github = "Github repo: https://github.com/pharaun/Karmator";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, github),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!github");

                        } else if t == "!run_sql_query".to_string() {
                            sql_tx.send(runQuery::Insert("hi".to_string(), "bye".to_string())).await;
                            sql_tx.send(runQuery::Query).await;

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Ok running query", u),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            println!("Inbound - \t\t!run_sql_query");
                        }
                    },

                    _ => println!("Inbound - \t\tNo action taken"),
                }
            },

            Event::SystemControl(sc) => (),
            Event::MessageControl(mc) => (),
        }
    }
    Ok(())
}

fn parse_event(s: String) -> Option<Event> {
    // Return User Events first, then the other two
    serde_json::from_str(&s).and_then(|ue| {
        println!("Inbound - \tUser Event = {:?}", ue);
        Ok(Event::UserEvent(ue))
    }).or_else(|_| {
        serde_json::from_str(&s).and_then(|sc| {
            println!("Inbound - \tSystem Control = {:?}", sc);
            Ok(Event::SystemControl(sc))
        }).or_else(|_| {
            serde_json::from_str(&s).and_then(|mc| {
                println!("Inbound - \tMessage Control = {:?}", mc);
                Ok(Event::MessageControl(mc))
            }).or_else(|x| {
                // TODO: for now print to stderr what didn't get deserialized
                // later can have config option to log to file the error or not
                eprintln!("Inbound - \tFail parse = {:?}", s);
                Err(x)
            })
        })
    }).ok()
}

#[derive(Debug)]
enum Event {
    UserEvent(UserEvent),
    SystemControl(SystemControl),
    MessageControl(MessageControl),
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum UserEvent {
    // These events are what plugins will care for:
    //
    // Slack messages
    // TODO: more involved here for now basics
    Message {
        subtype: Option<String>,
        hidden: Option<bool>,
        channel: Option<String>,
        user: String,
        text: String,
        ts: String,
    },

    // TODO: consider looking at slack_api for types to reuse here
    ReactionAdded {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
    ReactionRemoved {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum SystemControl {
    // First message upon successful connection establishment
    Hello,

    // Client should reconnect after getting such message
    Goodbye,

    // Reply to Ping = { type: ping, id: num }
    Pong { reply_to: usize },
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum MessageControl {
    // Reply to message sent
    MessageSent {
        ok: bool,
        reply_to: usize,
        text: String,
        ts: String
    },

    // Reply to failed message sent
    MessageError {
        ok: bool,
        reply_to: usize,
        error: ErrorDetail,
    },
}

#[derive(Debug, Deserialize)]
struct ErrorDetail {
    code: usize,
    msg: String,
}
<commit_msg>Supports oneshot sending the result back if its desired via Some<oneshot><commit_after>use slack_api as slack;
use tokio_tungstenite as tungstenite;

use futures_util::{SinkExt, StreamExt};

use tokio::sync::mpsc;

use serde::Deserialize;
use serde_json::json;

use atomic_counter::AtomicCounter;
use atomic_counter::RelaxedCounter;

use std::sync::Arc;
use std::default::Default;
use std::env;
use std::result::Result;

use chrono::prelude::{Utc, DateTime};
use std::time::Duration;
use humantime::format_duration;

// SQlite worker thread
use std::thread;
use futures::executor::block_on_stream;

use rusqlite as rs;
use std::path::Path;
use tokio::sync::oneshot;

// Use of a mod or pub mod is not actually necessary.
pub mod build_info {
   // The file has been placed there by the build script.
   include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

// Type alias for msg_id
type MsgId = Arc<RelaxedCounter>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Shared integer counter for message ids
    let msg_id = Arc::new(RelaxedCounter::new(0));

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // Post a message
    let msg = slack::chat::PostMessageRequest {
        channel: "karmator-devs",
        text: "Pogger, i'm starting up!",
        as_user: Some(true),
        ..Default::default()
    };
    slack::chat::post_message(&client, &token, &msg).await;

    // Work to establish the WS connection
    let response = slack::rtm::connect(&client, &token).await.map_err(|e| format!("Control - {:?}", e))?;
    println!("Control - Got an ok reply");

    let ws_url = response.url.ok_or(format!("Control - \tNo Ws url"))?;
    println!("Control - \tGot a WS url: {:?}", ws_url);

    let (ws_stream, _) = tungstenite::connect_async(ws_url).await.map_err(|e| format!("Control - \t\t{:?}", e))?;
    println!("Control - \t\tWS connection established");

    // Setup the mpsc channel
    let (tx, mut rx) = mpsc::channel(32);

    // Split the stream
    let (mut ws_write, mut ws_read) = ws_stream.split();

    // Setup the mpsc channel for sqlite and spawn the sqlite worker thread
    // TODO: define a data format for asking the db to query something on your behalf, for now
    // pass in the raw query string?
    let (sql_tx, sql_rx) = mpsc::channel(32);

    let sql_worker = thread::spawn(move || {
        println!("Sql Worker - Launching");
        process_queries(sql_rx);
        println!("Sql Worker - Exiting");
    });

    // Spawn the inbound ws stream processor
    let inbound = tokio::spawn(async move {
        loop {
            if let Some(Ok(ws_msg)) = ws_read.next().await {
                let msg_id = msg_id.clone();
                let tx2 = tx.clone();
                let sql_tx2 = sql_tx.clone();

                tokio::spawn(async move {
                    process_inbound_message(msg_id, ws_msg, tx2, sql_tx2, start_time).await;
                });

            }
        }
    });

    // Spawn outbound ws processor
    // TODO: need to make sure to wait on sending till we recieve the hello event
    // could be a oneshot or some sort of flag which it waits till inbound has
    // gotten the hello
    let outbound = tokio::spawn(async move {
        loop {
            while let Some(message) = rx.recv().await {
                println!("Outbound - {:?}", message);

                // TODO: present some way to do plain vs fancy message, and if
                // fancy do a webapi post, otherwise dump into the WS
                //
                // TODO: look into tracking the sent message with a confirmation
                // that it was sent (via msg id) and if it fails, resend with
                // backoff
                //
                // TODO: find a way to handle the ping/pong cycle and monitor
                ws_write.send(message).await;
            }
        }
    });

    // Wait till either exits (error) then begin recovery
    match tokio::try_join!(inbound, outbound) {
        Ok((first, second)) => println!("Control - \t\t\tBoth exited fine"),
        Err(err) => println!("Control - \t\t\tSomething failed: {:?}", err),
    }

    // The sql_tx got moved into the inbound tokio async, so when that dies....
    let res = sql_worker.join();
    println!("Control - \t\t\tSql worker: {:?}", res);

    Ok(())
}

fn process_queries(
    sql_rx: mpsc::Receiver<(runQuery, Option<oneshot::Sender<resQuery>>)>
) {
    let mut block_sql_rx = block_on_stream(sql_rx);

    //let filename = "db.sqlite";
    //let conn = rs::Connection::open_with_flags(
    //    Path::new(filename),
    //    rs::OpenFlags::SQLITE_OPEN_READ_WRITE
    //).expect(&format!("Connection error: {}", filename));
    let conn = rs::Connection::open_in_memory().expect("Failure");

    // Just create a db table
    let res = conn.execute(
        "CREATE TABLE votes (
            for TEXT NOT NULL,
            what TEXT NOT NULL
        )",
        rs::params![],
    );
    println!("Sql Worker - Table - {:?}", res);

    // Listen for inbound query
    while let Some((query, res_tx)) = block_sql_rx.next() {
        match query {
            runQuery::Query => {
                println!("Sql Worker - Query");
                let mut stmt = conn.prepare("SELECT * FROM votes").unwrap();

                let mut vote_iter = stmt.query_map(rs::params![], |row| {
                    let a: String = row.get(0).unwrap();
                    let b: String = row.get(1).unwrap();

                    Ok(format!("{} {}", a, b))
                }).unwrap();

                // Result
                if let Some(Ok(res)) = vote_iter.next() {
                    println!("Sql Worker - Query - {:?}", res);
                    res_tx.unwrap().send(resQuery::Query(res));
                } else {
                    println!("Sql Worker - Query - Fail");
                }
            },
            runQuery::Insert(f, w) => {
                println!("Sql Worker - Insert");
                let mut stmt = conn.prepare("INSERT INTO votes (for, what) VALUES (?, ?)").unwrap();
                let res = stmt.execute(&[f, w]);

                println!("Sql Worker - Insert - {:?}", res);
                res_tx.unwrap().send(resQuery::Insert(res.unwrap()));
            }
        }
    }
}

#[derive(Debug)]
enum runQuery {
    Insert(String, String),
    Query,
}

#[derive(Debug)]
enum resQuery {
    Insert(usize),
    Query(String),
}

async fn process_inbound_message(
    msg_id: MsgId,
    msg: tungstenite::tungstenite::Message,
    mut tx: mpsc::Sender<tungstenite::tungstenite::Message>,
    mut sql_tx: mpsc::Sender<(runQuery, Option<oneshot::Sender<resQuery>>)>,
    start_time: DateTime<Utc>
) -> Result<(), Box<dyn std::error::Error>>
{
    let id = msg_id.inc();

    println!("Inbound - id = {:?}", id);

    // Parse incoming message
    let raw_msg = match msg {
        tungstenite::tungstenite::Message::Text(x) => Some(x),
        _ => {
            println!("Inbound - \tUnsupported ws message type - {:?}", msg);
            None
        },
    };
    println!("Inbound - raw event = {:?}", raw_msg);

    if let Some(e) = raw_msg.and_then(parse_event) {
        match e {
            Event::UserEvent(event) => {
                // Check if its a message/certain string, if so, reply
                match event {
                    // !version
                    UserEvent::Message {
                        channel: Some(c),
                        text: t,
                        subtype: _,
                        hidden: _,
                        user: u,
                        ts: _,
                    } if c == "CAF6S4TRT".to_string() => {
                        if t == "!version".to_string() {
                            let ver = build_info::PKG_VERSION;
                            let dat = build_info::BUILT_TIME_UTC;
                            let sha = build_info::GIT_COMMIT_HASH.unwrap_or("Unknown");

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Cargo Version: {} - Build Date: {}, - Build SHA: {}", u, ver, dat, sha),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!version");

                        } else if t == "!uptime".to_string() {
                            let end_time: DateTime<Utc> = Utc::now();
                            let durt = end_time.signed_duration_since(start_time).to_std().unwrap_or(
                                Duration::from_secs(3122064000));

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, format_duration(durt).to_string()),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!uptime");

                        } else if t == "!help".to_string() {
                            let help = "Available commands: !uptime !version !github !sidevotes !karma !givers !rank !ranksidevote";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, help),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!help");

                        } else if t == "!github".to_string() {
                            let github = "Github repo: https://github.com/pharaun/Karmator";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, github),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!github");

                        } else if t == "!run_query".to_string() {
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Ok running query", u),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            // Dummy get/send stuff
                            let (tx1, rx1) = oneshot::channel();
                            sql_tx.send((
                                runQuery::Insert("hi".to_string(), "bye".to_string()),
                                Some(tx1)
                            )).await;

                            let (tx2, rx2) = oneshot::channel();
                            sql_tx.send((
                                runQuery::Query,
                                Some(tx2)
                            )).await;

                            // Await for the results
                            let res1 = rx1.await;
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: insert: {:?}", u, res1),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            let res2 = rx2.await;
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: query: {:?}", u, res2),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            println!("Inbound - \t\t!run_sql_query");
                        }
                    },

                    _ => println!("Inbound - \t\tNo action taken"),
                }
            },

            Event::SystemControl(sc) => (),
            Event::MessageControl(mc) => (),
        }
    }
    Ok(())
}

fn parse_event(s: String) -> Option<Event> {
    // Return User Events first, then the other two
    serde_json::from_str(&s).and_then(|ue| {
        println!("Inbound - \tUser Event = {:?}", ue);
        Ok(Event::UserEvent(ue))
    }).or_else(|_| {
        serde_json::from_str(&s).and_then(|sc| {
            println!("Inbound - \tSystem Control = {:?}", sc);
            Ok(Event::SystemControl(sc))
        }).or_else(|_| {
            serde_json::from_str(&s).and_then(|mc| {
                println!("Inbound - \tMessage Control = {:?}", mc);
                Ok(Event::MessageControl(mc))
            }).or_else(|x| {
                // TODO: for now print to stderr what didn't get deserialized
                // later can have config option to log to file the error or not
                eprintln!("Inbound - \tFail parse = {:?}", s);
                Err(x)
            })
        })
    }).ok()
}

#[derive(Debug)]
enum Event {
    UserEvent(UserEvent),
    SystemControl(SystemControl),
    MessageControl(MessageControl),
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum UserEvent {
    // These events are what plugins will care for:
    //
    // Slack messages
    // TODO: more involved here for now basics
    Message {
        subtype: Option<String>,
        hidden: Option<bool>,
        channel: Option<String>,
        user: String,
        text: String,
        ts: String,
    },

    // TODO: consider looking at slack_api for types to reuse here
    ReactionAdded {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
    ReactionRemoved {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum SystemControl {
    // First message upon successful connection establishment
    Hello,

    // Client should reconnect after getting such message
    Goodbye,

    // Reply to Ping = { type: ping, id: num }
    Pong { reply_to: usize },
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum MessageControl {
    // Reply to message sent
    MessageSent {
        ok: bool,
        reply_to: usize,
        text: String,
        ts: String
    },

    // Reply to failed message sent
    MessageError {
        ok: bool,
        reply_to: usize,
        error: ErrorDetail,
    },
}

#[derive(Debug, Deserialize)]
struct ErrorDetail {
    code: usize,
    msg: String,
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use crate::completion::{
    completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
    CompletionItemKind, CompletionKind, Completions,
};

fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
    CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
        .insert_snippet(cap, snippet)
        .kind(CompletionItemKind::Snippet)
}

pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
        return;
    }
    let cap = match ctx.config.snippet_cap {
        Some(it) => it,
        None => return,
    };

    snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
    snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
}

pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_new_item {
        return;
    }
    let cap = match ctx.config.snippet_cap {
        Some(it) => it,
        None => return,
    };

    snippet(
        ctx,
        cap,
        "Test module",
        "\
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ${1:test_name}() {
        $0
    }
}",
    )
    .lookup_by("tmod")
    .add_to(acc);

    snippet(
        ctx,
        cap,
        "Test function",
        "\
#[test]
fn ${1:feature}() {
    $0
}",
    )
    .lookup_by("tfn")
    .add_to(acc);

    snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
    snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
}

#[cfg(test)]
mod tests {
    use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
    use insta::assert_debug_snapshot;

    fn do_snippet_completion(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Snippet)
    }

    #[test]
    fn completes_snippets_in_expressions() {
        assert_debug_snapshot!(
                    do_snippet_completion(r"fn foo(x: i32) { <|> }"),
        @r###"
        [
            CompletionItem {
                label: "pd",
                source_range: 17..17,
                delete: 17..17,
                insert: "eprintln!(\"$0 = {:?}\", $0);",
                kind: Snippet,
            },
            CompletionItem {
                label: "ppd",
                source_range: 17..17,
                delete: 17..17,
                insert: "eprintln!(\"$0 = {:#?}\", $0);",
                kind: Snippet,
            },
        ]
        "###
                );
    }

    #[test]
    fn should_not_complete_snippets_in_path() {
        assert_debug_snapshot!(
                    do_snippet_completion(r"fn foo(x: i32) { ::foo<|> }"),
        @"[]"
                );
        assert_debug_snapshot!(
                    do_snippet_completion(r"fn foo(x: i32) { ::<|> }"),
        @"[]"
                );
    }

    #[test]
    fn completes_snippets_in_items() {
        assert_debug_snapshot!(
            do_snippet_completion(
                r"
                #[cfg(test)]
                mod tests {
                    <|>
                }
                "
            ),
            @r###"
        [
            CompletionItem {
                label: "Test function",
                source_range: 78..78,
                delete: 78..78,
                insert: "#[test]\nfn ${1:feature}() {\n    $0\n}",
                kind: Snippet,
                lookup: "tfn",
            },
            CompletionItem {
                label: "macro_rules",
                source_range: 78..78,
                delete: 78..78,
                insert: "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}",
                kind: Snippet,
            },
            CompletionItem {
                label: "pub(crate)",
                source_range: 78..78,
                delete: 78..78,
                insert: "pub(crate) $0",
                kind: Snippet,
            },
        ]
        "###
        );
    }
}
<commit_msg>add tests module snippet<commit_after>//! FIXME: write short doc here

use crate::completion::{
    completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
    CompletionItemKind, CompletionKind, Completions,
};

fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
    CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
        .insert_snippet(cap, snippet)
        .kind(CompletionItemKind::Snippet)
}

pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
        return;
    }
    let cap = match ctx.config.snippet_cap {
        Some(it) => it,
        None => return,
    };

    snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
    snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
}

pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_new_item {
        return;
    }
    let cap = match ctx.config.snippet_cap {
        Some(it) => it,
        None => return,
    };

    snippet(
        ctx,
        cap,
        "Test module",
        "\
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ${1:test_name}() {
        $0
    }
}",
    )
    .lookup_by("tmod")
    .add_to(acc);

    snippet(
        ctx,
        cap,
        "Test function",
        "\
#[test]
fn ${1:feature}() {
    $0
}",
    )
    .lookup_by("tfn")
    .add_to(acc);

    snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
    snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
}

#[cfg(test)]
mod tests {
    use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
    use insta::assert_debug_snapshot;

    fn do_snippet_completion(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Snippet)
    }

    #[test]
    fn completes_snippets_in_expressions() {
        assert_debug_snapshot!(
                    do_snippet_completion(r"fn foo(x: i32) { <|> }"),
        @r###"
        [
            CompletionItem {
                label: "pd",
                source_range: 17..17,
                delete: 17..17,
                insert: "eprintln!(\"$0 = {:?}\", $0);",
                kind: Snippet,
            },
            CompletionItem {
                label: "ppd",
                source_range: 17..17,
                delete: 17..17,
                insert: "eprintln!(\"$0 = {:#?}\", $0);",
                kind: Snippet,
            },
        ]
        "###
                );
    }

    #[test]
    fn should_not_complete_snippets_in_path() {
        assert_debug_snapshot!(
                    do_snippet_completion(r"fn foo(x: i32) { ::foo<|> }"),
        @"[]"
                );
        assert_debug_snapshot!(
                    do_snippet_completion(r"fn foo(x: i32) { ::<|> }"),
        @"[]"
                );
    }

    #[test]
    fn completes_snippets_in_items() {
        assert_debug_snapshot!(
            do_snippet_completion(
                r"
                #[cfg(test)]
                mod tests {
                    <|>
                }
                "
            ),
            @r###"
        [
            CompletionItem {
                label: "Test function",
                source_range: 78..78,
                delete: 78..78,
                insert: "#[test]\nfn ${1:feature}() {\n    $0\n}",
                kind: Snippet,
                lookup: "tfn",
            },
            CompletionItem {
                label: "Test module",
                source_range: 78..78,
                delete: 78..78,
                insert: "#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn ${1:test_name}() {\n        $0\n    }\n}",
                kind: Snippet,
                lookup: "tmod",
            },
            CompletionItem {
                label: "macro_rules",
                source_range: 78..78,
                delete: 78..78,
                insert: "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}",
                kind: Snippet,
            },
            CompletionItem {
                label: "pub(crate)",
                source_range: 78..78,
                delete: 78..78,
                insert: "pub(crate) $0",
                kind: Snippet,
            },
        ]
        "###
        );
    }
}
<|endoftext|>


<commit_before>use crate::hil::spi_host::SpiHost;
use core::cell::Cell;
use core::cmp::min;
use kernel::common::cells::{OptionalCell, TakeCell};
use kernel::common::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly};
use kernel::common::StaticRef;
use kernel::hil::spi::{ClockPolarity, ClockPhase, SpiMaster, SpiMasterClient};
use kernel::ReturnCode;

// The TX and RX FIFOs both have the same length. We write and read at the same
// time.

// Registers for the SPI host controller
register_structs! {
    Registers {
        (0x0000 => ctrl: ReadWrite<u32, CTRL::Register>),
        (0x0004 => xact: ReadWrite<u32, XACT::Register>),
        (0x0008 => ictrl: ReadWrite<u32, ICTRL::Register>),
        (0x000c => istate: ReadOnly<u32, ISTATE::Register>),
        (0x0010 => istate_clr: ReadWrite<u32, ISTATE_CLR::Register>),
        (0x0014 => _reserved),
        (0x1000 => tx_fifo: [WriteOnly<u8>; 128]),
        (0x1080 => rx_fifo: [ReadOnly<u8>; 128]),
        (0x1100 => @END),
    }
}

register_bitfields![u32,
    CTRL [
        /// CPOL setting
        CPOL OFFSET(0) NUMBITS(1) [],
        /// CPHA setting
        CPHA OFFSET(1) NUMBITS(1) [],
        /// CSB to SCK setup time in SCK cycles + 1.5
        CSBSU OFFSET(2) NUMBITS(4) [],
        /// CSB from SCK hold time in SCK cycles + 1 (defined with respect to
        /// the last SCK edge)
        CSBHLD OFFSET(6) NUMBITS(4) [],
        /// SPI Clk Divider. Actual divider is IDIV+1. A value of 0 gives divide
        /// by 1 clock, 1 gives divide by 2 etc.
        IDIV OFFSET(10) NUMBITS(12) [],
        /// Polarity of CSB signal. 0:active low 1:active high
        CSBPOL OFFSET(22) NUMBITS(1) [],
        /// Order in which bits of byte are sent. 0: send bit 0 first. 1: send
        /// bit 7 first
        TXBITOR OFFSET(23) NUMBITS(1) [],
        /// Order in which bytes of buffer word are sent.
        /// 0: send byte 0 first. 1: send byte 3 first
        TXBYTOR OFFSET(24) NUMBITS(1) [],
        /// Order in which received bits are packed into byte.
        /// 0: first bit received is bit0 1: last bit received is bit 0
        RXBITOR OFFSET(25) NUMBITS(1) [],
        /// Order in which received bytes are packed into word.
        /// 0: first byte received is byte 0 1: first byte received is byte 3
        RXBYTOR OFFSET(26) NUMBITS(1) [],
        /// SPI Passthrough Mode. 0: Disable, 1: Enable. This is the host side
        /// control of whether passthrough is allowed. In order for full
        /// passthrough functionality, both the host and device passthrough
        /// functionality have to be enabled
        ENPASSTHRU OFFSET(27) NUMBITS(1) []
    ],
    XACT [
        /// Initiate transaction in buffer
        START OFFSET(0) NUMBITS(1) [],
        /// Bits-1 in last byte transferred. The default assumes last byte will
        /// have 8 bits, this should be sufficient for most usage.
        BCNT OFFSET(1) NUMBITS(3) [],
        /// Total number of transactions in bytes-1. If 64 bytes are to be
        /// transferred, this should be programmed as 63.
        SIZE OFFSET(4) NUMBITS(7) [],
        /// Poll for ready
        RDY_POLL OFFSET(11) NUMBITS(1) [],
        /// Delay before polling in PCLK cycles + 1
        RDY_POLL_DLY OFFSET(12) NUMBITS(5) []
    ],
    ICTRL [
        /// TX interrupt enable
        TXDONE OFFSET(0) NUMBITS(1) []
    ],
    ISTATE [
        /// TX done interrupt
        TXDONE OFFSET(0) NUMBITS(1) []
    ],
    ISTATE_CLR [
        /// TX done interrupt clear
        TXDONE OFFSET(0) NUMBITS(1) []
    ]
];

const SPI_HOST0_BASE_ADDR: u32 = 0x4070_0000;
const SPI_HOST1_BASE_ADDR: u32 = 0x4071_0000;

const SPI_HOST0_REGISTERS: StaticRef<Registers> =
    unsafe { StaticRef::new(SPI_HOST0_BASE_ADDR as *const Registers) };
const SPI_HOST1_REGISTERS: StaticRef<Registers> =
    unsafe { StaticRef::new(SPI_HOST1_BASE_ADDR as *const Registers) };

pub static mut SPI_HOST0: SpiHostHardware = SpiHostHardware::new(SPI_HOST0_REGISTERS);

pub static mut SPI_HOST1: SpiHostHardware = SpiHostHardware::new(SPI_HOST1_REGISTERS);

/// A SPI Host
pub struct SpiHostHardware {
    registers: StaticRef<Registers>,
    transaction_len: Cell<usize>,
    tx_buffer: TakeCell<'static, [u8]>,
    rx_buffer: TakeCell<'static, [u8]>,
    client: OptionalCell<&'static dyn SpiMasterClient>,
}

impl SpiHostHardware {
    const fn new(base_addr: StaticRef<Registers>) -> SpiHostHardware {
        SpiHostHardware {
            registers: base_addr,
            transaction_len: Cell::new(0),
            tx_buffer: TakeCell::empty(),
            rx_buffer: TakeCell::empty(),
            client: OptionalCell::empty(),
        }
    }

    pub fn init(&self) {
        self.registers.ctrl.write(
            CTRL::CPOL::CLEAR +
            CTRL::CPHA::CLEAR +
            CTRL::CSBSU::CLEAR +
            CTRL::CSBHLD::CLEAR +
            CTRL::IDIV.val(2) +
            CTRL::CSBPOL::CLEAR +
            CTRL::TXBITOR::SET +
            CTRL::TXBYTOR::CLEAR +
            CTRL::RXBITOR::SET +
            CTRL::RXBYTOR::CLEAR +
            CTRL::ENPASSTHRU::CLEAR);
        self.registers.xact.write(
            XACT::START::CLEAR +
            XACT::BCNT.val(7) +
            XACT::SIZE.val(0) +
            XACT::RDY_POLL::CLEAR +
            XACT::RDY_POLL_DLY.val(0));
    }

    fn enable_tx_interrupt(&self) {
        self.registers.ictrl.modify(ICTRL::TXDONE::SET);
    }

    fn disable_tx_interrupt(&self) {
        self.registers.ictrl.modify(ICTRL::TXDONE::CLEAR);
    }

    pub fn handle_interrupt(&self) {
        //debug!("h1::Spi:handle_interrupt: ISTATE = {:08x}", self.registers.istate.get());
        if self.registers.istate.is_set(ISTATE::TXDONE) {
            self.registers.istate_clr.write(ISTATE_CLR::TXDONE::SET);
            self.client.map(|client| {
                self.tx_buffer.take()
                .map(|tx_buf| {
                    self.rx_buffer
                    .map(|rx_buf| {
                        self.read_data(rx_buf);
                    });

                    client.read_write_done(
                        tx_buf,
                        self.rx_buffer.take(),
                        self.transaction_len.get())
                });
            });
        }
        self.disable_tx_interrupt();
    }

    fn start_transaction(
        &self,
        write_buffer: Option<&'static mut [u8]>,
        read_buffer: Option<&'static mut [u8]>,
        transaction_len: usize) -> ReturnCode {
        //debug!("h1::Spi:start_transaction: transaction_len={}", transaction_len);
        // The transaction needs at least one byte.
        // It also cannot have more bytes than tx_fifo or rx_fifo is long.
        if (transaction_len == 0) ||
            (transaction_len >= self.registers.tx_fifo.len()) ||
            (transaction_len >= self.registers.rx_fifo.len()) {
            //debug!("h1::Spi::start_transaction: Invalid transaction_len={}", transaction_len);
            return ReturnCode::ESIZE;
        }
        self.registers.xact.modify(XACT::BCNT.val(7));
        self.registers.xact.modify(XACT::SIZE.val((transaction_len - 1) as u32));

        let mut tx_buf_len = 0;
        write_buffer.as_ref().map(|tx_buf| {
            tx_buf_len = min(tx_buf.len(), transaction_len);
            for idx in 0..tx_buf_len {
                self.registers.tx_fifo[idx].set(tx_buf[idx]);
            }
        });

        // Clear the TX FIFO for additional bytes not supplied by write_buffer.
        // Since we have no control over how many bytes the SPI host reads, we
        // want to make sure to not accidentally leak information that made it
        // into the TX FIFO beyond the length of the `write_buffer`.
        for idx in tx_buf_len..transaction_len {
            self.registers.tx_fifo[idx].set(0xff);
        }

        write_buffer.map(|buf| {
            self.tx_buffer.replace(buf);
        });
        read_buffer.map(|buf| {
            self.rx_buffer.replace(buf);
        });
        self.transaction_len.set(transaction_len);

        self.registers.istate_clr.write(ISTATE_CLR::TXDONE::SET);
        self.enable_tx_interrupt();
        self.registers.xact.modify(XACT::START::SET);
        ReturnCode::SUCCESS
    }

    fn read_data(&self, read_buffer: &mut [u8]) {
        let read_len = min(read_buffer.len(), self.transaction_len.get());
        for idx in 0..read_len {
            let val = self.registers.rx_fifo[idx].get();
            read_buffer[idx] = val;
        }
    }
}

impl SpiHost for SpiHostHardware {
    fn spi_device_spi_host_passthrough(&self, enabled: bool) {
        self.registers.ctrl.modify(
            if enabled { CTRL::ENPASSTHRU::SET } else { CTRL::ENPASSTHRU::CLEAR });
    }

    fn wait_busy_clear_in_transactions(&self, enabled: bool) {
        self.registers.xact.modify(
            if enabled { XACT::RDY_POLL::SET } else { XACT::RDY_POLL::CLEAR });
    }
}

impl SpiMaster for SpiHostHardware {
    type ChipSelect = bool;

    fn set_client(&self, client: &'static dyn kernel::hil::spi::SpiMasterClient) {
        self.client.set(client);
    }

    fn init(&self) {}

    fn is_busy(&self) -> bool {
        self.registers.istate.is_set(ISTATE::TXDONE)
    }

    fn read_write_bytes(
        &self,
        write_buffer: &'static mut [u8],
        read_buffer: Option<&'static mut [u8]>,
        len: usize,
    ) -> ReturnCode {
        // If busy, don't start
        if self.is_busy() {
            return ReturnCode::EBUSY;
        }

        self.start_transaction(Some(write_buffer), read_buffer, len)
    }

    fn write_byte(&self, _val: u8) {
        panic!("write_byte is not implemented");
    }
    fn read_byte(&self) -> u8 {
        panic!("read_byte is not implemented");
    }
    fn read_write_byte(&self, _val: u8) -> u8 {
        panic!("read_write_byte is not implemented");
    }

    fn specify_chip_select(&self, _cs: Self::ChipSelect) {
        // Nothing to be done
    }

    /// Returns the actual rate set
    fn set_rate(&self, _rate: u32) -> u32 {
        panic!("set_rate is not implemented");
    }
    fn get_rate(&self) -> u32 {
        panic!("get_rate is not implemented");
    }
    fn set_clock(&self, _polarity: ClockPolarity) {
        panic!("set_clock is not implemented");
    }
    fn get_clock(&self) -> ClockPolarity {
        panic!("get_clock is not implemented");
    }
    fn set_phase(&self, _phase: ClockPhase) {
        panic!("set_phase is not implemented");
    }
    fn get_phase(&self) -> ClockPhase {
        panic!("get_phase is not implemented");
    }

    fn hold_low(&self) {
        panic!("hold_low is not implemented");
    }
    fn release_low(&self) {
        // Nothing to do, since this is the only mode supported.
    }
}
<commit_msg>Fix incorrect handling of maximum buffer size in SpiHostHardware.<commit_after>use crate::hil::spi_host::SpiHost;
use core::cell::Cell;
use core::cmp::min;
use kernel::common::cells::{OptionalCell, TakeCell};
use kernel::common::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly};
use kernel::common::StaticRef;
use kernel::hil::spi::{ClockPolarity, ClockPhase, SpiMaster, SpiMasterClient};
use kernel::ReturnCode;

// The TX and RX FIFOs both have the same length. We write and read at the same
// time.

// Registers for the SPI host controller
register_structs! {
    Registers {
        (0x0000 => ctrl: ReadWrite<u32, CTRL::Register>),
        (0x0004 => xact: ReadWrite<u32, XACT::Register>),
        (0x0008 => ictrl: ReadWrite<u32, ICTRL::Register>),
        (0x000c => istate: ReadOnly<u32, ISTATE::Register>),
        (0x0010 => istate_clr: ReadWrite<u32, ISTATE_CLR::Register>),
        (0x0014 => _reserved),
        (0x1000 => tx_fifo: [WriteOnly<u8>; 128]),
        (0x1080 => rx_fifo: [ReadOnly<u8>; 128]),
        (0x1100 => @END),
    }
}

register_bitfields![u32,
    CTRL [
        /// CPOL setting
        CPOL OFFSET(0) NUMBITS(1) [],
        /// CPHA setting
        CPHA OFFSET(1) NUMBITS(1) [],
        /// CSB to SCK setup time in SCK cycles + 1.5
        CSBSU OFFSET(2) NUMBITS(4) [],
        /// CSB from SCK hold time in SCK cycles + 1 (defined with respect to
        /// the last SCK edge)
        CSBHLD OFFSET(6) NUMBITS(4) [],
        /// SPI Clk Divider. Actual divider is IDIV+1. A value of 0 gives divide
        /// by 1 clock, 1 gives divide by 2 etc.
        IDIV OFFSET(10) NUMBITS(12) [],
        /// Polarity of CSB signal. 0:active low 1:active high
        CSBPOL OFFSET(22) NUMBITS(1) [],
        /// Order in which bits of byte are sent. 0: send bit 0 first. 1: send
        /// bit 7 first
        TXBITOR OFFSET(23) NUMBITS(1) [],
        /// Order in which bytes of buffer word are sent.
        /// 0: send byte 0 first. 1: send byte 3 first
        TXBYTOR OFFSET(24) NUMBITS(1) [],
        /// Order in which received bits are packed into byte.
        /// 0: first bit received is bit0 1: last bit received is bit 0
        RXBITOR OFFSET(25) NUMBITS(1) [],
        /// Order in which received bytes are packed into word.
        /// 0: first byte received is byte 0 1: first byte received is byte 3
        RXBYTOR OFFSET(26) NUMBITS(1) [],
        /// SPI Passthrough Mode. 0: Disable, 1: Enable. This is the host side
        /// control of whether passthrough is allowed. In order for full
        /// passthrough functionality, both the host and device passthrough
        /// functionality have to be enabled
        ENPASSTHRU OFFSET(27) NUMBITS(1) []
    ],
    XACT [
        /// Initiate transaction in buffer
        START OFFSET(0) NUMBITS(1) [],
        /// Bits-1 in last byte transferred. The default assumes last byte will
        /// have 8 bits, this should be sufficient for most usage.
        BCNT OFFSET(1) NUMBITS(3) [],
        /// Total number of transactions in bytes-1. If 64 bytes are to be
        /// transferred, this should be programmed as 63.
        SIZE OFFSET(4) NUMBITS(7) [],
        /// Poll for ready
        RDY_POLL OFFSET(11) NUMBITS(1) [],
        /// Delay before polling in PCLK cycles + 1
        RDY_POLL_DLY OFFSET(12) NUMBITS(5) []
    ],
    ICTRL [
        /// TX interrupt enable
        TXDONE OFFSET(0) NUMBITS(1) []
    ],
    ISTATE [
        /// TX done interrupt
        TXDONE OFFSET(0) NUMBITS(1) []
    ],
    ISTATE_CLR [
        /// TX done interrupt clear
        TXDONE OFFSET(0) NUMBITS(1) []
    ]
];

const SPI_HOST0_BASE_ADDR: u32 = 0x4070_0000;
const SPI_HOST1_BASE_ADDR: u32 = 0x4071_0000;

const SPI_HOST0_REGISTERS: StaticRef<Registers> =
    unsafe { StaticRef::new(SPI_HOST0_BASE_ADDR as *const Registers) };
const SPI_HOST1_REGISTERS: StaticRef<Registers> =
    unsafe { StaticRef::new(SPI_HOST1_BASE_ADDR as *const Registers) };

pub static mut SPI_HOST0: SpiHostHardware = SpiHostHardware::new(SPI_HOST0_REGISTERS);

pub static mut SPI_HOST1: SpiHostHardware = SpiHostHardware::new(SPI_HOST1_REGISTERS);

/// A SPI Host
pub struct SpiHostHardware {
    registers: StaticRef<Registers>,
    transaction_len: Cell<usize>,
    tx_buffer: TakeCell<'static, [u8]>,
    rx_buffer: TakeCell<'static, [u8]>,
    client: OptionalCell<&'static dyn SpiMasterClient>,
}

impl SpiHostHardware {
    const fn new(base_addr: StaticRef<Registers>) -> SpiHostHardware {
        SpiHostHardware {
            registers: base_addr,
            transaction_len: Cell::new(0),
            tx_buffer: TakeCell::empty(),
            rx_buffer: TakeCell::empty(),
            client: OptionalCell::empty(),
        }
    }

    pub fn init(&self) {
        self.registers.ctrl.write(
            CTRL::CPOL::CLEAR +
            CTRL::CPHA::CLEAR +
            CTRL::CSBSU::CLEAR +
            CTRL::CSBHLD::CLEAR +
            CTRL::IDIV.val(2) +
            CTRL::CSBPOL::CLEAR +
            CTRL::TXBITOR::SET +
            CTRL::TXBYTOR::CLEAR +
            CTRL::RXBITOR::SET +
            CTRL::RXBYTOR::CLEAR +
            CTRL::ENPASSTHRU::CLEAR);
        self.registers.xact.write(
            XACT::START::CLEAR +
            XACT::BCNT.val(7) +
            XACT::SIZE.val(0) +
            XACT::RDY_POLL::CLEAR +
            XACT::RDY_POLL_DLY.val(0));
    }

    fn enable_tx_interrupt(&self) {
        self.registers.ictrl.modify(ICTRL::TXDONE::SET);
    }

    fn disable_tx_interrupt(&self) {
        self.registers.ictrl.modify(ICTRL::TXDONE::CLEAR);
    }

    pub fn handle_interrupt(&self) {
        //debug!("SpiHostHardware::handle_interrupt: ISTATE = {:08x}", self.registers.istate.get());
        if self.registers.istate.is_set(ISTATE::TXDONE) {
            self.registers.istate_clr.write(ISTATE_CLR::TXDONE::SET);
            self.client.map(|client| {
                self.tx_buffer.take()
                .map(|tx_buf| {
                    self.rx_buffer
                    .map(|rx_buf| {
                        self.read_data(rx_buf);
                    });

                    client.read_write_done(
                        tx_buf,
                        self.rx_buffer.take(),
                        self.transaction_len.get())
                });
            });
        }
        self.disable_tx_interrupt();
    }

    fn start_transaction(
        &self,
        write_buffer: Option<&'static mut [u8]>,
        read_buffer: Option<&'static mut [u8]>,
        transaction_len: usize) -> ReturnCode {
        //debug!("SpiHostHardware::start_transaction: transaction_len={}", transaction_len);
        // The transaction needs at least one byte.
        // It also cannot have more bytes than tx_fifo or rx_fifo is long.
        if (transaction_len == 0) ||
            (transaction_len > self.registers.tx_fifo.len()) ||
            (transaction_len > self.registers.rx_fifo.len()) {
            //debug!("SpiHostHardware::start_transaction: Invalid transaction_len={}", transaction_len);
            return ReturnCode::ESIZE;
        }
        self.registers.xact.modify(XACT::BCNT.val(7));
        self.registers.xact.modify(XACT::SIZE.val((transaction_len - 1) as u32));

        let mut tx_buf_len = 0;
        write_buffer.as_ref().map(|tx_buf| {
            tx_buf_len = min(tx_buf.len(), transaction_len);
            for idx in 0..tx_buf_len {
                self.registers.tx_fifo[idx].set(tx_buf[idx]);
            }
        });

        // Clear the TX FIFO for additional bytes not supplied by write_buffer.
        // Since we have no control over how many bytes the SPI host reads, we
        // want to make sure to not accidentally leak information that made it
        // into the TX FIFO beyond the length of the `write_buffer`.
        for idx in tx_buf_len..transaction_len {
            self.registers.tx_fifo[idx].set(0xff);
        }

        write_buffer.map(|buf| {
            self.tx_buffer.replace(buf);
        });
        read_buffer.map(|buf| {
            self.rx_buffer.replace(buf);
        });
        self.transaction_len.set(transaction_len);

        self.registers.istate_clr.write(ISTATE_CLR::TXDONE::SET);
        self.enable_tx_interrupt();
        self.registers.xact.modify(XACT::START::SET);
        ReturnCode::SUCCESS
    }

    fn read_data(&self, read_buffer: &mut [u8]) {
        let read_len = min(read_buffer.len(), self.transaction_len.get());
        for idx in 0..read_len {
            let val = self.registers.rx_fifo[idx].get();
            read_buffer[idx] = val;
        }
    }
}

impl SpiHost for SpiHostHardware {
    fn spi_device_spi_host_passthrough(&self, enabled: bool) {
        self.registers.ctrl.modify(
            if enabled { CTRL::ENPASSTHRU::SET } else { CTRL::ENPASSTHRU::CLEAR });
    }

    fn wait_busy_clear_in_transactions(&self, enabled: bool) {
        self.registers.xact.modify(
            if enabled { XACT::RDY_POLL::SET } else { XACT::RDY_POLL::CLEAR });
    }
}

impl SpiMaster for SpiHostHardware {
    type ChipSelect = bool;

    fn set_client(&self, client: &'static dyn kernel::hil::spi::SpiMasterClient) {
        self.client.set(client);
    }

    fn init(&self) {}

    fn is_busy(&self) -> bool {
        self.registers.istate.is_set(ISTATE::TXDONE)
    }

    fn read_write_bytes(
        &self,
        write_buffer: &'static mut [u8],
        read_buffer: Option<&'static mut [u8]>,
        len: usize,
    ) -> ReturnCode {
        // If busy, don't start
        if self.is_busy() {
            return ReturnCode::EBUSY;
        }

        self.start_transaction(Some(write_buffer), read_buffer, len)
    }

    fn write_byte(&self, _val: u8) {
        panic!("write_byte is not implemented");
    }
    fn read_byte(&self) -> u8 {
        panic!("read_byte is not implemented");
    }
    fn read_write_byte(&self, _val: u8) -> u8 {
        panic!("read_write_byte is not implemented");
    }

    fn specify_chip_select(&self, _cs: Self::ChipSelect) {
        // Nothing to be done
    }

    /// Returns the actual rate set
    fn set_rate(&self, _rate: u32) -> u32 {
        panic!("set_rate is not implemented");
    }
    fn get_rate(&self) -> u32 {
        panic!("get_rate is not implemented");
    }
    fn set_clock(&self, _polarity: ClockPolarity) {
        panic!("set_clock is not implemented");
    }
    fn get_clock(&self) -> ClockPolarity {
        panic!("get_clock is not implemented");
    }
    fn set_phase(&self, _phase: ClockPhase) {
        panic!("set_phase is not implemented");
    }
    fn get_phase(&self) -> ClockPhase {
        panic!("get_phase is not implemented");
    }

    fn hold_low(&self) {
        panic!("hold_low is not implemented");
    }
    fn release_low(&self) {
        // Nothing to do, since this is the only mode supported.
    }
}
<|endoftext|>