<commit_before>use std::collections::{HashMap, VecDeque};

use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
use crate::grammar::Rule;
use crate::json::path::*;
use crate::json::value::ScopedJson;
use crate::util::extend;

pub type Object = HashMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

#[derive(Debug)]
enum ResolvedPath<'a> {
    // FIXME: change to borrowed when possible
    // full path
    AbsolutePath(Vec<String>),
    // relative path and path root
    RelativePath(Vec<String>),
    // relative path against block param value
    BlockParamValue(Vec<String>, &'a Json),
}

fn parse_json_visitor<'a, 'reg: 'rc, 'rc>(
    relative_path: &[PathSeg],
    block_contexts: &'a VecDeque<BlockContext<'reg, 'rc>>,
    always_for_absolute_path: bool,
) -> Result<ResolvedPath<'a>, RenderError> {
    let mut path_context_depth: i64 = 0;
    let mut with_block_param = None;
    let mut from_root = false;

    // peek relative_path for block param, @root and  "../../"
    for path_seg in relative_path {
        match path_seg {
            PathSeg::Named(the_path) => {
                if let Some((holder, base_path)) = get_in_block_params(&block_contexts, the_path) {
                    with_block_param = Some((holder, base_path));
                }
                break;
            }
            PathSeg::Ruled(the_rule) => match the_rule {
                Rule::path_root => {
                    from_root = true;
                    break;
                }
                Rule::path_up => path_context_depth += 1,
                _ => break,
            },
        }
    }

    let mut path_stack = Vec::with_capacity(relative_path.len() + 5);
    match with_block_param {
        Some((BlockParamHolder::Value(ref value), _)) => {
            merge_json_path(&mut path_stack, &relative_path[1..]);
            Ok(ResolvedPath::BlockParamValue(path_stack, value))
        }
        Some((BlockParamHolder::Path(ref paths), base_path)) => {
            extend(&mut path_stack, base_path);
            if !paths.is_empty() {
                extend(&mut path_stack, paths);
            }
            merge_json_path(&mut path_stack, &relative_path[1..]);

            Ok(ResolvedPath::AbsolutePath(path_stack))
        }
        None => {
            if path_context_depth > 0 {
                if let Some(ref context_base_path) = block_contexts
                    .get(path_context_depth as usize)
                    .map(|blk| blk.base_path())
                {
                    extend(&mut path_stack, context_base_path);
                } else {
                    // TODO: is this correct behaviour?
                    if let Some(ref base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        extend(&mut path_stack, base_path);
                    }
                }
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else if from_root {
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else if always_for_absolute_path {
                if let Some(base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                    extend(&mut path_stack, base_path);
                }
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else {
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::RelativePath(path_stack))
            }
        }
    }
}

fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> {
    let result = match d {
        Some(&Json::Array(ref l)) => p.parse::<usize>().map(|idx_u| l.get(idx_u))?,
        Some(&Json::Object(ref m)) => m.get(p),
        Some(_) => None,
        None => None,
    };
    Ok(result)
}

fn get_in_block_params<'a, 'reg: 'rc, 'rc>(
    block_contexts: &'a VecDeque<BlockContext<'reg, 'rc>>,
    p: &str,
) -> Option<(&'a BlockParamHolder, &'a Vec<String>)> {
    for bc in block_contexts {
        let v = bc.get_block_param(p);
        if v.is_some() {
            return v.map(|v| (v, bc.base_path()));
        }
    }

    None
}

pub(crate) fn merge_json(base: &Json, addition: &HashMap<&&str, &Json>) -> Json {
    let mut base_map = match base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert((*(*k)).to_string(), (*v).clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with relative path and block scopes
    pub(crate) fn navigate<'reg, 'rc>(
        &'rc self,
        relative_path: &[PathSeg],
        block_contexts: &VecDeque<BlockContext<'reg, 'rc>>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // always use absolute at the moment until we get base_value lifetime issue fixed
        let resolved_visitor = parse_json_visitor(&relative_path, block_contexts, true)?;

        match resolved_visitor {
            ResolvedPath::AbsolutePath(paths) => {
                let mut ptr = Some(self.data());
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }

                Ok(ptr
                    .map(|v| ScopedJson::Context(v, paths))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::RelativePath(_paths) => {
                // relative path is disabled for now
                unreachable!()
                // let mut ptr = block_contexts.front().and_then(|blk| blk.base_value());
                // for p in paths.iter() {
                //     ptr = get_data(ptr, p)?;
                // }

                // Ok(ptr
                //     .map(|v| ScopedJson::Context(v, paths))
                //     .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::BlockParamValue(paths, value) => {
                let mut ptr = Some(value);
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }
                Ok(ptr
                    .map(|v| ScopedJson::Derived(v.clone()))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
        }
    }

    /// Return the Json data wrapped in context
    pub fn data(&self) -> &Json {
        &self.data
    }

    /// Return the mutable reference to Json data wrapped in context
    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

impl Serialize for Context {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ::serde::Serializer,
    {
        self.data.serialize(serializer)
    }
}

#[cfg(test)]
mod test {
    use crate::block::{BlockContext, BlockParams};
    use crate::context::{self, Context};
    use crate::error::RenderError;
    use crate::json::path::Path;
    use crate::json::value::{self, ScopedJson};
    use serde_json::value::Map;
    use std::collections::{HashMap, VecDeque};

    fn navigate_from_root<'reg, 'rc>(
        ctx: &'rc Context,
        path: &str,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        let relative_path = Path::parse(path).unwrap();
        ctx.navigate(relative_path.segs().unwrap(), &VecDeque::new())
    }

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this").unwrap().render(),
            v.to_string()
        );
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr,
            titles: vec!["programmer".to_string(), "cartographer".to_string()],
        };

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "./addr/country").unwrap().render(),
            "China".to_string()
        );
        assert_eq!(
            navigate_from_root(&ctx, "addr.[country]").unwrap().render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            navigate_from_root(&ctx2, "this").unwrap().render(),
            "true".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "titles.[0]").unwrap().render(),
            "programmer".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "age").unwrap().render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            navigate_from_root(&ctx1, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx2, "age").unwrap().render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let mut hash = HashMap::new();
        let v = value::to_json("h1");
        hash.insert(&"tag", &v);

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a1, "age").unwrap().render(),
            "4".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a1, "tag").unwrap().render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a2, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a2, "tag").unwrap().render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this_name").unwrap().render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        let mut block = BlockContext::new();
        *block.base_path_mut() = ["a".to_owned(), "b".to_owned()].to_vec();

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("@root/b").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
    }

    #[test]
    fn test_block_params() {
        let m = json!([{
            "a": [1, 2]
        }, {
            "b": [2, 3]
        }]);

        let ctx = Context::wraps(&m).unwrap();
        let mut block_params = BlockParams::new();
        block_params
            .add_path("z", ["0".to_owned(), "a".to_owned()].to_vec())
            .unwrap();
        block_params.add_value("t", json!("good")).unwrap();

        let mut block = BlockContext::new();
        block.set_block_params(block_params);

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("z.[1]").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
        assert_eq!(
            ctx.navigate(&Path::parse("t").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "good".to_string()
        );
    }
}
<commit_msg>Revert "(feat) Impl serialize for context"<commit_after>use std::collections::{HashMap, VecDeque};

use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
use crate::grammar::Rule;
use crate::json::path::*;
use crate::json::value::ScopedJson;
use crate::util::extend;

pub type Object = HashMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

#[derive(Debug)]
enum ResolvedPath<'a> {
    // FIXME: change to borrowed when possible
    // full path
    AbsolutePath(Vec<String>),
    // relative path and path root
    RelativePath(Vec<String>),
    // relative path against block param value
    BlockParamValue(Vec<String>, &'a Json),
}

fn parse_json_visitor<'a, 'reg: 'rc, 'rc>(
    relative_path: &[PathSeg],
    block_contexts: &'a VecDeque<BlockContext<'reg, 'rc>>,
    always_for_absolute_path: bool,
) -> Result<ResolvedPath<'a>, RenderError> {
    let mut path_context_depth: i64 = 0;
    let mut with_block_param = None;
    let mut from_root = false;

    // peek relative_path for block param, @root and  "../../"
    for path_seg in relative_path {
        match path_seg {
            PathSeg::Named(the_path) => {
                if let Some((holder, base_path)) = get_in_block_params(&block_contexts, the_path) {
                    with_block_param = Some((holder, base_path));
                }
                break;
            }
            PathSeg::Ruled(the_rule) => match the_rule {
                Rule::path_root => {
                    from_root = true;
                    break;
                }
                Rule::path_up => path_context_depth += 1,
                _ => break,
            },
        }
    }

    let mut path_stack = Vec::with_capacity(relative_path.len() + 5);
    match with_block_param {
        Some((BlockParamHolder::Value(ref value), _)) => {
            merge_json_path(&mut path_stack, &relative_path[1..]);
            Ok(ResolvedPath::BlockParamValue(path_stack, value))
        }
        Some((BlockParamHolder::Path(ref paths), base_path)) => {
            extend(&mut path_stack, base_path);
            if !paths.is_empty() {
                extend(&mut path_stack, paths);
            }
            merge_json_path(&mut path_stack, &relative_path[1..]);

            Ok(ResolvedPath::AbsolutePath(path_stack))
        }
        None => {
            if path_context_depth > 0 {
                if let Some(ref context_base_path) = block_contexts
                    .get(path_context_depth as usize)
                    .map(|blk| blk.base_path())
                {
                    extend(&mut path_stack, context_base_path);
                } else {
                    // TODO: is this correct behaviour?
                    if let Some(ref base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        extend(&mut path_stack, base_path);
                    }
                }
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else if from_root {
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else if always_for_absolute_path {
                if let Some(base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                    extend(&mut path_stack, base_path);
                }
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::AbsolutePath(path_stack))
            } else {
                merge_json_path(&mut path_stack, relative_path);
                Ok(ResolvedPath::RelativePath(path_stack))
            }
        }
    }
}

fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> {
    let result = match d {
        Some(&Json::Array(ref l)) => p.parse::<usize>().map(|idx_u| l.get(idx_u))?,
        Some(&Json::Object(ref m)) => m.get(p),
        Some(_) => None,
        None => None,
    };
    Ok(result)
}

fn get_in_block_params<'a, 'reg: 'rc, 'rc>(
    block_contexts: &'a VecDeque<BlockContext<'reg, 'rc>>,
    p: &str,
) -> Option<(&'a BlockParamHolder, &'a Vec<String>)> {
    for bc in block_contexts {
        let v = bc.get_block_param(p);
        if v.is_some() {
            return v.map(|v| (v, bc.base_path()));
        }
    }

    None
}

pub(crate) fn merge_json(base: &Json, addition: &HashMap<&&str, &Json>) -> Json {
    let mut base_map = match base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert((*(*k)).to_string(), (*v).clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with relative path and block scopes
    pub(crate) fn navigate<'reg, 'rc>(
        &'rc self,
        relative_path: &[PathSeg],
        block_contexts: &VecDeque<BlockContext<'reg, 'rc>>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // always use absolute at the moment until we get base_value lifetime issue fixed
        let resolved_visitor = parse_json_visitor(&relative_path, block_contexts, true)?;

        match resolved_visitor {
            ResolvedPath::AbsolutePath(paths) => {
                let mut ptr = Some(self.data());
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }

                Ok(ptr
                    .map(|v| ScopedJson::Context(v, paths))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::RelativePath(_paths) => {
                // relative path is disabled for now
                unreachable!()
                // let mut ptr = block_contexts.front().and_then(|blk| blk.base_value());
                // for p in paths.iter() {
                //     ptr = get_data(ptr, p)?;
                // }

                // Ok(ptr
                //     .map(|v| ScopedJson::Context(v, paths))
                //     .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::BlockParamValue(paths, value) => {
                let mut ptr = Some(value);
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }
                Ok(ptr
                    .map(|v| ScopedJson::Derived(v.clone()))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
        }
    }

    /// Return the Json data wrapped in context
    pub fn data(&self) -> &Json {
        &self.data
    }

    /// Return the mutable reference to Json data wrapped in context
    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

#[cfg(test)]
mod test {
    use crate::block::{BlockContext, BlockParams};
    use crate::context::{self, Context};
    use crate::error::RenderError;
    use crate::json::path::Path;
    use crate::json::value::{self, ScopedJson};
    use serde_json::value::Map;
    use std::collections::{HashMap, VecDeque};

    fn navigate_from_root<'reg, 'rc>(
        ctx: &'rc Context,
        path: &str,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        let relative_path = Path::parse(path).unwrap();
        ctx.navigate(relative_path.segs().unwrap(), &VecDeque::new())
    }

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this").unwrap().render(),
            v.to_string()
        );
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr,
            titles: vec!["programmer".to_string(), "cartographer".to_string()],
        };

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "./addr/country").unwrap().render(),
            "China".to_string()
        );
        assert_eq!(
            navigate_from_root(&ctx, "addr.[country]").unwrap().render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            navigate_from_root(&ctx2, "this").unwrap().render(),
            "true".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "titles.[0]").unwrap().render(),
            "programmer".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "age").unwrap().render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            navigate_from_root(&ctx1, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx2, "age").unwrap().render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let mut hash = HashMap::new();
        let v = value::to_json("h1");
        hash.insert(&"tag", &v);

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a1, "age").unwrap().render(),
            "4".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a1, "tag").unwrap().render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a2, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a2, "tag").unwrap().render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this_name").unwrap().render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        let mut block = BlockContext::new();
        *block.base_path_mut() = ["a".to_owned(), "b".to_owned()].to_vec();

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("@root/b").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
    }

    #[test]
    fn test_block_params() {
        let m = json!([{
            "a": [1, 2]
        }, {
            "b": [2, 3]
        }]);

        let ctx = Context::wraps(&m).unwrap();
        let mut block_params = BlockParams::new();
        block_params
            .add_path("z", ["0".to_owned(), "a".to_owned()].to_vec())
            .unwrap();
        block_params.add_value("t", json!("good")).unwrap();

        let mut block = BlockContext::new();
        block.set_block_params(block_params);

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("z.[1]").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
        assert_eq!(
            ctx.navigate(&Path::parse("t").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "good".to_string()
        );
    }
}
<|endoftext|>


<commit_before>//! Convert sdf files to other formats.

use std::iter::repeat;
use std::path::Path;

use peakbag::{PeakDetector, Peak};
use sdc;

use error::SdfError;
use file::{Block, Channel, File, FileInfo, Record};
use result::Result;

const HIGH_WIDTH: usize = 2;
const HIGH_FLOOR: u16 = 15;
const HIGH_CEILING: u16 = 255;
const LOW_WIDTHS: (usize, usize) = (3, 2);
const LOW_FLOOR: u16 = 15;
const LOW_CEILINGS: (u16, u16) = (250, 255); // heh
const MIN_HEIGHT_ABOVE_BACKGROUND: f64 = 5.0;
const MAX_KURTOSIS: f64 = 0.04;

/// Turns a single sdf record into zero or more `Point`s.
///
/// At this point, we assume that the timestamps are absolute. TODO make this smarter to handle the
/// case when the user has called `File::set_sosbl_mode(SosblMode::Relative)`.
///
/// # Panics
///
/// Panics if there are zero or more than one reference blocks in the record.
///
/// # Examples
///
/// ```
/// use sdf::convert::discretize;
/// use sdf::file::File;
/// let mut file = File::open("data/110630_174316.sdf").unwrap();
/// let ref file_info = file.info().unwrap();
/// file.reindex().unwrap();
/// let ref record = file.read().unwrap();
/// let points = discretize(record, file_info).unwrap();
/// ```
pub fn discretize(record: &Record, file_info: &FileInfo) -> Result<Vec<Point>> {
    let mut high_blocks = Vec::new();
    let mut low_blocks = Vec::new();
    let mut reference_block = None;
    for block in &record.blocks {
        match block.channel {
            Channel::High => high_blocks.push(block),
            Channel::Low => low_blocks.push(block),
            Channel::Reference => {
                if reference_block.is_some() {
                    panic!("More than one reference block in this record");
                } else {
                    reference_block = Some(block);
                }
            }
            _ => {}
        }
    }
    let reference_block = reference_block.unwrap();
    let high_detector = PeakDetector::new(HIGH_WIDTH, HIGH_FLOOR, HIGH_CEILING)
                            .min_height_above_background(MIN_HEIGHT_ABOVE_BACKGROUND)
                            .max_kurtosis(MAX_KURTOSIS);
    let (low_width, low_ceiling) = match high_blocks.len() {
        0 => (LOW_WIDTHS.0, LOW_CEILINGS.0),
        _ => (LOW_WIDTHS.1, LOW_CEILINGS.1),
    };
    let low_detector = PeakDetector::new(low_width, LOW_FLOOR, low_ceiling)
                           .min_height_above_background(MIN_HEIGHT_ABOVE_BACKGROUND)
                           .max_kurtosis(MAX_KURTOSIS)
                           .saturation(LOW_CEILINGS.1);
    let reference_detector = high_detector;

    let reference_peaks = reference_detector.detect_peaks(&reference_block.samples[..]);
    if reference_peaks.len() != 1 {
        debug!("Could not get a single reference peak out of: {:?}",
               reference_block.samples);
        return Err(SdfError::NeedSingleReferencePeak(reference_peaks.len()));
    }
    let ref reference_peak = reference_peaks[0];

    let timestamp = |peak: &Peak<_>, block: &Block| {
        block.time_sosbl + peak.index as f64 * file_info.sampling_time
    };

    let t_ref = timestamp(reference_peak, &reference_block);

    let mut points = Vec::new();
    for (block, detector) in low_blocks.iter()
                                       .zip(repeat(low_detector))
                                       .chain(high_blocks.iter().zip(repeat(high_detector))) {
        let peaks = detector.detect_peaks(&block.samples[..]);
        let num_target = peaks.len();
        for (i, peak) in peaks.into_iter().enumerate() {
            let time = timestamp(&peak, block);
            let range = file_info.v_group / 2.0 * (time - t_ref);
            // x is straight out of the scanner, and the mirror pans it along the
            // z axis.
            let theta = (record.direction[2] / record.direction[0])
                            .atan()
                            .to_degrees();
            let point = Point {
                time: time,
                range: range as f32,
                theta: theta as f32,
                x: (record.origin[0] + record.direction[0] * range) as f32,
                y: (record.origin[1] + record.direction[1] * range) as f32,
                z: (record.origin[2] + record.direction[2] * range) as f32,
                target: (i + 1) as u8,
                num_target: num_target as u8,
                facet: record.facet,
                peak: peak,
                high_channel: block.channel == Channel::High,
            };
            points.push(point);
        }
    }

    Ok(points)
}

/// A 3D point in the scanner's own coordiante frame.
#[derive(Clone, Copy, Debug)]
pub struct Point {
    time: f64,
    range: f32,
    theta: f32,
    x: f32,
    y: f32,
    z: f32,
    target: u8,
    num_target: u8,
    facet: u16,
    peak: Peak<u16>,
    high_channel: bool,
}

impl File {
    /// Writes all points from an .sdf file to an .sdc file.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use sdf::file::File;
    /// File::convert("110630_174316.sdf", "110630_174316.sdc").unwrap();
    /// ```
    pub fn convert<T: Into<Vec<u8>>, P: AsRef<Path>>(sdf_path: T, sdc_path: P) -> Result<()> {
        let mut sdf_file = try!(File::open(sdf_path));
        let ref file_info = try!(sdf_file.info());
        let mut sdc_file = try!(sdc::Writer::from_path(sdc_path));
        for (i, record) in sdf_file.into_iter().enumerate() {
            let points = match discretize(&record, file_info) {
                Ok(points) => points,
                Err(SdfError::NeedSingleReferencePeak(_)) => {
                    warn!("No reference peak detected for pulse {}, skipping", i);
                    continue;
                }
                Err(err) => return Err(err),
            };
            for p in points {
                let ref point = sdc::Point {
                    time: p.time,
                    range: p.range,
                    theta: p.theta,
                    x: p.x,
                    y: p.y,
                    z: p.z,
                    amplitude: p.peak.amplitude,
                    // FIXME actually calculate this value
                    width: 1,
                    target_type: sdc::TargetType::Peak,
                    // Riegl one-indexes its target counts. Bah.
                    target: p.target,
                    num_target: p.num_target,
                    // FIXME I have no idea what this means
                    rg_index: 1,
                    facet_number: p.facet as u8,
                    high_channel: p.high_channel,
                    class_id: None,
                    rho: None,
                    reflectance: None,
                };
                try!(sdc_file.write_point(point));
            }
        }
        Ok(())
    }
}

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

    use file::File;

    #[test]
    fn first_point() {
        let mut file = File::open("data/110630_174316.sdf").unwrap();
        file.reindex().unwrap();
        let ref file_info = file.info().unwrap();
        let ref record = file.read().unwrap();
        let points = discretize(record, file_info).unwrap();
        assert_eq!(4, points.len());
    }
}
<commit_msg>Make the point fields public<commit_after>//! Convert sdf files to other formats.

use std::iter::repeat;
use std::path::Path;

use peakbag::{PeakDetector, Peak};
use sdc;

use error::SdfError;
use file::{Block, Channel, File, FileInfo, Record};
use result::Result;

const HIGH_WIDTH: usize = 2;
const HIGH_FLOOR: u16 = 15;
const HIGH_CEILING: u16 = 255;
const LOW_WIDTHS: (usize, usize) = (3, 2);
const LOW_FLOOR: u16 = 15;
const LOW_CEILINGS: (u16, u16) = (250, 255); // heh
const MIN_HEIGHT_ABOVE_BACKGROUND: f64 = 5.0;
const MAX_KURTOSIS: f64 = 0.04;

/// Turns a single sdf record into zero or more `Point`s.
///
/// At this point, we assume that the timestamps are absolute. TODO make this smarter to handle the
/// case when the user has called `File::set_sosbl_mode(SosblMode::Relative)`.
///
/// # Panics
///
/// Panics if there are zero or more than one reference blocks in the record.
///
/// # Examples
///
/// ```
/// use sdf::convert::discretize;
/// use sdf::file::File;
/// let mut file = File::open("data/110630_174316.sdf").unwrap();
/// let ref file_info = file.info().unwrap();
/// file.reindex().unwrap();
/// let ref record = file.read().unwrap();
/// let points = discretize(record, file_info).unwrap();
/// ```
pub fn discretize(record: &Record, file_info: &FileInfo) -> Result<Vec<Point>> {
    let mut high_blocks = Vec::new();
    let mut low_blocks = Vec::new();
    let mut reference_block = None;
    for block in &record.blocks {
        match block.channel {
            Channel::High => high_blocks.push(block),
            Channel::Low => low_blocks.push(block),
            Channel::Reference => {
                if reference_block.is_some() {
                    panic!("More than one reference block in this record");
                } else {
                    reference_block = Some(block);
                }
            }
            _ => {}
        }
    }
    let reference_block = reference_block.unwrap();
    let high_detector = PeakDetector::new(HIGH_WIDTH, HIGH_FLOOR, HIGH_CEILING)
                            .min_height_above_background(MIN_HEIGHT_ABOVE_BACKGROUND)
                            .max_kurtosis(MAX_KURTOSIS);
    let (low_width, low_ceiling) = match high_blocks.len() {
        0 => (LOW_WIDTHS.0, LOW_CEILINGS.0),
        _ => (LOW_WIDTHS.1, LOW_CEILINGS.1),
    };
    let low_detector = PeakDetector::new(low_width, LOW_FLOOR, low_ceiling)
                           .min_height_above_background(MIN_HEIGHT_ABOVE_BACKGROUND)
                           .max_kurtosis(MAX_KURTOSIS)
                           .saturation(LOW_CEILINGS.1);
    let reference_detector = high_detector;

    let reference_peaks = reference_detector.detect_peaks(&reference_block.samples[..]);
    if reference_peaks.len() != 1 {
        debug!("Could not get a single reference peak out of: {:?}",
               reference_block.samples);
        return Err(SdfError::NeedSingleReferencePeak(reference_peaks.len()));
    }
    let ref reference_peak = reference_peaks[0];

    let timestamp = |peak: &Peak<_>, block: &Block| {
        block.time_sosbl + peak.index as f64 * file_info.sampling_time
    };

    let t_ref = timestamp(reference_peak, &reference_block);

    let mut points = Vec::new();
    for (block, detector) in low_blocks.iter()
                                       .zip(repeat(low_detector))
                                       .chain(high_blocks.iter().zip(repeat(high_detector))) {
        let peaks = detector.detect_peaks(&block.samples[..]);
        let num_target = peaks.len();
        for (i, peak) in peaks.into_iter().enumerate() {
            let time = timestamp(&peak, block);
            let range = file_info.v_group / 2.0 * (time - t_ref);
            // x is straight out of the scanner, and the mirror pans it along the
            // z axis.
            let theta = (record.direction[2] / record.direction[0])
                            .atan()
                            .to_degrees();
            let point = Point {
                time: time,
                range: range as f32,
                theta: theta as f32,
                x: (record.origin[0] + record.direction[0] * range) as f32,
                y: (record.origin[1] + record.direction[1] * range) as f32,
                z: (record.origin[2] + record.direction[2] * range) as f32,
                target: (i + 1) as u8,
                num_target: num_target as u8,
                facet: record.facet,
                peak: peak,
                high_channel: block.channel == Channel::High,
            };
            points.push(point);
        }
    }

    Ok(points)
}

/// A 3D point in the scanner's own coordiante frame.
#[derive(Clone, Copy, Debug)]
pub struct Point {
    /// The time that this point was collected. Its reference frame depends on the time settings
    /// provided to the sdf library, which at this point are poorly defined.
    pub time: f64,
    /// The raw range to the point, which is different than the cartesian xyz distance.
    pub range: f32,
    /// The mirror scan angle in degrees.
    pub theta: f32,
    /// The x coordinate of the point in the scanner's own coordinate system.
    pub x: f32,
    /// The y coordinate of the point in the scanner's own coordinate system.
    pub y: f32,
    /// The z coordinate of the point in the scanner's own coordinate system.
    pub z: f32,
    /// The target number (1-indexed).
    pub target: u8,
    /// The total number of targets in this pulse.
    pub num_target: u8,
    /// The mirror facet used to reflect the laser energy.
    pub facet: u16,
    /// The raw peak information returned from `peakbag`.
    pub peak: Peak<u16>,
    /// Was this point collected on the high channel?
    pub high_channel: bool,
}

impl File {
    /// Writes all points from an .sdf file to an .sdc file.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use sdf::file::File;
    /// File::convert("110630_174316.sdf", "110630_174316.sdc").unwrap();
    /// ```
    pub fn convert<T: Into<Vec<u8>>, P: AsRef<Path>>(sdf_path: T, sdc_path: P) -> Result<()> {
        let mut sdf_file = try!(File::open(sdf_path));
        let ref file_info = try!(sdf_file.info());
        let mut sdc_file = try!(sdc::Writer::from_path(sdc_path));
        for (i, record) in sdf_file.into_iter().enumerate() {
            let points = match discretize(&record, file_info) {
                Ok(points) => points,
                Err(SdfError::NeedSingleReferencePeak(_)) => {
                    warn!("No reference peak detected for pulse {}, skipping", i);
                    continue;
                }
                Err(err) => return Err(err),
            };
            for p in points {
                let ref point = sdc::Point {
                    time: p.time,
                    range: p.range,
                    theta: p.theta,
                    x: p.x,
                    y: p.y,
                    z: p.z,
                    amplitude: p.peak.amplitude,
                    // FIXME actually calculate this value
                    width: 1,
                    target_type: sdc::TargetType::Peak,
                    // Riegl one-indexes its target counts. Bah.
                    target: p.target,
                    num_target: p.num_target,
                    // FIXME I have no idea what this means
                    rg_index: 1,
                    facet_number: p.facet as u8,
                    high_channel: p.high_channel,
                    class_id: None,
                    rho: None,
                    reflectance: None,
                };
                try!(sdc_file.write_point(point));
            }
        }
        Ok(())
    }
}

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

    use file::File;

    #[test]
    fn first_point() {
        let mut file = File::open("data/110630_174316.sdf").unwrap();
        file.reindex().unwrap();
        let ref file_info = file.info().unwrap();
        let ref record = file.read().unwrap();
        let points = discretize(record, file_info).unwrap();
        assert_eq!(4, points.len());
    }
}
<|endoftext|>


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

use libc::{c_char, c_int, int32_t, uint32_t, size_t};

pub enum ShadercCompiler {}
pub enum ShadercCompileOptions {}
pub enum ShadercCompilationResult {}

#[link(name = "shaderc_combined")]
#[cfg_attr(target_os = "linux", link(name = "stdc++"))]
#[cfg_attr(target_os = "macos", link(name = "c++"))]
#[cfg_attr(target_os = "windows", link(name = "msvcrt"))]
extern "C" {
    pub fn shaderc_compiler_initialize() -> *mut ShadercCompiler;
    pub fn shaderc_compiler_release(compiler: *mut ShadercCompiler);

    pub fn shaderc_compile_into_spv(compiler: *const ShadercCompiler,
                                    source_text: *const c_char,
                                    source_size: size_t,
                                    shader_kind: int32_t,
                                    input_file_name: *const c_char,
                                    entry_point_name: *const c_char,
                                    additional_options: *const ShadercCompileOptions)
                                    -> *mut ShadercCompilationResult;
    pub fn shaderc_compile_into_spv_assembly(compiler: *const ShadercCompiler,
                                             source_text: *const c_char,
                                             source_size: size_t,
                                             shader_kind: int32_t,
                                             input_file_name: *const c_char,
                                             entry_point_name: *const c_char,
                                             additional_options: *const ShadercCompileOptions)
                                             -> *mut ShadercCompilationResult;
    pub fn shaderc_compile_into_preprocessed_text(compiler: *const ShadercCompiler,
                                                  source_text: *const c_char,
                                                  source_size: size_t,
                                                  shader_kind: int32_t,
                                                  input_file_name: *const c_char,
                                                  entry_point_name: *const c_char,
                                                  additional_options: *const ShadercCompileOptions)
                                                  -> *mut ShadercCompilationResult;
    pub fn shaderc_assemble_into_spv(compiler: *const ShadercCompiler,
                                     source_assembly: *const c_char,
                                     source_size: size_t,
                                     additional_options: *const ShadercCompileOptions)
                                     -> *mut ShadercCompilationResult;

    pub fn shaderc_compile_options_initialize() -> *mut ShadercCompileOptions;
    pub fn shaderc_compile_options_clone(options: *const ShadercCompileOptions)
                                         -> *mut ShadercCompileOptions;
    pub fn shaderc_compile_options_release(options: *mut ShadercCompileOptions);

    pub fn shaderc_compile_options_add_macro_definition(options: *mut ShadercCompileOptions,
                                                        name: *const c_char,
                                                        name_length: size_t,
                                                        value: *const c_char,
                                                        vaule_length: size_t);
    pub fn shaderc_compile_options_set_source_language(options: *mut ShadercCompileOptions,
                                                       language: int32_t);
    pub fn shaderc_compile_options_set_generate_debug_info(options: *mut ShadercCompileOptions);
    pub fn shaderc_compile_options_set_optimization_level(options: *mut ShadercCompileOptions,
                                                          level: int32_t);
    pub fn shaderc_compile_options_set_forced_version_profile(options: *mut ShadercCompileOptions,
                                                              version: c_int,
                                                              profile: int32_t);
    pub fn shaderc_compile_options_set_suppress_warnings(options: *mut ShadercCompileOptions);
    pub fn shaderc_compile_options_set_warnings_as_errors(options: *mut ShadercCompileOptions);
    pub fn shaderc_compile_options_set_target_env(options: *mut ShadercCompileOptions,
                                                  env: int32_t,
                                                  version: uint32_t);
    pub fn shaderc_compile_options_set_limit(options: *mut ShadercCompileOptions,
                                             limit: int32_t,
                                             value: c_int);

    pub fn shaderc_result_release(result: *mut ShadercCompilationResult);
    pub fn shaderc_result_get_compilation_status(result: *const ShadercCompilationResult) -> int32_t;
    pub fn shaderc_result_get_num_errors(result: *const ShadercCompilationResult) -> size_t;
    pub fn shaderc_result_get_num_warnings(result: *const ShadercCompilationResult) -> size_t;
    pub fn shaderc_result_get_error_message(result: *const ShadercCompilationResult) -> *const c_char;
    pub fn shaderc_result_get_length(result: *const ShadercCompilationResult) -> size_t;
    pub fn shaderc_result_get_bytes(result: *const ShadercCompilationResult) -> *const c_char;

    pub fn shaderc_get_spv_version(version: *mut c_int, revision: *mut c_int);
    pub fn shaderc_parse_version_profile(str: *const c_char,
                                         version: *mut c_int,
                                         profile: *mut int32_t)
                                         -> bool;
}
<commit_msg>Remove link attributes on external C functions for Windows.<commit_after>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use libc::{c_char, c_int, int32_t, uint32_t, size_t};

pub enum ShadercCompiler {}
pub enum ShadercCompileOptions {}
pub enum ShadercCompilationResult {}

#[cfg_attr(target_os = "linux", link(name = "stdc++"))]
#[cfg_attr(target_os = "macos", link(name = "c++"))]
extern "C" {
    pub fn shaderc_compiler_initialize() -> *mut ShadercCompiler;
    pub fn shaderc_compiler_release(compiler: *mut ShadercCompiler);

    pub fn shaderc_compile_into_spv(compiler: *const ShadercCompiler,
                                    source_text: *const c_char,
                                    source_size: size_t,
                                    shader_kind: int32_t,
                                    input_file_name: *const c_char,
                                    entry_point_name: *const c_char,
                                    additional_options: *const ShadercCompileOptions)
                                    -> *mut ShadercCompilationResult;
    pub fn shaderc_compile_into_spv_assembly(compiler: *const ShadercCompiler,
                                             source_text: *const c_char,
                                             source_size: size_t,
                                             shader_kind: int32_t,
                                             input_file_name: *const c_char,
                                             entry_point_name: *const c_char,
                                             additional_options: *const ShadercCompileOptions)
                                             -> *mut ShadercCompilationResult;
    pub fn shaderc_compile_into_preprocessed_text(compiler: *const ShadercCompiler,
                                                  source_text: *const c_char,
                                                  source_size: size_t,
                                                  shader_kind: int32_t,
                                                  input_file_name: *const c_char,
                                                  entry_point_name: *const c_char,
                                                  additional_options: *const ShadercCompileOptions)
                                                  -> *mut ShadercCompilationResult;
    pub fn shaderc_assemble_into_spv(compiler: *const ShadercCompiler,
                                     source_assembly: *const c_char,
                                     source_size: size_t,
                                     additional_options: *const ShadercCompileOptions)
                                     -> *mut ShadercCompilationResult;

    pub fn shaderc_compile_options_initialize() -> *mut ShadercCompileOptions;
    pub fn shaderc_compile_options_clone(options: *const ShadercCompileOptions)
                                         -> *mut ShadercCompileOptions;
    pub fn shaderc_compile_options_release(options: *mut ShadercCompileOptions);

    pub fn shaderc_compile_options_add_macro_definition(options: *mut ShadercCompileOptions,
                                                        name: *const c_char,
                                                        name_length: size_t,
                                                        value: *const c_char,
                                                        vaule_length: size_t);
    pub fn shaderc_compile_options_set_source_language(options: *mut ShadercCompileOptions,
                                                       language: int32_t);
    pub fn shaderc_compile_options_set_generate_debug_info(options: *mut ShadercCompileOptions);
    pub fn shaderc_compile_options_set_optimization_level(options: *mut ShadercCompileOptions,
                                                          level: int32_t);
    pub fn shaderc_compile_options_set_forced_version_profile(options: *mut ShadercCompileOptions,
                                                              version: c_int,
                                                              profile: int32_t);
    pub fn shaderc_compile_options_set_suppress_warnings(options: *mut ShadercCompileOptions);
    pub fn shaderc_compile_options_set_warnings_as_errors(options: *mut ShadercCompileOptions);
    pub fn shaderc_compile_options_set_target_env(options: *mut ShadercCompileOptions,
                                                  env: int32_t,
                                                  version: uint32_t);
    pub fn shaderc_compile_options_set_limit(options: *mut ShadercCompileOptions,
                                             limit: int32_t,
                                             value: c_int);

    pub fn shaderc_result_release(result: *mut ShadercCompilationResult);
    pub fn shaderc_result_get_compilation_status(result: *const ShadercCompilationResult) -> int32_t;
    pub fn shaderc_result_get_num_errors(result: *const ShadercCompilationResult) -> size_t;
    pub fn shaderc_result_get_num_warnings(result: *const ShadercCompilationResult) -> size_t;
    pub fn shaderc_result_get_error_message(result: *const ShadercCompilationResult) -> *const c_char;
    pub fn shaderc_result_get_length(result: *const ShadercCompilationResult) -> size_t;
    pub fn shaderc_result_get_bytes(result: *const ShadercCompilationResult) -> *const c_char;

    pub fn shaderc_get_spv_version(version: *mut c_int, revision: *mut c_int);
    pub fn shaderc_parse_version_profile(str: *const c_char,
                                         version: *mut c_int,
                                         profile: *mut int32_t)
                                         -> bool;
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use std::iter::FromIterator;
use std::error::Error;
use std::convert::From;
use std::num::ParseIntError;
use std::fmt::{self, Display, Formatter};

use schedule::{Schedule, Period, Calendar, ScheduleParseError, PeriodParseError};

pub trait ToCrontabEntry : FromStr<Err=CrontabEntryParseError> {
    fn to_crontab_entry(self) -> CrontabEntry;
}

#[derive(Debug, PartialEq)]
pub enum CrontabEntry {
    EnvVar(EnvVarEntry),
    User(UserCrontabEntry),
    System(SystemCrontabEntry),
    Anacron(AnacrontabEntry)
}

#[derive(Debug, PartialEq)]
pub struct EnvVarEntry(pub String, pub String);

#[derive(Debug, PartialEq)]
pub struct UserCrontabEntry {
    pub sched: Schedule,
    pub cmd: String
}

#[derive(Debug, PartialEq)]
pub struct SystemCrontabEntry {
    pub sched: Schedule,
    pub user: UserInfo,
    pub cmd: String
}

#[derive(Debug, PartialEq)]
pub struct AnacrontabEntry {
    pub period: Period,
    pub delay: u32,
    pub jobid: String,
    pub cmd: String
}

impl ToCrontabEntry for UserCrontabEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::User(self)
    }
}

impl ToCrontabEntry for SystemCrontabEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::System(self)
    }
}

impl ToCrontabEntry for AnacrontabEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::Anacron(self)
    }
}

impl ToCrontabEntry for EnvVarEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::EnvVar(self)
    }
}

impl CrontabEntry {
    pub fn period<'a>(&'a self) -> Option<&'a Period> {
        match *self {
            CrontabEntry::Anacron(AnacrontabEntry { ref period, .. }) => Some(period),
            CrontabEntry::User(UserCrontabEntry { sched: Schedule::Period(ref period), .. }) => Some(period),
            CrontabEntry::System(SystemCrontabEntry { sched: Schedule::Period(ref period), .. }) => Some(period),
            _ => None
        }
    }

    pub fn calendar<'a>(&'a self) -> Option<&'a Calendar> {
        match *self {
            CrontabEntry::User(UserCrontabEntry { sched: Schedule::Calendar(ref cal), .. }) => Some(cal),
            CrontabEntry::System(SystemCrontabEntry { sched: Schedule::Calendar(ref cal), .. }) => Some(cal),
            _ => None
        }
    }

    pub fn command<'a>(&'a self) -> Option<&'a str> {
        match *self {
            CrontabEntry::User(UserCrontabEntry { ref cmd, .. }) => Some(&**cmd),
            CrontabEntry::System(SystemCrontabEntry { ref cmd, .. }) => Some(&**cmd),
            CrontabEntry::Anacron(AnacrontabEntry { ref cmd, .. }) => Some(&**cmd),
            _ => None
        }
    }
}

impl FromStr for EnvVarEntry {
    type Err = CrontabEntryParseError;
    fn from_str(s: &str) -> Result<EnvVarEntry, CrontabEntryParseError> {
        let spaces = [' ', '\t'];
        let mut splits = s.splitn(2, '=');

        let name = match splits.next() {
            Some(n) => n.trim_right_matches(&spaces[..]),
            None => return Err(CrontabEntryParseError::MissingEnvVarName)
        };

        if name.len() == 0 {
            return Err(CrontabEntryParseError::MissingEnvVarName)
        }

        if name.chars().any(|v| v == ' ' || v == '\t') {
            return Err(CrontabEntryParseError::InvalidEnvVarName)
        }

        let mut value = match splits.next() {
            Some(v) => v.trim_left_matches(&spaces[..]),
            None => return Err(CrontabEntryParseError::MissingEnvVarValue)
        };

        if value.len() > 1 {
            if &value[..1] == "'" || &value[..1] == "\"" && &value[..1] == &value[value.len()-1..] {
                value = &value[1..value.len()-1];
            }
        }

        Ok(EnvVarEntry(name.to_string(), value.to_string()))
    }
}

#[derive(Debug, PartialEq)]
// user, group, class
pub struct UserInfo(pub String, pub Option<String>, pub Option<String>);

#[derive(Debug, PartialEq)]
pub struct UserInfoParseError;
impl FromStr for UserInfo {
    type Err = UserInfoParseError;
    fn from_str(s: &str) -> Result<UserInfo, UserInfoParseError> {
        let mut splits = s.split(':');
        Ok(UserInfo(
            try!(splits.next().ok_or(UserInfoParseError).map(ToString::to_string)),
            splits.next().map(ToString::to_string),
            splits.next().map(ToString::to_string)
        ))
    }
}

impl Error for UserInfoParseError {
    fn description(&self) -> &str {
        "invalid user name"
    }
    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl Display for UserInfoParseError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("invalid user name")
    }
}

#[derive(Debug, PartialEq)]
pub enum CrontabEntryParseError {
    InvalidSchedule(ScheduleParseError),
    InvalidPeriod(PeriodParseError),
    InvalidUser(UserInfoParseError),
    InvalidDelay(ParseIntError),
    InvalidEnvVarName,
    MissingPeriod,
    MissingDelay,
    MissingJobId,
    MissingEnvVarName,
    MissingEnvVarValue,
}

impl Display for CrontabEntryParseError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::CrontabEntryParseError::*;
        match *self {
            InvalidSchedule(ref e) => write!(f, "invalid schedule: {}", e),
            InvalidPeriod(ref e) => write!(f, "invalid period: {}", e),
            InvalidUser(ref e) => write!(f, "invalid user: {}", e),
            InvalidDelay(ref e) => write!(f, "invalid delay: {}", e),
            _ => f.write_str(self.description()),
        }
    }
}

impl Error for CrontabEntryParseError {
    fn description(&self) -> &str {
        use self::CrontabEntryParseError::*;
        match *self {
            InvalidSchedule(_) => "invalid schedule",
            InvalidPeriod(_) => "invalid period",
            InvalidUser(_) => "invalid user",
            InvalidDelay(_) => "invalid delay",
            InvalidEnvVarName => "invalid environment variable name",
            MissingPeriod => "missing period",
            MissingDelay => "missing delay",
            MissingJobId => "missing jobid",
            MissingEnvVarName => "missing environment variable name",
            MissingEnvVarValue => "missing environment variable value",
        }
    }
    fn cause(&self) -> Option<&Error> {
        use self::CrontabEntryParseError::*;
        match *self {
            InvalidSchedule(ref e) => Some(e),
            InvalidPeriod(ref e) => Some(e),
            InvalidUser(ref e) => Some(e),
            InvalidDelay(ref e) => Some(e),
            _ => None,
        }
    }
}

impl FromStr for UserCrontabEntry {
    type Err = CrontabEntryParseError;

    fn from_str(s: &str) -> Result<UserCrontabEntry, CrontabEntryParseError> {
        let seps = [' ', '\t'];
        let mut splits = s.split(&seps[..]).filter(|v| *v != "");
        Ok(UserCrontabEntry {
            sched: try!(Schedule::from_iter(&mut splits)),
            cmd: splits.collect::<Vec<&str>>().connect(" ")
        })
    }
}

impl FromStr for SystemCrontabEntry {
    type Err = CrontabEntryParseError;

    fn from_str(s: &str) -> Result<SystemCrontabEntry, CrontabEntryParseError> {
        let seps = [' ', '\t'];
        let mut splits = s.split(&seps[..]).filter(|v| *v != "");
        Ok(SystemCrontabEntry {
            sched: try!(Schedule::from_iter(&mut splits)),
            user: try!(splits.next().ok_or(UserInfoParseError).and_then(FromStr::from_str)),
            cmd: splits.collect::<Vec<&str>>().connect(" ")
        })
    }
}

impl FromStr for AnacrontabEntry {
    type Err = CrontabEntryParseError;

    fn from_str(s: &str) -> Result<AnacrontabEntry, CrontabEntryParseError> {
        let seps = [' ', '\t'];
        let mut splits = s.split(&seps[..]).filter(|v| *v != "");
        Ok(AnacrontabEntry {
            period: try!(splits.next().map(|v| v.parse().map_err(CrontabEntryParseError::InvalidPeriod)).unwrap_or(Err(CrontabEntryParseError::MissingPeriod))),
            delay: try!(splits.next().map(|v| v.parse().map_err(CrontabEntryParseError::InvalidDelay)).unwrap_or(Err(CrontabEntryParseError::MissingDelay))),
            jobid: try!(splits.next().map(ToString::to_string).ok_or(CrontabEntryParseError::MissingJobId)),
            cmd: splits.collect::<Vec<&str>>().connect(" ")
        })
    }
}

impl From<ScheduleParseError> for CrontabEntryParseError {
    fn from(e: ScheduleParseError) -> CrontabEntryParseError {
        CrontabEntryParseError::InvalidSchedule(e)
    }
}

impl From<UserInfoParseError> for CrontabEntryParseError {
    fn from(e: UserInfoParseError) -> CrontabEntryParseError {
        CrontabEntryParseError::InvalidUser(e)
    }
}

impl From<ParseIntError> for CrontabEntryParseError {
    fn from(e: ParseIntError) -> CrontabEntryParseError {
        CrontabEntryParseError::InvalidDelay(e)
    }
}
<commit_msg>add helper methods to get user and group from entries<commit_after>use std::str::FromStr;
use std::iter::FromIterator;
use std::error::Error;
use std::convert::From;
use std::num::ParseIntError;
use std::fmt::{self, Display, Formatter};

use schedule::{Schedule, Period, Calendar, ScheduleParseError, PeriodParseError};

pub trait ToCrontabEntry : FromStr<Err=CrontabEntryParseError> {
    fn to_crontab_entry(self) -> CrontabEntry;
}

#[derive(Debug, PartialEq)]
pub enum CrontabEntry {
    EnvVar(EnvVarEntry),
    User(UserCrontabEntry),
    System(SystemCrontabEntry),
    Anacron(AnacrontabEntry)
}

#[derive(Debug, PartialEq)]
pub struct EnvVarEntry(pub String, pub String);

#[derive(Debug, PartialEq)]
pub struct UserCrontabEntry {
    pub sched: Schedule,
    pub cmd: String
}

#[derive(Debug, PartialEq)]
pub struct SystemCrontabEntry {
    pub sched: Schedule,
    pub user: UserInfo,
    pub cmd: String
}

#[derive(Debug, PartialEq)]
pub struct AnacrontabEntry {
    pub period: Period,
    pub delay: u32,
    pub jobid: String,
    pub cmd: String
}

impl ToCrontabEntry for UserCrontabEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::User(self)
    }
}

impl ToCrontabEntry for SystemCrontabEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::System(self)
    }
}

impl ToCrontabEntry for AnacrontabEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::Anacron(self)
    }
}

impl ToCrontabEntry for EnvVarEntry {
    fn to_crontab_entry(self) -> CrontabEntry {
        CrontabEntry::EnvVar(self)
    }
}

impl CrontabEntry {
    pub fn period<'a>(&'a self) -> Option<&'a Period> {
        match *self {
            CrontabEntry::Anacron(AnacrontabEntry { ref period, .. }) => Some(period),
            CrontabEntry::User(UserCrontabEntry { sched: Schedule::Period(ref period), .. }) => Some(period),
            CrontabEntry::System(SystemCrontabEntry { sched: Schedule::Period(ref period), .. }) => Some(period),
            _ => None
        }
    }

    pub fn calendar<'a>(&'a self) -> Option<&'a Calendar> {
        match *self {
            CrontabEntry::User(UserCrontabEntry { sched: Schedule::Calendar(ref cal), .. }) => Some(cal),
            CrontabEntry::System(SystemCrontabEntry { sched: Schedule::Calendar(ref cal), .. }) => Some(cal),
            _ => None
        }
    }

    pub fn command<'a>(&'a self) -> Option<&'a str> {
        match *self {
            CrontabEntry::User(UserCrontabEntry { ref cmd, .. }) => Some(&**cmd),
            CrontabEntry::System(SystemCrontabEntry { ref cmd, .. }) => Some(&**cmd),
            CrontabEntry::Anacron(AnacrontabEntry { ref cmd, .. }) => Some(&**cmd),
            _ => None
        }
    }

    pub fn user<'a>(&'a self) -> Option<&'a str> {
        match *self {
            CrontabEntry::System(SystemCrontabEntry { user: UserInfo(ref user, _, _), .. }) => Some(&**user),
            _ => None
        }
    }

    pub fn group<'a>(&'a self) -> Option<&'a str> {
        match *self {
            CrontabEntry::System(SystemCrontabEntry { user: UserInfo(_, Some(ref group), _), .. }) => Some(&**group),
            _ => None
        }
    }
}

impl FromStr for EnvVarEntry {
    type Err = CrontabEntryParseError;
    fn from_str(s: &str) -> Result<EnvVarEntry, CrontabEntryParseError> {
        let spaces = [' ', '\t'];
        let mut splits = s.splitn(2, '=');

        let name = match splits.next() {
            Some(n) => n.trim_right_matches(&spaces[..]),
            None => return Err(CrontabEntryParseError::MissingEnvVarName)
        };

        if name.len() == 0 {
            return Err(CrontabEntryParseError::MissingEnvVarName)
        }

        if name.chars().any(|v| v == ' ' || v == '\t') {
            return Err(CrontabEntryParseError::InvalidEnvVarName)
        }

        let mut value = match splits.next() {
            Some(v) => v.trim_left_matches(&spaces[..]),
            None => return Err(CrontabEntryParseError::MissingEnvVarValue)
        };

        if value.len() > 1 {
            if &value[..1] == "'" || &value[..1] == "\"" && &value[..1] == &value[value.len()-1..] {
                value = &value[1..value.len()-1];
            }
        }

        Ok(EnvVarEntry(name.to_string(), value.to_string()))
    }
}

#[derive(Debug, PartialEq)]
// user, group, class
pub struct UserInfo(pub String, pub Option<String>, pub Option<String>);

#[derive(Debug, PartialEq)]
pub struct UserInfoParseError;
impl FromStr for UserInfo {
    type Err = UserInfoParseError;
    fn from_str(s: &str) -> Result<UserInfo, UserInfoParseError> {
        let mut splits = s.split(':');
        Ok(UserInfo(
            try!(splits.next().ok_or(UserInfoParseError).map(ToString::to_string)),
            splits.next().map(ToString::to_string),
            splits.next().map(ToString::to_string)
        ))
    }
}

impl Error for UserInfoParseError {
    fn description(&self) -> &str {
        "invalid user name"
    }
    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl Display for UserInfoParseError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("invalid user name")
    }
}

#[derive(Debug, PartialEq)]
pub enum CrontabEntryParseError {
    InvalidSchedule(ScheduleParseError),
    InvalidPeriod(PeriodParseError),
    InvalidUser(UserInfoParseError),
    InvalidDelay(ParseIntError),
    InvalidEnvVarName,
    MissingPeriod,
    MissingDelay,
    MissingJobId,
    MissingEnvVarName,
    MissingEnvVarValue,
}

impl Display for CrontabEntryParseError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::CrontabEntryParseError::*;
        match *self {
            InvalidSchedule(ref e) => write!(f, "invalid schedule: {}", e),
            InvalidPeriod(ref e) => write!(f, "invalid period: {}", e),
            InvalidUser(ref e) => write!(f, "invalid user: {}", e),
            InvalidDelay(ref e) => write!(f, "invalid delay: {}", e),
            _ => f.write_str(self.description()),
        }
    }
}

impl Error for CrontabEntryParseError {
    fn description(&self) -> &str {
        use self::CrontabEntryParseError::*;
        match *self {
            InvalidSchedule(_) => "invalid schedule",
            InvalidPeriod(_) => "invalid period",
            InvalidUser(_) => "invalid user",
            InvalidDelay(_) => "invalid delay",
            InvalidEnvVarName => "invalid environment variable name",
            MissingPeriod => "missing period",
            MissingDelay => "missing delay",
            MissingJobId => "missing jobid",
            MissingEnvVarName => "missing environment variable name",
            MissingEnvVarValue => "missing environment variable value",
        }
    }
    fn cause(&self) -> Option<&Error> {
        use self::CrontabEntryParseError::*;
        match *self {
            InvalidSchedule(ref e) => Some(e),
            InvalidPeriod(ref e) => Some(e),
            InvalidUser(ref e) => Some(e),
            InvalidDelay(ref e) => Some(e),
            _ => None,
        }
    }
}

impl FromStr for UserCrontabEntry {
    type Err = CrontabEntryParseError;

    fn from_str(s: &str) -> Result<UserCrontabEntry, CrontabEntryParseError> {
        let seps = [' ', '\t'];
        let mut splits = s.split(&seps[..]).filter(|v| *v != "");
        Ok(UserCrontabEntry {
            sched: try!(Schedule::from_iter(&mut splits)),
            cmd: splits.collect::<Vec<&str>>().connect(" ")
        })
    }
}

impl FromStr for SystemCrontabEntry {
    type Err = CrontabEntryParseError;

    fn from_str(s: &str) -> Result<SystemCrontabEntry, CrontabEntryParseError> {
        let seps = [' ', '\t'];
        let mut splits = s.split(&seps[..]).filter(|v| *v != "");
        Ok(SystemCrontabEntry {
            sched: try!(Schedule::from_iter(&mut splits)),
            user: try!(splits.next().ok_or(UserInfoParseError).and_then(FromStr::from_str)),
            cmd: splits.collect::<Vec<&str>>().connect(" ")
        })
    }
}

impl FromStr for AnacrontabEntry {
    type Err = CrontabEntryParseError;

    fn from_str(s: &str) -> Result<AnacrontabEntry, CrontabEntryParseError> {
        let seps = [' ', '\t'];
        let mut splits = s.split(&seps[..]).filter(|v| *v != "");
        Ok(AnacrontabEntry {
            period: try!(splits.next().map(|v| v.parse().map_err(CrontabEntryParseError::InvalidPeriod)).unwrap_or(Err(CrontabEntryParseError::MissingPeriod))),
            delay: try!(splits.next().map(|v| v.parse().map_err(CrontabEntryParseError::InvalidDelay)).unwrap_or(Err(CrontabEntryParseError::MissingDelay))),
            jobid: try!(splits.next().map(ToString::to_string).ok_or(CrontabEntryParseError::MissingJobId)),
            cmd: splits.collect::<Vec<&str>>().connect(" ")
        })
    }
}

impl From<ScheduleParseError> for CrontabEntryParseError {
    fn from(e: ScheduleParseError) -> CrontabEntryParseError {
        CrontabEntryParseError::InvalidSchedule(e)
    }
}

impl From<UserInfoParseError> for CrontabEntryParseError {
    fn from(e: UserInfoParseError) -> CrontabEntryParseError {
        CrontabEntryParseError::InvalidUser(e)
    }
}

impl From<ParseIntError> for CrontabEntryParseError {
    fn from(e: ParseIntError) -> CrontabEntryParseError {
        CrontabEntryParseError::InvalidDelay(e)
    }
}
<|endoftext|>


<commit_before>use std;

pub fn ngrams(text: &str, length: usize) -> NGrams {
    NGrams {
        length: length,
        chars: text.chars(),
        last_ngram: String::with_capacity(length*2),
    }
}

pub struct NGrams<'a> {
    length: usize,
    chars: std::str::Chars<'a>,
    last_ngram: String,
}

impl<'a> Iterator for NGrams<'a> {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        if self.last_ngram.len() < self.length {
            while self.last_ngram.len() < self.length {
                let new_char_option = self.chars.next();
                if new_char_option == None {
                    return None;
                }

                self.last_ngram.push(new_char_option.unwrap())
            }
            return Some(self.last_ngram.clone());
        }

        let new_char_option = self.chars.next();
        if new_char_option == None {
            return None;
        }

        let ngram = self.build_new_ngram(new_char_option.unwrap());
        self.last_ngram = ngram.clone();
        Some(ngram)
    }
}

impl<'a> NGrams<'a> {
    fn build_new_ngram(&mut self, new_character: char) -> String {
        let mut ngram = String::with_capacity(self.length*2);
        let current_chars = self.last_ngram.chars().skip(1);

        for character in current_chars {
            ngram.push(character);
        }
        ngram.push(new_character);

        ngram
    }
}
<commit_msg>Convert to more idiomatic Rust<commit_after>use std;

pub fn ngrams(text: &str, length: usize) -> NGrams {
    NGrams {
        length: length,
        chars: text.chars(),
        last_ngram: String::with_capacity(length*2),
    }
}

pub struct NGrams<'a> {
    length: usize,
    chars: std::str::Chars<'a>,
    last_ngram: String,
}

impl<'a> Iterator for NGrams<'a> {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        if self.last_ngram.len() < self.length {
            while self.last_ngram.len() < self.length {
                match self.chars.next() {
                    Some(character) => {
                        self.last_ngram.push(character);
                    },
                    None => {
                        return None;
                    },
                };
            }
            return Some(self.last_ngram.clone());
        }

        match self.chars.next() {
            Some(character) => {
                let ngram = self.build_new_ngram(character);
                self.last_ngram = ngram.clone();
                Some(ngram)
            },
            None => None,
        }
    }
}

impl<'a> NGrams<'a> {
    fn build_new_ngram(&mut self, new_character: char) -> String {
        let mut ngram = String::with_capacity(self.length*2);
        let current_chars = self.last_ngram.chars().skip(1);

        for character in current_chars {
            ngram.push(character);
        }
        ngram.push(new_character);

        ngram
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2019 pam-auth Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use libc::{c_char, c_int, c_void, calloc, free, size_t, strdup};
use pam::{PamMessage, PamMessageStyle, PamResponse, PamReturnCode};

use std::ffi::{CStr, CString};
use std::mem;
use std::slice;

pub extern "C" fn converse(
    num_msg: c_int,
    msg: *mut *mut PamMessage,
    resp: *mut *mut PamResponse,
    appdata_ptr: *mut c_void,
) -> c_int {
    unsafe {
        // allocate space for responses
        *resp =
            calloc(num_msg as usize, mem::size_of::<PamResponse>() as size_t) as *mut PamResponse;
        if (*resp).is_null() {
            return PamReturnCode::BUF_ERR as c_int;
        }
    }

    let data: &[&str] = unsafe { slice::from_raw_parts(appdata_ptr as *const &str, 2) };

    let mut result: PamReturnCode = PamReturnCode::SUCCESS;
    for i in 0..num_msg as isize {
        unsafe {
            // get indexed values
            let m: &mut PamMessage = &mut **(msg.offset(i));
            let r: &mut PamResponse = &mut *((*resp).offset(i));
            // match on msg_style
            match PamMessageStyle::from(m.msg_style) {
                // assume username is requested
                PamMessageStyle::PROMPT_ECHO_ON => {
                    if let Ok(username) = CString::new(data[0]) {
                        r.resp = strdup(username.as_ptr() as *const c_char);
                    } else {
                        result = PamReturnCode::CONV_ERR;
                    }
                }
                // assume password is requested
                PamMessageStyle::PROMPT_ECHO_OFF => {
                    if let Ok(password) = CString::new(data[1]) {
                        r.resp = strdup(password.as_ptr() as *const c_char);
                    } else {
                        result = PamReturnCode::CONV_ERR;
                    }
                }
                // an error occured
                PamMessageStyle::ERROR_MSG => {
                    result = PamReturnCode::CONV_ERR;
                }
                // print the message to stdout
                PamMessageStyle::TEXT_INFO => {
                    println!(
                        "PAM_TEXT_INFO: {}",
                        String::from_utf8_lossy(CStr::from_ptr(m.msg).to_bytes())
                    );
                }
            }
        }
        if result != PamReturnCode::SUCCESS {
            break;
        }
    }

    // free allocated memory if an error occured
    if result != PamReturnCode::SUCCESS {
        unsafe { free(*resp as *mut c_void) };
    }

    result as c_int
}
<commit_msg>don't set response on errors<commit_after>// Copyright 2015-2019 pam-auth Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use libc::{c_char, c_int, c_void, calloc, free, size_t, strdup};
use pam::{PamMessage, PamMessageStyle, PamResponse, PamReturnCode};

use std::ffi::{CStr, CString};
use std::mem;
use std::slice;

pub extern "C" fn converse(
    num_msg: c_int,
    msg: *mut *mut PamMessage,
    out_resp: *mut *mut PamResponse,
    appdata_ptr: *mut c_void,
) -> c_int {
        // allocate space for responses
    let resp = unsafe {
        calloc(num_msg as usize, mem::size_of::<PamResponse>() as size_t) as *mut PamResponse
    };
    if resp.is_null() {
        return PamReturnCode::BUF_ERR as c_int;
    }

    let data: &[&str] = unsafe { slice::from_raw_parts(appdata_ptr as *const &str, 2) };

    let mut result: PamReturnCode = PamReturnCode::SUCCESS;
    for i in 0..num_msg as isize {
        unsafe {
            // get indexed values
            let m: &mut PamMessage = &mut **(msg.offset(i));
            let r: &mut PamResponse = &mut *(resp.offset(i));
            // match on msg_style
            match PamMessageStyle::from(m.msg_style) {
                // assume username is requested
                PamMessageStyle::PROMPT_ECHO_ON => {
                    if let Ok(username) = CString::new(data[0]) {
                        r.resp = strdup(username.as_ptr() as *const c_char);
                    } else {
                        result = PamReturnCode::CONV_ERR;
                    }
                }
                // assume password is requested
                PamMessageStyle::PROMPT_ECHO_OFF => {
                    if let Ok(password) = CString::new(data[1]) {
                        r.resp = strdup(password.as_ptr() as *const c_char);
                    } else {
                        result = PamReturnCode::CONV_ERR;
                    }
                }
                // an error occured
                PamMessageStyle::ERROR_MSG => {
                    result = PamReturnCode::CONV_ERR;
                }
                // print the message to stdout
                PamMessageStyle::TEXT_INFO => {
                    println!(
                        "PAM_TEXT_INFO: {}",
                        String::from_utf8_lossy(CStr::from_ptr(m.msg).to_bytes())
                    );
                }
            }
        }
        if result != PamReturnCode::SUCCESS {
            break;
        }
    }

    // free allocated memory if an error occured
    if result != PamReturnCode::SUCCESS {
        unsafe { free(resp as *mut c_void) };
    } else {
        unsafe { *out_resp = resp };
    }

    result as c_int
}
<|endoftext|>


<commit_before>use std::net::ToSocketAddrs;
use std::time::Duration;
use std::env;
use router::{Router, HttpRouter, Matcher};
use middleware::{MiddlewareStack, Middleware, ErrorHandler};
use server::Server;
use hyper::method::Method;
use hyper::status::StatusCode;

//pre defined middleware
use default_error_handler::DefaultErrorHandler;

/// Configuration options for the server.
///
/// This struct provides a builder-style API for constructing the desired options.
///
/// # Examples
/// ```{rust}
/// use nickel::{Nickel, Options};
/// let mut server = Nickel::new();
///
/// // Don't print to stdout when starting the server.
/// server.options = Options::default().output_on_listen(false);
/// ```
pub struct Options {
    output_on_listen: bool,
}

impl Options {
    /// Whether the server should print the local address it is listening on when starting.
    ///
    /// Defaults to `true`.
    pub fn output_on_listen(mut self, output: bool) -> Self {
        self.output_on_listen = output;
        self
    }
}

impl Default for Options {
    fn default() -> Self {
        Options {
            output_on_listen: true,
        }
    }
}

/// Nickel is the application object. It's the surface that
/// holds all public APIs.
pub struct Nickel<D: Sync + Send + 'static = ()> {
    middleware_stack: MiddlewareStack<D>,
    data: D,
    keep_alive_timeout: Option<Duration>,

    /// Configuration options for the server.
    pub options: Options,
}

impl<D: Sync + Send + 'static> HttpRouter<D> for Nickel<D> {
    fn add_route<M: Into<Matcher>, H: Middleware<D>>(&mut self, method: Method, matcher: M, handler: H) -> &mut Self {
        let mut router = Router::new();
        router.add_route(method, matcher, handler);
        self.utilize(router);
        self
    }
}

impl Nickel<()> {
    /// Creates an instance of Nickel with default error handling.
    pub fn new() -> Nickel<()> {
        Nickel::with_data(())
    }
}

impl<D: Sync + Send + 'static> Nickel<D> {
    /// Creates an instance of Nickel with default error handling and custom data.
    pub fn with_data(data: D) -> Nickel<D> {
        let mut middleware_stack = MiddlewareStack::new();

        // Hook up the default error handler by default. Users are
        // free to cancel it out from their custom error handler if
        // they don't like the default behaviour.
        middleware_stack.add_error_handler(DefaultErrorHandler);

        Nickel {
            middleware_stack: middleware_stack,
            options: Options::default(),
            data: data,
            // Default value from nginx
            keep_alive_timeout: Some(Duration::from_secs(75))
        }
    }

    /// Registers a middleware handler which will be invoked among other middleware
    /// handlers before each request. Middleware can be stacked and is invoked in the
    /// same order it was registered.
    ///
    /// A middleware handler is nearly identical to a regular route handler with the only
    /// difference that it expects a result of either Action or NickelError.
    /// That is to indicate whether other middleware handlers (if any) further
    /// down the stack should continue or if the middleware invocation should
    /// be stopped after the current handler.
    ///
    /// # Examples
    /// ```{rust}
    /// # #[macro_use] extern crate nickel;
    /// # fn main() {
    /// use nickel::Nickel;
    /// let mut server = Nickel::new();
    /// server.utilize(middleware! { |req|
    ///     println!("logging request: {:?}", req.origin.uri);
    /// });
    /// # }
    /// ```
    pub fn utilize<T: Middleware<D>>(&mut self, handler: T){
        self.middleware_stack.add_middleware(handler);
    }

    /// Registers an error handler which will be invoked among other error handler
    /// as soon as any regular handler returned an error
    ///
    /// A error handler is nearly identical to a regular middleware handler with the only
    /// difference that it takes an additional error parameter or type `NickelError.
    ///
    /// # Examples
    ///
    /// ```{rust}
    /// # extern crate nickel;
    /// # fn main() {
    /// use std::io::Write;
    /// use nickel::{Nickel, Request, Continue, Halt};
    /// use nickel::{NickelError, Action};
    /// use nickel::status::StatusCode::NotFound;
    ///
    /// fn error_handler<D>(err: &mut NickelError<D>, _req: &mut Request<D>) -> Action {
    ///    if let Some(ref mut res) = err.stream {
    ///        if res.status() == NotFound {
    ///            let _ = res.write_all(b"<h1>Call the police!</h1>");
    ///            return Halt(())
    ///        }
    ///    }
    ///
    ///     Continue(())
    /// }
    ///
    /// let mut server = Nickel::new();
    ///
    /// let ehandler: fn(&mut NickelError<()>, &mut Request<()>) -> Action = error_handler;
    ///
    /// server.handle_error(ehandler)
    /// # }
    /// ```
    pub fn handle_error<T: ErrorHandler<D>>(&mut self, handler: T){
        self.middleware_stack.add_error_handler(handler);
    }

    /// Create a new middleware to serve as a router.
    ///
    ///
    /// # Examples
    /// ```{rust}
    /// #[macro_use] extern crate nickel;
    /// use nickel::{Nickel, HttpRouter};
    ///
    /// fn main() {
    ///     let mut server = Nickel::new();
    ///     let mut router = Nickel::router();
    ///
    ///     router.get("/foo", middleware! {
    ///         "Hi from /foo"
    ///     });
    ///
    ///     server.utilize(router);
    /// }
    /// ```
    pub fn router() -> Router<D> {
        Router::new()
    }

    /// Bind and listen for connections on the given host and port.
    ///
    /// # Panics
    ///
    /// Panics if `addr` is an invalid `SocketAddr`.
    ///
    /// # Examples
    /// ```{rust,no_run}
    /// use nickel::Nickel;
    ///
    /// let server = Nickel::new();
    /// server.listen("127.0.0.1:6767");
    /// ```
    pub fn listen<T: ToSocketAddrs>(mut self, addr: T) {
        self.middleware_stack.add_middleware(middleware! {
            (StatusCode::NotFound, "File Not Found")
        });

        let server = Server::new(self.middleware_stack, self.data);

        let is_test_harness = env::vars().any(|(ref k, _)| k == "NICKEL_TEST_HARNESS");

        let listener = if is_test_harness {
            // If we're under a test harness, we'll pass zero to get assigned a random
            // port. See http://doc.rust-lang.org/std/net/struct.TcpListener.html#method.bind
            server.serve("localhost:0", self.keep_alive_timeout).unwrap()
        } else {
            server.serve(addr, self.keep_alive_timeout).unwrap()
        };

        if self.options.output_on_listen {
            println!("Listening on http://{}", listener.socket);
            println!("Ctrl-C to shutdown server");
        }
    }

    /// Set the timeout for the keep-alive loop
    ///
    /// # Performance
    ///
    /// Setting this to `None` can have significant performance impact, but if
    /// you need to use a version of rustc < 1.4, then it may be a good choice.
    ///
    /// Alternatively, setting this too high, can lead to thread exhaustion,
    /// see [this thread](https://github.com/hyperium/hyper/issues/368) for more.
    ///
    /// # Default
    ///
    /// The default value is 75 seconds.
    pub fn keep_alive_timeout(&mut self, timeout: Option<Duration>){
        self.keep_alive_timeout = timeout;
    }
}

#[test]
#[should_panic]
fn invalid_listen_addr() {
    Nickel::new().listen("127.0.0.1.6667");
}
<commit_msg>feat(https): Implemented Nickel::listen_https() when ssl is available<commit_after>use std::net::ToSocketAddrs;
use std::time::Duration;
use std::env;
use router::{Router, HttpRouter, Matcher};
use middleware::{MiddlewareStack, Middleware, ErrorHandler};
use server::Server;
use hyper::method::Method;
use hyper::status::StatusCode;

//pre defined middleware
use default_error_handler::DefaultErrorHandler;

/// Configuration options for the server.
///
/// This struct provides a builder-style API for constructing the desired options.
///
/// # Examples
/// ```{rust}
/// use nickel::{Nickel, Options};
/// let mut server = Nickel::new();
///
/// // Don't print to stdout when starting the server.
/// server.options = Options::default().output_on_listen(false);
/// ```
pub struct Options {
    output_on_listen: bool,
}

impl Options {
    /// Whether the server should print the local address it is listening on when starting.
    ///
    /// Defaults to `true`.
    pub fn output_on_listen(mut self, output: bool) -> Self {
        self.output_on_listen = output;
        self
    }
}

impl Default for Options {
    fn default() -> Self {
        Options {
            output_on_listen: true,
        }
    }
}

/// Nickel is the application object. It's the surface that
/// holds all public APIs.
pub struct Nickel<D: Sync + Send + 'static = ()> {
    middleware_stack: MiddlewareStack<D>,
    data: D,
    keep_alive_timeout: Option<Duration>,

    /// Configuration options for the server.
    pub options: Options,
}

impl<D: Sync + Send + 'static> HttpRouter<D> for Nickel<D> {
    fn add_route<M: Into<Matcher>, H: Middleware<D>>(&mut self, method: Method, matcher: M, handler: H) -> &mut Self {
        let mut router = Router::new();
        router.add_route(method, matcher, handler);
        self.utilize(router);
        self
    }
}

impl Nickel<()> {
    /// Creates an instance of Nickel with default error handling.
    pub fn new() -> Nickel<()> {
        Nickel::with_data(())
    }
}

impl<D: Sync + Send + 'static> Nickel<D> {
    /// Creates an instance of Nickel with default error handling and custom data.
    pub fn with_data(data: D) -> Nickel<D> {
        let mut middleware_stack = MiddlewareStack::new();

        // Hook up the default error handler by default. Users are
        // free to cancel it out from their custom error handler if
        // they don't like the default behaviour.
        middleware_stack.add_error_handler(DefaultErrorHandler);

        Nickel {
            middleware_stack: middleware_stack,
            options: Options::default(),
            data: data,
            // Default value from nginx
            keep_alive_timeout: Some(Duration::from_secs(75))
        }
    }

    /// Registers a middleware handler which will be invoked among other middleware
    /// handlers before each request. Middleware can be stacked and is invoked in the
    /// same order it was registered.
    ///
    /// A middleware handler is nearly identical to a regular route handler with the only
    /// difference that it expects a result of either Action or NickelError.
    /// That is to indicate whether other middleware handlers (if any) further
    /// down the stack should continue or if the middleware invocation should
    /// be stopped after the current handler.
    ///
    /// # Examples
    /// ```{rust}
    /// # #[macro_use] extern crate nickel;
    /// # fn main() {
    /// use nickel::Nickel;
    /// let mut server = Nickel::new();
    /// server.utilize(middleware! { |req|
    ///     println!("logging request: {:?}", req.origin.uri);
    /// });
    /// # }
    /// ```
    pub fn utilize<T: Middleware<D>>(&mut self, handler: T){
        self.middleware_stack.add_middleware(handler);
    }

    /// Registers an error handler which will be invoked among other error handler
    /// as soon as any regular handler returned an error
    ///
    /// A error handler is nearly identical to a regular middleware handler with the only
    /// difference that it takes an additional error parameter or type `NickelError.
    ///
    /// # Examples
    ///
    /// ```{rust}
    /// # extern crate nickel;
    /// # fn main() {
    /// use std::io::Write;
    /// use nickel::{Nickel, Request, Continue, Halt};
    /// use nickel::{NickelError, Action};
    /// use nickel::status::StatusCode::NotFound;
    ///
    /// fn error_handler<D>(err: &mut NickelError<D>, _req: &mut Request<D>) -> Action {
    ///    if let Some(ref mut res) = err.stream {
    ///        if res.status() == NotFound {
    ///            let _ = res.write_all(b"<h1>Call the police!</h1>");
    ///            return Halt(())
    ///        }
    ///    }
    ///
    ///     Continue(())
    /// }
    ///
    /// let mut server = Nickel::new();
    ///
    /// let ehandler: fn(&mut NickelError<()>, &mut Request<()>) -> Action = error_handler;
    ///
    /// server.handle_error(ehandler)
    /// # }
    /// ```
    pub fn handle_error<T: ErrorHandler<D>>(&mut self, handler: T){
        self.middleware_stack.add_error_handler(handler);
    }

    /// Create a new middleware to serve as a router.
    ///
    ///
    /// # Examples
    /// ```{rust}
    /// #[macro_use] extern crate nickel;
    /// use nickel::{Nickel, HttpRouter};
    ///
    /// fn main() {
    ///     let mut server = Nickel::new();
    ///     let mut router = Nickel::router();
    ///
    ///     router.get("/foo", middleware! {
    ///         "Hi from /foo"
    ///     });
    ///
    ///     server.utilize(router);
    /// }
    /// ```
    pub fn router() -> Router<D> {
        Router::new()
    }

    /// Bind and listen for connections on the given host and port.
    ///
    /// # Panics
    ///
    /// Panics if `addr` is an invalid `SocketAddr`.
    ///
    /// # Examples
    /// ```{rust,no_run}
    /// use nickel::Nickel;
    ///
    /// let server = Nickel::new();
    /// server.listen("127.0.0.1:6767");
    /// ```
    pub fn listen<T: ToSocketAddrs>(mut self, addr: T) {
        self.middleware_stack.add_middleware(middleware! {
            (StatusCode::NotFound, "File Not Found")
        });

        let server = Server::new(self.middleware_stack, self.data);

        let is_test_harness = env::vars().any(|(ref k, _)| k == "NICKEL_TEST_HARNESS");

        let listener = if is_test_harness {
            // If we're under a test harness, we'll pass zero to get assigned a random
            // port. See http://doc.rust-lang.org/std/net/struct.TcpListener.html#method.bind
            server.serve("localhost:0", self.keep_alive_timeout).unwrap()
        } else {
            server.serve(addr, self.keep_alive_timeout).unwrap()
        };

        if self.options.output_on_listen {
            println!("Listening on http://{}", listener.socket);
            println!("Ctrl-C to shutdown server");
        }
    }

    /// Set the timeout for the keep-alive loop
    ///
    /// # Performance
    ///
    /// Setting this to `None` can have significant performance impact, but if
    /// you need to use a version of rustc < 1.4, then it may be a good choice.
    ///
    /// Alternatively, setting this too high, can lead to thread exhaustion,
    /// see [this thread](https://github.com/hyperium/hyper/issues/368) for more.
    ///
    /// # Default
    ///
    /// The default value is 75 seconds.
    pub fn keep_alive_timeout(&mut self, timeout: Option<Duration>){
        self.keep_alive_timeout = timeout;
    }
}

#[cfg(feature = "ssl")]
mod ssl {
    use std::net::ToSocketAddrs;
    use std::env;
    use server::Server;
    use hyper::status::StatusCode;
    use hyper::net::Ssl;

    use super::Nickel;

    /// Bind and listen for connections on the given host and port.
    /// Only accepts SSL connections
    ///
    /// # Panics
    ///
    /// Panics if `addr` is an invalid `SocketAddr`.
    ///
    /// # Examples
    /// ```{rust,no_run}
    /// # extern crate nickel;
    /// extern crate hyper;
    ///
    /// use nickel::Nickel;
    /// use hyper::net::Openssl;
    ///
    /// # fn main() {
    /// let server = Nickel::new();
    /// let ssl = Openssl::with_cert_and_key("foo.crt", "key.pem").unwrap();
    /// server.listen_https("127.0.0.1:6767", ssl);
    /// # }
    /// ```
    impl <D: Sync + Send + 'static> Nickel<D> {
        pub fn listen_https<T,S>(mut self, addr: T, ssl: S)
        where T: ToSocketAddrs,
              S: Ssl + Send + Clone + 'static {
            self.middleware_stack.add_middleware(middleware! {
                (StatusCode::NotFound, "File Not Found")
            });

            let server = Server::new(self.middleware_stack, self.data);

            let is_test_harness = env::vars().any(|(ref k, _)| k == "NICKEL_TEST_HARNESS");

            let listener = if is_test_harness {
                // If we're under a test harness, we'll pass zero to get assigned a random
                // port. See http://doc.rust-lang.org/std/net/struct.TcpListener.html#method.bind
                server.serve_https("localhost:0", self.keep_alive_timeout, ssl).unwrap()
            } else {
                server.serve_https(addr, self.keep_alive_timeout, ssl).unwrap()
            };

            println!("Listening on https://{}", listener.socket);
            println!("Ctrl-C to shutdown server");
        }
    }
}

#[test]
#[should_panic]
fn invalid_listen_addr() {
    Nickel::new().listen("127.0.0.1.6667");
}
<|endoftext|>