<commit_before>use crate::patches::Move;

use super::prelude::*;
use itertools::Itertools;
use similar::{ChangeTag, TextDiff};
use std::any::{type_name, Any};
use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::iter::FromIterator;
use std::ops::Deref;

impl Diffable for String {
    diffable_is_same!(String);

    fn is_equal(&self, other: &Self) -> Result<()> {
        if self == other {
            Ok(())
        } else {
            bail!(Error::NotEqual)
        }
    }

    diffable_diff!(String);

    fn diff_same(&self, differ: &mut Differ, other: &Self) {
        if self == other {
            return;
        }

        let diff = TextDiff::from_chars(self, other);
        let mut ops: Vec<Operation> = Vec::new();
        let mut last: char = 'e';
        let mut curr: char = 'e';
        let mut next: char = 'e';
        let mut position = 0;
        let mut key: usize = 0;
        let mut items: usize = 0;
        let mut value: String = String::new();
        let mut adds: HashMap<String, usize> = HashMap::new();
        let mut removes: HashMap<String, usize> = HashMap::new();

        let changes = diff.iter_all_changes().collect_vec();
        for (index, change) in changes.iter().enumerate() {
            match change.tag() {
                ChangeTag::Equal => {
                    position += 1;
                    next = 'e';
                }
                ChangeTag::Delete => match curr {
                    'd' => {
                        items += 1;
                        value.push_str(change.value());
                    }
                    _ => {
                        next = 'd';
                        key = position;
                        items = 1;
                        value = change.value().into();
                    }
                },
                ChangeTag::Insert => {
                    match curr {
                        'i' => {
                            value.push_str(change.value());
                        }
                        _ => {
                            next = 'i';
                            if last != 'd' {
                                key = position;
                            }
                            value = change.value().into();
                        }
                    }
                    position += 1;
                }
            }

            let end = index == changes.len() - 1;
            if next != curr || end {
                // Generate a keys for a string position index
                fn keys(index: usize) -> VecDeque<Key> {
                    VecDeque::from_iter(vec![Key::Index(index)])
                }

                match if end { next } else { curr } {
                    'd' => {
                        if next != 'i' {
                            if let Entry::Occupied(entry) = adds.entry(value.clone()) {
                                let index = *entry.get();
                                let move_ = if let Some(Operation::Add(add)) = ops.get(index) {
                                    Operation::Move(Move {
                                        from: keys(position - value.len()),
                                        items,
                                        to: add.keys.clone(),
                                    })
                                } else {
                                    unreachable!()
                                };
                                ops.remove(index);
                                ops.push(move_);
                                entry.remove_entry();
                            } else {
                                ops.push(Operation::Remove(Remove {
                                    keys: keys(key),
                                    items,
                                }));
                                removes.insert(value.clone(), ops.len() - 1);
                            }
                        }
                    }
                    'i' => {
                        if last == 'd' || end && curr == 'd' {
                            ops.push(Operation::Replace(Replace {
                                keys: keys(key),
                                items,
                                value: Box::new(value.clone()),
                            }));
                        } else {
                            // Clippy seems to think this is collapsible with the above. It's not, tests break.
                            #[allow(clippy::collapsible_else_if)]
                            if let Entry::Occupied(entry) = removes.entry(value.clone()) {
                                let index = *entry.get();
                                let move_ = if let Some(Operation::Remove(remove)) = ops.get(index)
                                {
                                    Operation::Move(Move {
                                        from: remove.keys.clone(),
                                        items: remove.items,
                                        to: keys(position - value.len()),
                                    })
                                } else {
                                    unreachable!()
                                };
                                ops.remove(index);
                                ops.push(move_);
                                entry.remove_entry();
                            } else {
                                ops.push(Operation::Add(Add {
                                    keys: keys(key),
                                    value: Box::new(value.clone()),
                                }));
                                adds.insert(value.clone(), ops.len() - 1);
                            }
                        }
                    }
                    _ => {}
                };

                last = curr;
            }

            curr = next;
        }

        differ.append(ops)
    }

    fn apply_add(&mut self, keys: &mut Keys, value: &Box<dyn Any>) {
        let value = if let Some(value) = value.deref().downcast_ref::<Self>() {
            value
        } else {
            unreachable!("Invalid replacement value for {}", type_name::<Self>())
        };

        if let Some(Key::Index(index)) = keys.pop_front() {
            self.insert_str(index, value)
        } else {
            invalid_keys!(keys)
        }
    }

    fn apply_remove(&mut self, keys: &mut Keys, items: usize) {
        if let Some(Key::Index(index)) = keys.pop_front() {
            self.replace_range(index..(index + items), "")
        } else {
            invalid_keys!(keys)
        }
    }

    fn apply_move(&mut self, from: &mut Keys, items: usize, to: &mut Keys) {
        if let (Some(Key::Index(from)), Some(Key::Index(to))) = (from.pop_front(), to.pop_front()) {
            let chars: Vec<char> = self.chars().collect();
            let chars = if from < to {
                [
                    &chars[..from],
                    &chars[(from + items)..(to + items)],
                    &chars[from..(from + items)],
                    &chars[(to + items)..],
                ]
                .concat()
            } else {
                [
                    &chars[..to],
                    &chars[from..(from + items)],
                    &chars[to..from],
                    &chars[(from + items)..],
                ]
                .concat()
            };
            *self = chars.into_iter().collect();
        } else {
            invalid_keys!(from)
        }
    }

    fn apply_replace(&mut self, keys: &mut Keys, items: usize, value: &Box<dyn Any>) {
        let value = if let Some(value) = value.deref().downcast_ref::<Self>() {
            value
        } else {
            unreachable!("Invalid replacement value for {}", type_name::<Self>())
        };

        if keys.is_empty() {
            *self = value.clone();
        } else if let Some(Key::Index(index)) = keys.pop_front() {
            self.replace_range(index..(index + items), value);
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        assert_json,
        patches::{apply_new, diff, equal},
    };
    use pretty_assertions::assert_eq;

    #[test]
    fn test_string() {
        let empty = "".to_string();
        let a = "1".to_string();
        let b = "123".to_string();
        let c = "a2b3".to_string();
        let d = "abcdef".to_string();
        let e = "adefbc".to_string();
        let f = "adbcfe".to_string();

        assert!(equal(&empty, &empty));
        assert!(equal(&a, &a));
        assert!(equal(&b, &b));
        assert!(equal(&c, &c));
        assert!(equal(&d, &d));

        // No diff

        assert_json!(diff(&empty, &empty), []);
        assert_json!(diff(&a, &a), []);
        assert_json!(diff(&d, &d), []);

        // Add

        let patch = diff(&empty, &a);
        assert_json!(
            patch,
            [{ "op": "add", "keys": [0], "value": "1" }]
        );
        assert_eq!(apply_new(&empty, &patch), a);

        let patch = diff(&empty, &d);
        assert_json!(
            patch,
            [{ "op": "add", "keys": [0], "value": "abcdef" }]
        );
        assert_eq!(apply_new(&empty, &patch), d);

        let patch = diff(&a, &b);
        assert_json!(
            patch,
            [{ "op": "add", "keys": [1], "value": "23" }]
        );
        assert_eq!(apply_new(&a, &patch), b);

        // Remove

        let patch = diff(&a, &empty);
        assert_json!(
            patch,
            [{ "op": "remove", "keys": [0], "items": 1 }]
        );

        let patch = diff(&d, &empty);
        assert_json!(
            patch,
            [{ "op": "remove", "keys": [0], "items": 6 }]
        );

        let patch = diff(&b, &a);
        assert_json!(
            patch,
            [{ "op": "remove", "keys": [1], "items": 2 }]
        );

        // Replace

        let patch = diff(&a, &c);
        assert_json!(
            patch,
            [{ "op": "replace", "keys": [0], "items": 1, "value": "a2b3" }]
        );
        assert_eq!(apply_new(&a, &patch), c);

        let patch = diff(&b, &d);
        assert_json!(
            patch,
            [{ "op": "replace", "keys": [0], "items": 3, "value": "abcdef" }]
        );
        assert_eq!(apply_new(&b, &patch), d);

        // Move

        let patch = diff(&d, &e);
        assert_json!(
            patch,
            [
                { "op": "move", "from": [1], "items": 2, "to": [4] },
            ]
        );
        assert_eq!(apply_new(&d, &patch), e);

        let patch = diff(&e, &d);
        assert_json!(
            patch,
            [
                { "op": "move", "from": [4], "items": 2, "to": [1] },
            ]
        );
        assert_eq!(apply_new(&e, &patch), d);

        // Mixed

        let patch = diff(&c, &d);
        assert_json!(
            patch,
            [
                { "op": "remove", "keys": [1], "items": 1 },
                { "op": "replace", "keys": [2], "items": 1, "value": "cdef" }
            ]
        );
        assert_eq!(apply_new(&c, &patch), d);

        let patch = diff(&d, &c);
        assert_json!(
            patch,
            [
                { "op": "add", "keys": [1], "value": "2" },
                { "op": "replace", "keys": [3], "items": 4, "value": "3" }
            ]
        );
        assert_eq!(apply_new(&d, &patch), c);

        let patch = diff(&d, &f);
        assert_json!(
            patch,
            [
                { "op": "add", "keys": [1], "value": "d" },
                { "op": "replace", "keys": [4], "items": 1, "value": "f" },
                { "op": "remove", "keys": [6], "items": 1 }
            ]
        );
        assert_eq!(apply_new(&d, &patch), f);
    }
}
<commit_msg>fix(String patches): Make unicode character aware<commit_after>use crate::patches::Move;

use super::prelude::*;
use itertools::Itertools;
use similar::{ChangeTag, TextDiff};
use std::any::{type_name, Any};
use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::iter::FromIterator;
use std::ops::Deref;

impl Diffable for String {
    diffable_is_same!(String);

    fn is_equal(&self, other: &Self) -> Result<()> {
        if self == other {
            Ok(())
        } else {
            bail!(Error::NotEqual)
        }
    }

    diffable_diff!(String);

    fn diff_same(&self, differ: &mut Differ, other: &Self) {
        if self == other {
            return;
        }

        let diff = TextDiff::from_chars(self, other);
        let mut ops: Vec<Operation> = Vec::new();
        let mut last: char = 'e';
        let mut curr: char = 'e';
        let mut next: char = 'e';
        let mut position = 0;
        let mut key: usize = 0;
        let mut items: usize = 0;
        let mut value: String = String::new();
        let mut adds: HashMap<String, usize> = HashMap::new();
        let mut removes: HashMap<String, usize> = HashMap::new();

        let changes = diff.iter_all_changes().collect_vec();
        for (index, change) in changes.iter().enumerate() {
            match change.tag() {
                ChangeTag::Equal => {
                    position += 1;
                    next = 'e';
                }
                ChangeTag::Delete => match curr {
                    'd' => {
                        items += 1;
                        value.push_str(change.value());
                    }
                    _ => {
                        next = 'd';
                        key = position;
                        items = 1;
                        value = change.value().into();
                    }
                },
                ChangeTag::Insert => {
                    match curr {
                        'i' => {
                            value.push_str(change.value());
                        }
                        _ => {
                            next = 'i';
                            if last != 'd' {
                                key = position;
                            }
                            value = change.value().into();
                        }
                    }
                    position += 1;
                }
            }

            let end = index == changes.len() - 1;
            if next != curr || end {
                // Generate a keys for a string position index
                fn keys(index: usize) -> VecDeque<Key> {
                    VecDeque::from_iter(vec![Key::Index(index)])
                }

                let change = if end && next != 'e' { next } else { curr };
                match change {
                    'd' => {
                        if next != 'i' {
                            if let Entry::Occupied(entry) = adds.entry(value.clone()) {
                                let index = *entry.get();
                                let move_ = if let Some(Operation::Add(add)) = ops.get(index) {
                                    Operation::Move(Move {
                                        from: keys(position - value.len()),
                                        items,
                                        to: add.keys.clone(),
                                    })
                                } else {
                                    unreachable!()
                                };
                                ops.remove(index);
                                ops.push(move_);
                                entry.remove_entry();
                            } else {
                                ops.push(Operation::Remove(Remove {
                                    keys: keys(key),
                                    items,
                                }));
                                removes.insert(value.clone(), ops.len() - 1);
                            }
                        }
                    }
                    'i' => {
                        if last == 'd' || end && curr == 'd' {
                            ops.push(Operation::Replace(Replace {
                                keys: keys(key),
                                items,
                                value: Box::new(value.clone()),
                            }));
                        } else {
                            // Clippy seems to think this is collapsible with the above. It's not, tests break.
                            #[allow(clippy::collapsible_else_if)]
                            if let Entry::Occupied(entry) = removes.entry(value.clone()) {
                                let index = *entry.get();
                                let move_ = if let Some(Operation::Remove(remove)) = ops.get(index)
                                {
                                    Operation::Move(Move {
                                        from: remove.keys.clone(),
                                        items: remove.items,
                                        to: keys(position - value.len()),
                                    })
                                } else {
                                    unreachable!()
                                };
                                ops.remove(index);
                                ops.push(move_);
                                entry.remove_entry();
                            } else {
                                ops.push(Operation::Add(Add {
                                    keys: keys(key),
                                    value: Box::new(value.clone()),
                                }));
                                adds.insert(value.clone(), ops.len() - 1);
                            }
                        }
                    }
                    _ => {}
                };

                last = curr;
            }

            curr = next;
        }

        differ.append(ops)
    }

    fn apply_add(&mut self, keys: &mut Keys, value: &Box<dyn Any>) {
        let value = if let Some(value) = value.deref().downcast_ref::<Self>() {
            value
        } else {
            unreachable!("Invalid replacement value for {}", type_name::<Self>())
        };

        if let Some(Key::Index(index)) = keys.pop_front() {
            let chars: Vec<char> = self.chars().collect();
            let chars = [
                &chars[..index],
                &value.chars().collect_vec(),
                &chars[index..],
            ]
            .concat();
            *self = chars.into_iter().collect();
        } else {
            invalid_keys!(keys)
        }
    }

    fn apply_remove(&mut self, keys: &mut Keys, items: usize) {
        if let Some(Key::Index(index)) = keys.pop_front() {
            let chars: Vec<char> = self.chars().collect();
            let chars = [&chars[..index], &chars[(index + items)..]].concat();
            *self = chars.into_iter().collect();
        } else {
            invalid_keys!(keys)
        }
    }

    fn apply_move(&mut self, from: &mut Keys, items: usize, to: &mut Keys) {
        if let (Some(Key::Index(from)), Some(Key::Index(to))) = (from.pop_front(), to.pop_front()) {
            let chars = self.chars().collect_vec();
            let chars = if from < to {
                [
                    &chars[..from],
                    &chars[(from + items)..(to + items)],
                    &chars[from..(from + items)],
                    &chars[(to + items)..],
                ]
            } else {
                [
                    &chars[..to],
                    &chars[from..(from + items)],
                    &chars[to..from],
                    &chars[(from + items)..],
                ]
            };
            *self = chars.concat().into_iter().collect();
        } else {
            invalid_keys!(from)
        }
    }

    fn apply_replace(&mut self, keys: &mut Keys, items: usize, value: &Box<dyn Any>) {
        let value = if let Some(value) = value.deref().downcast_ref::<Self>() {
            value
        } else {
            unreachable!("Invalid replacement value for {}", type_name::<Self>())
        };

        if keys.is_empty() {
            *self = value.clone();
        } else if let Some(Key::Index(index)) = keys.pop_front() {
            let chars: Vec<char> = self.chars().collect();
            let chars = [
                &chars[..index],
                &value.chars().collect_vec(),
                &chars[(index + items)..],
            ]
            .concat();
            *self = chars.into_iter().collect();
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        assert_json,
        patches::{apply_new, diff, equal},
    };
    use pretty_assertions::assert_eq;

    #[test]
    fn test_string() {
        let empty = "".to_string();
        let a = "1".to_string();
        let b = "123".to_string();
        let c = "a2b3".to_string();
        let d = "abcdef".to_string();
        let e = "adefbc".to_string();
        let f = "adbcfe".to_string();

        assert!(equal(&empty, &empty));
        assert!(equal(&a, &a));
        assert!(equal(&b, &b));
        assert!(equal(&c, &c));
        assert!(equal(&d, &d));

        // No diff

        assert_json!(diff(&empty, &empty), []);
        assert_json!(diff(&a, &a), []);
        assert_json!(diff(&d, &d), []);

        // Add

        let patch = diff(&empty, &a);
        assert_json!(
            patch,
            [{ "op": "add", "keys": [0], "value": "1" }]
        );
        assert_eq!(apply_new(&empty, &patch), a);

        let patch = diff(&empty, &d);
        assert_json!(
            patch,
            [{ "op": "add", "keys": [0], "value": "abcdef" }]
        );
        assert_eq!(apply_new(&empty, &patch), d);

        let patch = diff(&a, &b);
        assert_json!(
            patch,
            [{ "op": "add", "keys": [1], "value": "23" }]
        );
        assert_eq!(apply_new(&a, &patch), b);

        // Remove

        let patch = diff(&a, &empty);
        assert_json!(
            patch,
            [{ "op": "remove", "keys": [0], "items": 1 }]
        );

        let patch = diff(&d, &empty);
        assert_json!(
            patch,
            [{ "op": "remove", "keys": [0], "items": 6 }]
        );

        let patch = diff(&b, &a);
        assert_json!(
            patch,
            [{ "op": "remove", "keys": [1], "items": 2 }]
        );

        // Replace

        let patch = diff(&a, &c);
        assert_json!(
            patch,
            [{ "op": "replace", "keys": [0], "items": 1, "value": "a2b3" }]
        );
        assert_eq!(apply_new(&a, &patch), c);

        let patch = diff(&b, &d);
        assert_json!(
            patch,
            [{ "op": "replace", "keys": [0], "items": 3, "value": "abcdef" }]
        );
        assert_eq!(apply_new(&b, &patch), d);

        // Move

        let patch = diff(&d, &e);
        assert_json!(
            patch,
            [
                { "op": "move", "from": [1], "items": 2, "to": [4] },
            ]
        );
        assert_eq!(apply_new(&d, &patch), e);

        let patch = diff(&e, &d);
        assert_json!(
            patch,
            [
                { "op": "move", "from": [4], "items": 2, "to": [1] },
            ]
        );
        assert_eq!(apply_new(&e, &patch), d);

        // Mixed

        let patch = diff(&c, &d);
        assert_json!(
            patch,
            [
                { "op": "remove", "keys": [1], "items": 1 },
                { "op": "replace", "keys": [2], "items": 1, "value": "cdef" }
            ]
        );
        assert_eq!(apply_new(&c, &patch), d);

        let patch = diff(&d, &c);
        assert_json!(
            patch,
            [
                { "op": "add", "keys": [1], "value": "2" },
                { "op": "replace", "keys": [3], "items": 4, "value": "3" }
            ]
        );
        assert_eq!(apply_new(&d, &patch), c);

        let patch = diff(&d, &f);
        assert_json!(
            patch,
            [
                { "op": "add", "keys": [1], "value": "d" },
                { "op": "replace", "keys": [4], "items": 1, "value": "f" },
                { "op": "remove", "keys": [6], "items": 1 }
            ]
        );
        assert_eq!(apply_new(&d, &patch), f);
    }

    /// Test that works with Unicode graphemes (which are made
    /// up of multiple `char`s).
    #[test]
    fn test_unicode() {
        let a = "ä".to_string();
        let b = "ä1👍🏻2".to_string();
        let c = "1👍🏿2".to_string();

        let patch = diff(&a, &b);
        assert_json!(patch, [
            { "op": "add", "keys": [1], "value": "1👍🏻2" },
        ]);
        assert_eq!(apply_new(&a, &patch), b);

        let patch = diff(&b, &c);
        assert_json!(patch, [
            { "op": "remove", "keys": [0], "items": 1 },
            { "op": "replace", "keys": [2], "items": 1, "value": "🏿" },
        ]);
        assert_eq!(apply_new(&b, &patch), c);

        let patch = diff(&c, &b);
        assert_json!(patch, [
            { "op": "add", "keys": [0], "value": "ä" },
            { "op": "replace", "keys": [3], "items": 1, "value": "🏻" },
        ]);
        assert_eq!(apply_new(&c, &patch), b);
    }
}
<|endoftext|>


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

use serde_json::value::Value as Json;

use crate::context::{merge_json, Context};
use crate::error::RenderError;
use crate::json::path::Path;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Decorator, Evaluable, RenderContext, Renderable};
use crate::template::Template;

fn render_partial<'reg: 'rc, 'rc>(
    t: &'reg Template,
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    local_rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // partial context path
    if let Some(ref param_ctx) = d.param(0) {
        if let (Some(p), Some(block)) = (param_ctx.context_path(), local_rc.block_mut()) {
            *block.base_path_mut() = p.clone();
        }
    }

    // @partial-block
    if let Some(t) = d.template() {
        local_rc.set_partial("@partial-block".to_owned(), t);
    }

    let result = if d.hash().is_empty() {
        t.render(r, ctx, local_rc, out)
    } else {
        let hash_ctx = d
            .hash()
            .iter()
            .map(|(k, v)| (k, v.value()))
            .collect::<HashMap<&&str, &Json>>();
        let current_path = Path::current();
        let partial_context =
            merge_json(local_rc.evaluate2(ctx, &current_path)?.as_json(), &hash_ctx);
        let ctx = Context::wraps(&partial_context)?;
        let mut partial_rc = local_rc.new_for_block();
        t.render(r, &ctx, &mut partial_rc, out)
    };

    local_rc.remove_partial("@partial-block");

    result
}

pub fn expand_partial<'reg: 'rc, 'rc>(
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // try eval inline partials first
    if let Some(t) = d.template() {
        t.eval(r, ctx, rc)?;
    }

    let tname = d.name();
    if rc.is_current_template(tname) {
        return Err(RenderError::new("Cannot include self in >"));
    }

    let partial = rc.get_partial(tname);

    match partial {
        Some(t) => {
            let mut local_rc = rc.clone();
            render_partial(&t, d, r, ctx, &mut local_rc, out)?;
        }
        None => {
            if let Some(t) = r.get_template(tname).or_else(|| d.template()) {
                let mut local_rc = rc.clone();
                render_partial(t, d, r, ctx, &mut local_rc, out)?;
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use crate::context::Context;
    use crate::error::RenderError;
    use crate::output::Output;
    use crate::registry::Registry;
    use crate::render::{Helper, RenderContext};

    #[test]
    fn test() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t0", "{{> t1}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t1", "{{this}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t2", "{{#> t99}}not there{{/t99}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t3", "{{#*inline \"t31\"}}{{this}}{{/inline}}{{> t31}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t4",
                "{{#> t5}}{{#*inline \"nav\"}}navbar{{/inline}}{{/t5}}"
            )
            .is_ok());
        assert!(handlebars
            .register_template_string("t5", "include {{> nav}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t6", "{{> t1 a}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t7",
                "{{#*inline \"t71\"}}{{a}}{{/inline}}{{> t71 a=\"world\"}}"
            )
            .is_ok());
        assert!(handlebars.register_template_string("t8", "{{a}}").is_ok());
        assert!(handlebars
            .register_template_string("t9", "{{> t8 a=2}}")
            .is_ok());

        assert_eq!(handlebars.render("t0", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t2", &1).ok().unwrap(),
            "not there".to_string()
        );
        assert_eq!(handlebars.render("t3", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t4", &1).ok().unwrap(),
            "include navbar".to_string()
        );
        assert_eq!(
            handlebars
                .render("t6", &btreemap! {"a".to_string() => "2".to_string()})
                .ok()
                .unwrap(),
            "2".to_string()
        );
        assert_eq!(
            handlebars.render("t7", &1).ok().unwrap(),
            "world".to_string()
        );
        assert_eq!(handlebars.render("t9", &1).ok().unwrap(), "2".to_string());
    }

    #[test]
    fn test_include_partial_block() {
        let t0 = "hello {{> @partial-block}}";
        let t1 = "{{#> t0}}inner {{this}}{{/t0}}";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "hello inner true".to_string());
    }

    #[test]
    fn test_self_inclusion() {
        let t0 = "hello {{> t1}} {{> t0}}";
        let t1 = "some template";
        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t0", &true);
        assert!(r0.is_err());
    }

    #[test]
    fn test_issue_143() {
        let main_template = "one{{> two }}three{{> two }}";
        let two_partial = "--- two ---";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars
            .register_template_string("two", two_partial)
            .is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "one--- two ---three--- two ---");
    }

    #[test]
    fn test_hash_context_outscope() {
        let main_template = "In: {{> p a=2}} Out: {{a}}";
        let p_partial = "{{a}}";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars.register_template_string("p", p_partial).is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "In: 2 Out: ");
    }

    #[test]
    fn test_partial_context_hash() {
        let mut hbs = Registry::new();
        hbs.register_template_string("one", "This is a test. {{> two name=\"fred\" }}")
            .unwrap();
        hbs.register_template_string("two", "Lets test {{name}}")
            .unwrap();
        assert_eq!(
            "This is a test. Lets test fred",
            hbs.render("one", &0).unwrap()
        );
    }

    #[test]
    fn test_partial_subexpression_context_hash() {
        let mut hbs = Registry::new();
        hbs.register_template_string("one", "This is a test. {{> (x @root) name=\"fred\" }}")
            .unwrap();
        hbs.register_template_string("two", "Lets test {{name}}")
            .unwrap();

        hbs.register_helper(
            "x",
            Box::new(
                |_: &Helper<'_, '_>,
                 _: &Registry<'_>,
                 _: &Context,
                 _: &mut RenderContext<'_, '_>,
                 out: &mut dyn Output|
                 -> Result<(), RenderError> {
                    out.write("two")?;
                    Ok(())
                },
            ),
        );
        assert_eq!(
            "This is a test. Lets test fred",
            hbs.render("one", &0).unwrap()
        );
    }

    #[test]
    fn test_nested_partial_scope() {
        let t = "{{#*inline \"pp\"}}{{a}} {{b}}{{/inline}}{{#each c}}{{> pp a=2}}{{/each}}";
        let data = json!({"c": [{"b": true}, {"b": false}]});

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t", t).is_ok());
        let r0 = handlebars.render("t", &data);
        assert_eq!(r0.ok().unwrap(), "2 true2 false");
    }
}
<commit_msg>(refactor) use const for string literal<commit_after>use std::collections::HashMap;

use serde_json::value::Value as Json;

use crate::context::{merge_json, Context};
use crate::error::RenderError;
use crate::json::path::Path;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Decorator, Evaluable, RenderContext, Renderable};
use crate::template::Template;

const PARTIAL_BLOCK: &str = "@partial-block";

fn render_partial<'reg: 'rc, 'rc>(
    t: &'reg Template,
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    local_rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // partial context path
    if let Some(ref param_ctx) = d.param(0) {
        if let (Some(p), Some(block)) = (param_ctx.context_path(), local_rc.block_mut()) {
            *block.base_path_mut() = p.clone();
        }
    }

    // @partial-block
    if let Some(t) = d.template() {
        local_rc.set_partial(PARTIAL_BLOCK.to_owned(), t);
    }

    let result = if d.hash().is_empty() {
        t.render(r, ctx, local_rc, out)
    } else {
        let hash_ctx = d
            .hash()
            .iter()
            .map(|(k, v)| (k, v.value()))
            .collect::<HashMap<&&str, &Json>>();
        let current_path = Path::current();
        let partial_context =
            merge_json(local_rc.evaluate2(ctx, &current_path)?.as_json(), &hash_ctx);
        let ctx = Context::wraps(&partial_context)?;
        let mut partial_rc = local_rc.new_for_block();
        t.render(r, &ctx, &mut partial_rc, out)
    };

    local_rc.remove_partial(PARTIAL_BLOCK);

    result
}

pub fn expand_partial<'reg: 'rc, 'rc>(
    d: &Decorator<'reg, 'rc>,
    r: &'reg Registry<'reg>,
    ctx: &'rc Context,
    rc: &mut RenderContext<'reg, 'rc>,
    out: &mut dyn Output,
) -> Result<(), RenderError> {
    // try eval inline partials first
    if let Some(t) = d.template() {
        t.eval(r, ctx, rc)?;
    }

    let tname = d.name();
    if rc.is_current_template(tname) {
        return Err(RenderError::new("Cannot include self in >"));
    }

    let partial = rc.get_partial(tname);

    match partial {
        Some(t) => {
            let mut local_rc = rc.clone();
            render_partial(&t, d, r, ctx, &mut local_rc, out)?;
        }
        None => {
            if let Some(t) = r.get_template(tname).or_else(|| d.template()) {
                let mut local_rc = rc.clone();
                render_partial(t, d, r, ctx, &mut local_rc, out)?;
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use crate::context::Context;
    use crate::error::RenderError;
    use crate::output::Output;
    use crate::registry::Registry;
    use crate::render::{Helper, RenderContext};

    #[test]
    fn test() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t0", "{{> t1}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t1", "{{this}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t2", "{{#> t99}}not there{{/t99}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t3", "{{#*inline \"t31\"}}{{this}}{{/inline}}{{> t31}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t4",
                "{{#> t5}}{{#*inline \"nav\"}}navbar{{/inline}}{{/t5}}"
            )
            .is_ok());
        assert!(handlebars
            .register_template_string("t5", "include {{> nav}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t6", "{{> t1 a}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t7",
                "{{#*inline \"t71\"}}{{a}}{{/inline}}{{> t71 a=\"world\"}}"
            )
            .is_ok());
        assert!(handlebars.register_template_string("t8", "{{a}}").is_ok());
        assert!(handlebars
            .register_template_string("t9", "{{> t8 a=2}}")
            .is_ok());

        assert_eq!(handlebars.render("t0", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t2", &1).ok().unwrap(),
            "not there".to_string()
        );
        assert_eq!(handlebars.render("t3", &1).ok().unwrap(), "1".to_string());
        assert_eq!(
            handlebars.render("t4", &1).ok().unwrap(),
            "include navbar".to_string()
        );
        assert_eq!(
            handlebars
                .render("t6", &btreemap! {"a".to_string() => "2".to_string()})
                .ok()
                .unwrap(),
            "2".to_string()
        );
        assert_eq!(
            handlebars.render("t7", &1).ok().unwrap(),
            "world".to_string()
        );
        assert_eq!(handlebars.render("t9", &1).ok().unwrap(), "2".to_string());
    }

    #[test]
    fn test_include_partial_block() {
        let t0 = "hello {{> @partial-block}}";
        let t1 = "{{#> t0}}inner {{this}}{{/t0}}";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "hello inner true".to_string());
    }

    #[test]
    fn test_self_inclusion() {
        let t0 = "hello {{> t1}} {{> t0}}";
        let t1 = "some template";
        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let r0 = handlebars.render("t0", &true);
        assert!(r0.is_err());
    }

    #[test]
    fn test_issue_143() {
        let main_template = "one{{> two }}three{{> two }}";
        let two_partial = "--- two ---";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars
            .register_template_string("two", two_partial)
            .is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "one--- two ---three--- two ---");
    }

    #[test]
    fn test_hash_context_outscope() {
        let main_template = "In: {{> p a=2}} Out: {{a}}";
        let p_partial = "{{a}}";

        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("template", main_template)
            .is_ok());
        assert!(handlebars.register_template_string("p", p_partial).is_ok());

        let r0 = handlebars.render("template", &true);
        assert_eq!(r0.ok().unwrap(), "In: 2 Out: ");
    }

    #[test]
    fn test_partial_context_hash() {
        let mut hbs = Registry::new();
        hbs.register_template_string("one", "This is a test. {{> two name=\"fred\" }}")
            .unwrap();
        hbs.register_template_string("two", "Lets test {{name}}")
            .unwrap();
        assert_eq!(
            "This is a test. Lets test fred",
            hbs.render("one", &0).unwrap()
        );
    }

    #[test]
    fn test_partial_subexpression_context_hash() {
        let mut hbs = Registry::new();
        hbs.register_template_string("one", "This is a test. {{> (x @root) name=\"fred\" }}")
            .unwrap();
        hbs.register_template_string("two", "Lets test {{name}}")
            .unwrap();

        hbs.register_helper(
            "x",
            Box::new(
                |_: &Helper<'_, '_>,
                 _: &Registry<'_>,
                 _: &Context,
                 _: &mut RenderContext<'_, '_>,
                 out: &mut dyn Output|
                 -> Result<(), RenderError> {
                    out.write("two")?;
                    Ok(())
                },
            ),
        );
        assert_eq!(
            "This is a test. Lets test fred",
            hbs.render("one", &0).unwrap()
        );
    }

    #[test]
    fn test_nested_partial_scope() {
        let t = "{{#*inline \"pp\"}}{{a}} {{b}}{{/inline}}{{#each c}}{{> pp a=2}}{{/each}}";
        let data = json!({"c": [{"b": true}, {"b": false}]});

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t", t).is_ok());
        let r0 = handlebars.render("t", &data);
        assert_eq!(r0.ok().unwrap(), "2 true2 false");
    }
}
<|endoftext|>


<commit_before>use ::*;

#[get("/players")]
fn players<'a>() -> ContRes<'a> {
    let conn = lock_database();
    let mut stmt = conn.prepare("SELECT id, name from players ORDER BY name ASC").unwrap();

    let mut players = Vec::new();

    for p in stmt.query_map(&[], |row| (row.get::<_, i32>(0), row.get::<_, String>(1))).unwrap() {
        let (id, name) = p.unwrap();
        players.push(Named{id: id, name: name});
    }

    let mut context = create_context("players");
    context.add("players", &players);

    respond_page("players", context)
}

#[get("/player/<name>")]
fn player<'a>(mut name: String) -> ContRes<'a> {
    let conn = lock_database();
    let mut stmt =
        conn.prepare("SELECT (SELECT name FROM players p WHERE p.id = g.home_id) AS home, \
                      (SELECT name FROM players p WHERE p.id = g.away_id) AS away, home_score, \
                      away_score, ball_id, (SELECT img FROM balls b WHERE ball_id = b.id), \
                      (SELECT name FROM balls b WHERE ball_id = b.id), dato FROM games g \
                      where (home = ?1) or (away = ?1) \
                      AND dato > date('now', 'start of month') \
                      ORDER BY ID DESC")
            .unwrap();
    let games: Vec<_> = stmt.query_map(&[&name], |row| {
            PlayedGame {
                home: row.get(0),
                away: row.get(1),
                home_score: row.get(2),
                away_score: row.get(3),
                ball: row.get(5),
                ball_name: row.get(6),
                dato: row.get(7),
            }
        })
        .unwrap()
        .map(Result::unwrap)
        .collect();

    let mut context = create_context("players");

    context.add("games", &games);
    context.add("name", &name);
    respond_page("player", context)
}

#[get("/newplayer")]
fn newplayer<'a>() -> ContRes<'a> {
    respond_page("newplayer", create_context("players"))
}

#[derive(FromForm)]
struct NewPlayerQuery {
    name: String,
    secret: String,
    #[allow(dead_code)]
    submit: IgnoreField
}

#[post("/newplayer/submit", data = "<f>")]
fn submit_newplayer<'r>(f: Form<NewPlayerQuery>) -> Resp<'r> {
    let NewPlayerQuery{name, secret, ..} = f.into_inner();

    let mut context = create_context("players");
    if secret != CONFIG.secret {
        context.add("fejl", &"Det indtastede kodeord er forkert 💩");
    } else if name.is_empty() {
        context.add("fejl", &"Den indtastede spiller er ikke lovlig 😜");
    } else {
        let n = lock_database().execute("INSERT INTO players (name) VALUES (?)", &[&name]).unwrap();

        if n == 0 {
            context.add("fejl", &"Den indtastede spiller eksisterer allerede 💩");
        } else {
            reset_ratings();
            return Resp::red(Redirect::to("/"));
        }
    }
    Resp::cont(respond_page("newplayer_fejl", context))
}
<commit_msg>Removed unnecessary mut<commit_after>use ::*;

#[get("/players")]
fn players<'a>() -> ContRes<'a> {
    let conn = lock_database();
    let mut stmt = conn.prepare("SELECT id, name from players ORDER BY name ASC").unwrap();

    let mut players = Vec::new();

    for p in stmt.query_map(&[], |row| (row.get::<_, i32>(0), row.get::<_, String>(1))).unwrap() {
        let (id, name) = p.unwrap();
        players.push(Named{id: id, name: name});
    }

    let mut context = create_context("players");
    context.add("players", &players);

    respond_page("players", context)
}

#[get("/player/<name>")]
fn player<'a>(name: String) -> ContRes<'a> {
    let conn = lock_database();
    let mut stmt =
        conn.prepare("SELECT (SELECT name FROM players p WHERE p.id = g.home_id) AS home, \
                      (SELECT name FROM players p WHERE p.id = g.away_id) AS away, home_score, \
                      away_score, ball_id, (SELECT img FROM balls b WHERE ball_id = b.id), \
                      (SELECT name FROM balls b WHERE ball_id = b.id), dato FROM games g \
                      where (home = ?1) or (away = ?1) \
                      AND dato > date('now', 'start of month') \
                      ORDER BY ID DESC")
            .unwrap();
    let games: Vec<_> = stmt.query_map(&[&name], |row| {
            PlayedGame {
                home: row.get(0),
                away: row.get(1),
                home_score: row.get(2),
                away_score: row.get(3),
                ball: row.get(5),
                ball_name: row.get(6),
                dato: row.get(7),
            }
        })
        .unwrap()
        .map(Result::unwrap)
        .collect();

    let mut context = create_context("players");

    context.add("games", &games);
    context.add("name", &name);
    respond_page("player", context)
}

#[get("/newplayer")]
fn newplayer<'a>() -> ContRes<'a> {
    respond_page("newplayer", create_context("players"))
}

#[derive(FromForm)]
struct NewPlayerQuery {
    name: String,
    secret: String,
    #[allow(dead_code)]
    submit: IgnoreField
}

#[post("/newplayer/submit", data = "<f>")]
fn submit_newplayer<'r>(f: Form<NewPlayerQuery>) -> Resp<'r> {
    let NewPlayerQuery{name, secret, ..} = f.into_inner();

    let mut context = create_context("players");
    if secret != CONFIG.secret {
        context.add("fejl", &"Det indtastede kodeord er forkert 💩");
    } else if name.is_empty() {
        context.add("fejl", &"Den indtastede spiller er ikke lovlig 😜");
    } else {
        let n = lock_database().execute("INSERT INTO players (name) VALUES (?)", &[&name]).unwrap();

        if n == 0 {
            context.add("fejl", &"Den indtastede spiller eksisterer allerede 💩");
        } else {
            reset_ratings();
            return Resp::red(Redirect::to("/"));
        }
    }
    Resp::cont(respond_page("newplayer_fejl", context))
}
<|endoftext|>


<commit_before>
use token::{Token, TokenKind};
use error::{NcclError, ErrorKind};

use std::error::Error;

// ranked worst to best
enum Indent {
    Neither,
    Tabs,
    Spaces(u8),
}

pub struct Scanner {
    source: Vec<u8>,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: u64,
    indent: Indent,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source.into_bytes(),
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
            indent: Indent::Neither,
        }
    }

    pub fn scan_tokens(&mut self) -> Result<Vec<Token>, Vec<Box<Error>>> {
        let mut err: Vec<Box<Error>> = vec![];

        while !self.is_at_end() {
            self.start = self.current;
            let e = self.scan_token();
            if e.is_err() {
                err.push(Box::new(e.err().unwrap()));
            }
        }

        self.tokens.push(Token::new(TokenKind::EOF, "".into(), self.line));

        if !err.is_empty() {
            Err(err)
        } else {
            Ok(self.tokens.clone())
        }
    }

    fn scan_token(&mut self) -> Result<(), NcclError> {
        let mut error = Ok(());
        match self.advance() {
            b'#' => {
                while self.peek() != b'\n' && !self.is_at_end() {
                    self.advance();
                }
            },

            b' ' => {
                match self.indent {
                    Indent::Neither => {
                        let mut spaces = 0;
                        while self.peek() == b' ' && !self.is_at_end() {
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.indent = Indent::Spaces(spaces);
                        self.add_token(TokenKind::Indent);
                    }
                    Indent::Spaces(s) => {
                        let mut spaces = 0;
                        while spaces < s  && !self.is_at_end() {
                            if self.peek() != b' ' {
                                error = Err(NcclError::new(ErrorKind::IndentationError, &format!("Incorrect number of spaces: found {}, expected {}", spaces, s), self.line));
                            }
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Tabs => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected tabs, found spaces", self.line)); }
                }
            },

            b'\t' => {
                match self.indent {
                    Indent::Neither => {
                        self.add_token(TokenKind::Indent);
                        self.indent = Indent::Tabs;
                    },
                    Indent::Tabs => {
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Spaces(_) => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected spaces, found tabs", self.line)); }
                }
            },

            b'\n' => {
                self.add_token(TokenKind::Newline);
                self.line += 1;
                if self.peek() != b' ' && self.peek() != b'\t' && self.peek() != b'#' {
                    self.indent = Indent::Neither;
                }
            },

            b'\r' => {},

            b'"' => if let Err(e) = self.string() { error = Err(e); },

            _ => if let Err(e) = self.identifier() { error = Err(e); },
        }

        error
    }

    fn identifier(&mut self) -> Result<(), NcclError> {
        while self.peek() != b'\n' && self.peek() != b'\r' && !self.is_at_end() {
            self.advance();
        }

        let value = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn string(&mut self) -> Result<(), NcclError> {
        let mut value = String::new();
        while self.peek() != b'"' && !self.is_at_end() {
            if self.peek() == b'\n' {
                self.line += 1;
            }

            if self.peek() == b'\\' {
                self.advance();
                match self.peek() {
                    b'n' => {
                        value.push('\n');
                    },
                    b'r' => {
                        value.push('\r');
                    }
                    b'\\' => {
                        value.push('\\');
                    },
                    b'"' => {
                        value.push('"');
                    },
                    _ => return Err(NcclError::new(ErrorKind::ParseError, &format!("Unknown format code: {}", self.peek()), self.line))
                }
            } else {
                value.push(self.source[self.current] as char);
            }

            self.advance();
        }

        if self.is_at_end() {
            return Err(NcclError::new(ErrorKind::ParseError, "Unterminated string", self.line));
        }

        self.advance();

        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn add_token(&mut self, kind: TokenKind) {
        let text = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.tokens.push(Token::new(kind, text, self.line));
    }

    fn add_token_string(&mut self, kind: TokenKind, value: String) {
        self.tokens.push(Token::new(kind, value, self.line));
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> u8 {
        self.current += 1;
        self.source[self.current - 1]
    }

    fn reverse(&mut self) -> u8 {
        self.current -= 1;
        self.source[self.current]
    }

    fn peek(&mut self) -> u8 {
        if self.current >= self.source.len() {
            b'\0'
        } else {
            self.source[self.current]
        }
    }
}

<commit_msg>add c-style backslash - fix #1<commit_after>
use token::{Token, TokenKind};
use error::{NcclError, ErrorKind};

use std::error::Error;

// ranked worst to best
enum Indent {
    Neither,
    Tabs,
    Spaces(u8),
}

pub struct Scanner {
    source: Vec<u8>,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: u64,
    indent: Indent,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source.into_bytes(),
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
            indent: Indent::Neither,
        }
    }

    pub fn scan_tokens(&mut self) -> Result<Vec<Token>, Vec<Box<Error>>> {
        let mut err: Vec<Box<Error>> = vec![];

        while !self.is_at_end() {
            self.start = self.current;
            let e = self.scan_token();
            if e.is_err() {
                err.push(Box::new(e.err().unwrap()));
            }
        }

        self.tokens.push(Token::new(TokenKind::EOF, "".into(), self.line));

        if !err.is_empty() {
            Err(err)
        } else {
            Ok(self.tokens.clone())
        }
    }

    fn scan_token(&mut self) -> Result<(), NcclError> {
        let mut error = Ok(());
        match self.advance() {
            b'#' => {
                while self.peek() != b'\n' && !self.is_at_end() {
                    self.advance();
                }
            },

            b' ' => {
                match self.indent {
                    Indent::Neither => {
                        let mut spaces = 0;
                        while self.peek() == b' ' && !self.is_at_end() {
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.indent = Indent::Spaces(spaces);
                        self.add_token(TokenKind::Indent);
                    }
                    Indent::Spaces(s) => {
                        let mut spaces = 0;
                        while spaces < s  && !self.is_at_end() {
                            if self.peek() != b' ' {
                                error = Err(NcclError::new(ErrorKind::IndentationError, &format!("Incorrect number of spaces: found {}, expected {}", spaces, s), self.line));
                            }
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Tabs => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected tabs, found spaces", self.line)); }
                }
            },

            b'\t' => {
                match self.indent {
                    Indent::Neither => {
                        self.add_token(TokenKind::Indent);
                        self.indent = Indent::Tabs;
                    },
                    Indent::Tabs => {
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Spaces(_) => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected spaces, found tabs", self.line)); }
                }
            },

            b'\n' => {
                self.add_token(TokenKind::Newline);
                self.line += 1;
                if self.peek() != b' ' && self.peek() != b'\t' && self.peek() != b'#' {
                    self.indent = Indent::Neither;
                }
            },

            b'\r' => {},

            b'"' => if let Err(e) = self.string() { error = Err(e); },

            _ => if let Err(e) = self.identifier() { error = Err(e); },
        }

        error
    }

    fn identifier(&mut self) -> Result<(), NcclError> {
        while self.peek() != b'\n' && self.peek() != b'\r' && !self.is_at_end() {
            self.advance();
        }

        let value = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn string(&mut self) -> Result<(), NcclError> {
        let mut value = String::new();
        while self.peek() != b'"' && !self.is_at_end() {
            if self.peek() == b'\n' {
                self.line += 1;
            }

            if self.peek() == b'\\' {
                self.advance();
                match self.peek() {
                    b'n' => {
                        value.push('\n');
                    },
                    b'r' => {
                        value.push('\r');
                    }
                    b'\\' => {
                        value.push('\\');
                    },
                    b'"' => {
                        value.push('"');
                    },
                    b'\n' => {
                        self.advance();
                        println!("newline");
                        while self.peek() == b' ' || self.peek() == b'\t' {
                            println!("advance");
                            self.advance();
                        }
                        self.reverse();
                    },
                    _ => return Err(NcclError::new(ErrorKind::ParseError, &format!("Unknown format code: {}", self.peek()), self.line))
                }
            } else {
                value.push(self.source[self.current] as char);
            }

            self.advance();
        }

        if self.is_at_end() {
            return Err(NcclError::new(ErrorKind::ParseError, "Unterminated string", self.line));
        }

        self.advance();

        self.add_token_string(TokenKind::Value, value);

        Ok(())
    }

    fn add_token(&mut self, kind: TokenKind) {
        let text = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.tokens.push(Token::new(kind, text, self.line));
    }

    fn add_token_string(&mut self, kind: TokenKind, value: String) {
        self.tokens.push(Token::new(kind, value, self.line));
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> u8 {
        self.current += 1;
        self.source[self.current - 1]
    }

    fn reverse(&mut self) -> u8 {
        self.current -= 1;
        self.source[self.current]
    }

    fn peek(&mut self) -> u8 {
        if self.current >= self.source.len() {
            b'\0'
        } else {
            self.source[self.current]
        }
    }
}

<|endoftext|>


<commit_before>extern crate cdrs;
use cdrs::authenticators::{Authenticator, PasswordAuthenticator};

#[test]
fn test_password_authenticator_trait_impl() {
    let authenticator = PasswordAuthenticator::new("a", "a");
    let _ = authenticator_tester(Box::new(authenticator));
}

#[test]
fn test_password_authenticator_new() {
    PasswordAuthenticator::new("foo", "bar");
}

#[test]
fn test_password_authenticator_get_cassandra_name() {
    let auth = PasswordAuthenticator::new("foo", "bar");
    assert_eq!(auth.get_cassandra_name(), "org.apache.cassandra.auth.PasswordAuthenticator");
}

#[test]
fn test_password_authenticator_get_auth_token() {
    let auth = PasswordAuthenticator::new("foo", "bar");
    let mut expected_token = vec![0];
    expected_token.extend_from_slice("foo".as_bytes());
    expected_token.push(0);
    expected_token.extend_from_slice("bar".as_bytes());

    assert_eq!(auth.get_auth_token().into_plain(), expected_token);
}

fn authenticator_tester<A: Authenticator>(_authenticator: Box<A>) {}
<commit_msg>added test cases<commit_after>extern crate cdrs;
use cdrs::authenticators::{Authenticator, PasswordAuthenticator,AuthenticatorNone};

#[test]
fn test_password_authenticator_trait_impl() {
    let authenticator = PasswordAuthenticator::new("a", "a");
    let _ = authenticator_tester(Box::new(authenticator));
}

#[test]
fn test_password_authenticator_new() {
    PasswordAuthenticator::new("foo", "bar");
}

#[test]
fn test_password_authenticator_get_cassandra_name() {
    let auth = PasswordAuthenticator::new("foo", "bar");
    assert_eq!(auth.get_cassandra_name(), "org.apache.cassandra.auth.PasswordAuthenticator");
}

#[test]
fn test_password_authenticator_get_auth_token() {
    let auth = PasswordAuthenticator::new("foo", "bar");
    let mut expected_token = vec![0];
    expected_token.extend_from_slice("foo".as_bytes());
    expected_token.push(0);
    expected_token.extend_from_slice("bar".as_bytes());

    assert_eq!(auth.get_auth_token().into_plain(), expected_token);
}

#[test]
fn test_authenticator_none_get_cassandra_name() {
    let auth = AuthenticatorNone;
    assert_eq!(auth.get_cassandra_name(), "NONE");
    assert_eq!(auth.get_auth_token().into_plain(), vec![0]);
}

fn authenticator_tester<A: Authenticator>(_authenticator: Box<A>) {}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::compartments::InCompartment;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::XRBinding::XRSessionMode;
use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit;
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateMethods;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XREnvironmentBlendMode;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCallback;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRSessionMethods;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::xrframe::XRFrame;
use crate::dom::xrinputsource::XRInputSource;
use crate::dom::xrlayer::XRLayer;
use crate::dom::xrreferencespace::XRReferenceSpace;
use crate::dom::xrrenderstate::XRRenderState;
use crate::dom::xrspace::XRSpace;
use crate::dom::xrwebgllayer::XRWebGLLayer;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;
use euclid::TypedRigidTransform3D;
use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER;
use profile_traits::ipc;
use std::cell::Cell;
use std::mem;
use std::rc::Rc;
use webxr_api::{self, Frame, Session};

#[dom_struct]
pub struct XRSession {
    eventtarget: EventTarget,
    base_layer: MutNullableDom<XRLayer>,
    blend_mode: XREnvironmentBlendMode,
    viewer_space: MutNullableDom<XRSpace>,
    #[ignore_malloc_size_of = "defined in webxr"]
    session: DomRefCell<Session>,
    frame_requested: Cell<bool>,
    pending_render_state: MutNullableDom<XRRenderState>,
    active_render_state: MutDom<XRRenderState>,

    next_raf_id: Cell<i32>,
    #[ignore_malloc_size_of = "closures are hard"]
    raf_callback_list: DomRefCell<Vec<(i32, Option<Rc<XRFrameRequestCallback>>)>>,
    #[ignore_malloc_size_of = "defined in ipc-channel"]
    raf_sender: DomRefCell<Option<IpcSender<(f64, Frame)>>>,
    input_sources: DomRefCell<Vec<Dom<XRInputSource>>>,
}

impl XRSession {
    fn new_inherited(session: Session, render_state: &XRRenderState) -> XRSession {
        XRSession {
            eventtarget: EventTarget::new_inherited(),
            base_layer: Default::default(),
            // we don't yet support any AR devices
            blend_mode: XREnvironmentBlendMode::Opaque,
            viewer_space: Default::default(),
            session: DomRefCell::new(session),
            frame_requested: Cell::new(false),
            pending_render_state: MutNullableDom::new(None),
            active_render_state: MutDom::new(render_state),

            next_raf_id: Cell::new(0),
            raf_callback_list: DomRefCell::new(vec![]),
            raf_sender: DomRefCell::new(None),
            input_sources: DomRefCell::new(vec![]),
        }
    }

    pub fn new(global: &GlobalScope, session: Session) -> DomRoot<XRSession> {
        let render_state = XRRenderState::new(global, 0.1, 1000.0, None);
        let ret = reflect_dom_object(
            Box::new(XRSession::new_inherited(session, &render_state)),
            global,
            XRSessionBinding::Wrap,
        );
        {
            let mut input_sources = ret.input_sources.borrow_mut();
            for info in ret.session.borrow().initial_inputs() {
                // XXXManishearth we should be able to listen for updates
                // to the input sources
                let input = XRInputSource::new(global, &ret, *info);
                input_sources.push(Dom::from_ref(&input));
            }
        }
        ret
    }

    pub fn with_session<F: FnOnce(&Session)>(&self, with: F) {
        let session = self.session.borrow();
        with(&session)
    }

    /// https://immersive-web.github.io/webxr/#xr-animation-frame
    fn raf_callback(&self, (time, frame): (f64, Frame)) {
        // Step 1
        if let Some(pending) = self.pending_render_state.take() {
            // https://immersive-web.github.io/webxr/#apply-the-pending-render-state
            // (Steps 1-4 are implicit)
            // Step 5
            self.active_render_state.set(&pending);
            // Step 6-7: XXXManishearth handle inlineVerticalFieldOfView

            // XXXManishearth handle inline sessions and composition disabled flag
            let layer = pending.GetBaseLayer();
            if let Some(layer) = layer {
                let mut session = self.session.borrow_mut();
                if let Some(layer) = layer.downcast::<XRWebGLLayer>() {
                    session.update_webgl_external_image_api(
                        layer.Context().webgl_sender().webxr_external_image_api(),
                    );
                } else {
                    error!("updateRenderState() called with unknown layer type")
                }
            }
        }

        // Step 2
        if self.active_render_state.get().GetBaseLayer().is_none() {
            return;
        }

        // Step 3: XXXManishearth handle inline session

        // Step 4-5
        let mut callbacks = mem::replace(&mut *self.raf_callback_list.borrow_mut(), vec![]);

        let frame = XRFrame::new(&self.global(), self, frame);
        // Step 6,7
        frame.set_active(true);
        frame.set_animation_frame(true);

        // Step 8
        for (_, callback) in callbacks.drain(..) {
            if let Some(callback) = callback {
                let _ = callback.Call__(Finite::wrap(time), &frame, ExceptionHandling::Report);
            }
        }

        // Step 9: XXXManishearth unset `active` bool on `frame`
        self.session.borrow_mut().render_animation_frame();
    }
}

impl XRSessionMethods for XRSession {
    /// https://immersive-web.github.io/webxr/#dom-xrsession-mode
    fn Mode(&self) -> XRSessionMode {
        XRSessionMode::Immersive_vr
    }

    // https://immersive-web.github.io/webxr/#dom-xrsession-renderstate
    fn RenderState(&self) -> DomRoot<XRRenderState> {
        self.active_render_state.get()
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-updaterenderstate
    fn UpdateRenderState(&self, init: &XRRenderStateInit, _: InCompartment) {
        // XXXManishearth various checks:
        // If session’s ended value is true, throw an InvalidStateError and abort these steps
        // If newState’s baseLayer's was created with an XRSession other than session,
        // throw an InvalidStateError and abort these steps
        // If newState’s inlineVerticalFieldOfView is set and session is an
        // immersive session, throw an InvalidStateError and abort these steps.

        let pending = self
            .pending_render_state
            .or_init(|| self.active_render_state.get().clone_object());
        if let Some(near) = init.depthNear {
            pending.set_depth_near(*near);
        }
        if let Some(far) = init.depthFar {
            pending.set_depth_far(*far);
        }
        if let Some(ref layer) = init.baseLayer {
            pending.set_layer(Some(&layer))
        }
        // XXXManishearth handle inlineVerticalFieldOfView
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe
    fn RequestAnimationFrame(&self, callback: Rc<XRFrameRequestCallback>) -> i32 {
        #[derive(serde::Serialize, serde::Deserialize)]
        pub struct FrameCallback {
            sender: IpcSender<(f64, Frame)>,
        }

        #[typetag::serde]
        impl webxr_api::FrameRequestCallback for FrameCallback {
            fn callback(&mut self, time: f64, frame: Frame) {
                let _ = self.sender.send((time, frame));
            }
        }

        // queue up RAF callback, obtain ID
        let raf_id = self.next_raf_id.get();
        self.next_raf_id.set(raf_id + 1);
        self.raf_callback_list
            .borrow_mut()
            .push((raf_id, Some(callback)));

        // set up listener for response, if necessary
        if self.raf_sender.borrow().is_none() {
            let this = Trusted::new(self);
            let global = self.global();
            let window = global.as_window();
            let (task_source, canceller) = window
                .task_manager()
                .dom_manipulation_task_source_with_canceller();
            let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
            *self.raf_sender.borrow_mut() = Some(sender);
            ROUTER.add_route(
                receiver.to_opaque(),
                Box::new(move |message| {
                    let this = this.clone();
                    let _ = task_source.queue_with_canceller(
                        task!(xr_raf_callback: move || {
                            this.root().raf_callback(message.to().unwrap());
                        }),
                        &canceller,
                    );
                }),
            );
        }
        let sender = self.raf_sender.borrow().clone().unwrap();

        // request animation frame
        self.session
            .borrow_mut()
            .request_animation_frame(FrameCallback { sender });

        raf_id
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-cancelanimationframe
    fn CancelAnimationFrame(&self, frame: i32) {
        let mut list = self.raf_callback_list.borrow_mut();
        if let Some(pair) = list.iter_mut().find(|pair| pair.0 == frame) {
            pair.1 = None;
        }
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-environmentblendmode
    fn EnvironmentBlendMode(&self) -> XREnvironmentBlendMode {
        self.blend_mode
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-requestreferencespace
    fn RequestReferenceSpace(&self, ty: XRReferenceSpaceType, comp: InCompartment) -> Rc<Promise> {
        let p = Promise::new_in_current_compartment(&self.global(), comp);

        // https://immersive-web.github.io/webxr/#create-a-reference-space

        // XXXManishearth reject based on session type
        // https://github.com/immersive-web/webxr/blob/master/spatial-tracking-explainer.md#practical-usage-guidelines

        match ty {
            XRReferenceSpaceType::Bounded_floor | XRReferenceSpaceType::Unbounded => {
                // XXXManishearth eventually support these
                p.reject_error(Error::NotSupported)
            },
            ty => {
                p.resolve_native(&XRReferenceSpace::new(&self.global(), self, ty));
            },
        }

        p
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-getinputsources
    fn GetInputSources(&self) -> Vec<DomRoot<XRInputSource>> {
        self.input_sources
            .borrow()
            .iter()
            .map(|x| DomRoot::from_ref(&**x))
            .collect()
    }
}

#[derive(Clone, Copy, Debug)]
pub struct ApiSpace;
// The pose of an object in native-space. Should never be exposed.
pub type ApiPose = TypedRigidTransform3D<f32, ApiSpace, webxr_api::Native>;
// The pose of the viewer in some api-space.
pub type ApiViewerPose = TypedRigidTransform3D<f32, webxr_api::Viewer, ApiSpace>;
// A transform between objects in some API-space
pub type ApiRigidTransform = TypedRigidTransform3D<f32, ApiSpace, ApiSpace>;

#[allow(unsafe_code)]
pub fn cast_transform<T, U, V, W>(
    transform: TypedRigidTransform3D<f32, T, U>,
) -> TypedRigidTransform3D<f32, V, W> {
    unsafe { mem::transmute(transform) }
}
<commit_msg>Auto merge of #23785 - asajeffrey:script-webxr-dirty-canvas, r=Manishearth<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::compartments::InCompartment;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::XRBinding::XRSessionMode;
use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit;
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateMethods;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XREnvironmentBlendMode;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCallback;
use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRSessionMethods;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::node::Node;
use crate::dom::node::NodeDamage;
use crate::dom::promise::Promise;
use crate::dom::xrframe::XRFrame;
use crate::dom::xrinputsource::XRInputSource;
use crate::dom::xrlayer::XRLayer;
use crate::dom::xrreferencespace::XRReferenceSpace;
use crate::dom::xrrenderstate::XRRenderState;
use crate::dom::xrspace::XRSpace;
use crate::dom::xrwebgllayer::XRWebGLLayer;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;
use euclid::TypedRigidTransform3D;
use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER;
use profile_traits::ipc;
use std::cell::Cell;
use std::mem;
use std::rc::Rc;
use webxr_api::{self, Frame, Session};

#[dom_struct]
pub struct XRSession {
    eventtarget: EventTarget,
    base_layer: MutNullableDom<XRLayer>,
    blend_mode: XREnvironmentBlendMode,
    viewer_space: MutNullableDom<XRSpace>,
    #[ignore_malloc_size_of = "defined in webxr"]
    session: DomRefCell<Session>,
    frame_requested: Cell<bool>,
    pending_render_state: MutNullableDom<XRRenderState>,
    active_render_state: MutDom<XRRenderState>,

    next_raf_id: Cell<i32>,
    #[ignore_malloc_size_of = "closures are hard"]
    raf_callback_list: DomRefCell<Vec<(i32, Option<Rc<XRFrameRequestCallback>>)>>,
    #[ignore_malloc_size_of = "defined in ipc-channel"]
    raf_sender: DomRefCell<Option<IpcSender<(f64, Frame)>>>,
    input_sources: DomRefCell<Vec<Dom<XRInputSource>>>,
}

impl XRSession {
    fn new_inherited(session: Session, render_state: &XRRenderState) -> XRSession {
        XRSession {
            eventtarget: EventTarget::new_inherited(),
            base_layer: Default::default(),
            // we don't yet support any AR devices
            blend_mode: XREnvironmentBlendMode::Opaque,
            viewer_space: Default::default(),
            session: DomRefCell::new(session),
            frame_requested: Cell::new(false),
            pending_render_state: MutNullableDom::new(None),
            active_render_state: MutDom::new(render_state),

            next_raf_id: Cell::new(0),
            raf_callback_list: DomRefCell::new(vec![]),
            raf_sender: DomRefCell::new(None),
            input_sources: DomRefCell::new(vec![]),
        }
    }

    pub fn new(global: &GlobalScope, session: Session) -> DomRoot<XRSession> {
        let render_state = XRRenderState::new(global, 0.1, 1000.0, None);
        let ret = reflect_dom_object(
            Box::new(XRSession::new_inherited(session, &render_state)),
            global,
            XRSessionBinding::Wrap,
        );
        {
            let mut input_sources = ret.input_sources.borrow_mut();
            for info in ret.session.borrow().initial_inputs() {
                // XXXManishearth we should be able to listen for updates
                // to the input sources
                let input = XRInputSource::new(global, &ret, *info);
                input_sources.push(Dom::from_ref(&input));
            }
        }
        ret
    }

    pub fn with_session<F: FnOnce(&Session)>(&self, with: F) {
        let session = self.session.borrow();
        with(&session)
    }

    /// https://immersive-web.github.io/webxr/#xr-animation-frame
    fn raf_callback(&self, (time, frame): (f64, Frame)) {
        // Step 1
        if let Some(pending) = self.pending_render_state.take() {
            // https://immersive-web.github.io/webxr/#apply-the-pending-render-state
            // (Steps 1-4 are implicit)
            // Step 5
            self.active_render_state.set(&pending);
            // Step 6-7: XXXManishearth handle inlineVerticalFieldOfView

            // XXXManishearth handle inline sessions and composition disabled flag
            let layer = pending.GetBaseLayer();
            if let Some(layer) = layer {
                let mut session = self.session.borrow_mut();
                if let Some(layer) = layer.downcast::<XRWebGLLayer>() {
                    session.update_webgl_external_image_api(
                        layer.Context().webgl_sender().webxr_external_image_api(),
                    );
                } else {
                    error!("updateRenderState() called with unknown layer type")
                }
            }
        }

        // Step 2
        let base_layer = match self.active_render_state.get().GetBaseLayer() {
            Some(layer) => layer,
            None => return,
        };

        // Step 3: XXXManishearth handle inline session

        // Step 4-5
        let mut callbacks = mem::replace(&mut *self.raf_callback_list.borrow_mut(), vec![]);

        let frame = XRFrame::new(&self.global(), self, frame);
        // Step 6,7
        frame.set_active(true);
        frame.set_animation_frame(true);

        // Step 8
        for (_, callback) in callbacks.drain(..) {
            if let Some(callback) = callback {
                let _ = callback.Call__(Finite::wrap(time), &frame, ExceptionHandling::Report);
            }
        }

        // Step 9: XXXManishearth unset `active` bool on `frame`
        self.session.borrow_mut().render_animation_frame();

        // If the canvas element is attached to the DOM, it is now dirty,
        // and we need to trigger a reflow.
        if let Some(webgl_layer) = base_layer.downcast::<XRWebGLLayer>() {
            webgl_layer
                .Context()
                .Canvas()
                .upcast::<Node>()
                .dirty(NodeDamage::OtherNodeDamage);
        }
    }
}

impl XRSessionMethods for XRSession {
    /// https://immersive-web.github.io/webxr/#dom-xrsession-mode
    fn Mode(&self) -> XRSessionMode {
        XRSessionMode::Immersive_vr
    }

    // https://immersive-web.github.io/webxr/#dom-xrsession-renderstate
    fn RenderState(&self) -> DomRoot<XRRenderState> {
        self.active_render_state.get()
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-updaterenderstate
    fn UpdateRenderState(&self, init: &XRRenderStateInit, _: InCompartment) {
        // XXXManishearth various checks:
        // If session’s ended value is true, throw an InvalidStateError and abort these steps
        // If newState’s baseLayer's was created with an XRSession other than session,
        // throw an InvalidStateError and abort these steps
        // If newState’s inlineVerticalFieldOfView is set and session is an
        // immersive session, throw an InvalidStateError and abort these steps.

        let pending = self
            .pending_render_state
            .or_init(|| self.active_render_state.get().clone_object());
        if let Some(near) = init.depthNear {
            pending.set_depth_near(*near);
        }
        if let Some(far) = init.depthFar {
            pending.set_depth_far(*far);
        }
        if let Some(ref layer) = init.baseLayer {
            pending.set_layer(Some(&layer))
        }
        // XXXManishearth handle inlineVerticalFieldOfView
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe
    fn RequestAnimationFrame(&self, callback: Rc<XRFrameRequestCallback>) -> i32 {
        #[derive(serde::Serialize, serde::Deserialize)]
        pub struct FrameCallback {
            sender: IpcSender<(f64, Frame)>,
        }

        #[typetag::serde]
        impl webxr_api::FrameRequestCallback for FrameCallback {
            fn callback(&mut self, time: f64, frame: Frame) {
                let _ = self.sender.send((time, frame));
            }
        }

        // queue up RAF callback, obtain ID
        let raf_id = self.next_raf_id.get();
        self.next_raf_id.set(raf_id + 1);
        self.raf_callback_list
            .borrow_mut()
            .push((raf_id, Some(callback)));

        // set up listener for response, if necessary
        if self.raf_sender.borrow().is_none() {
            let this = Trusted::new(self);
            let global = self.global();
            let window = global.as_window();
            let (task_source, canceller) = window
                .task_manager()
                .dom_manipulation_task_source_with_canceller();
            let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
            *self.raf_sender.borrow_mut() = Some(sender);
            ROUTER.add_route(
                receiver.to_opaque(),
                Box::new(move |message| {
                    let this = this.clone();
                    let _ = task_source.queue_with_canceller(
                        task!(xr_raf_callback: move || {
                            this.root().raf_callback(message.to().unwrap());
                        }),
                        &canceller,
                    );
                }),
            );
        }
        let sender = self.raf_sender.borrow().clone().unwrap();

        // request animation frame
        self.session
            .borrow_mut()
            .request_animation_frame(FrameCallback { sender });

        raf_id
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-cancelanimationframe
    fn CancelAnimationFrame(&self, frame: i32) {
        let mut list = self.raf_callback_list.borrow_mut();
        if let Some(pair) = list.iter_mut().find(|pair| pair.0 == frame) {
            pair.1 = None;
        }
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-environmentblendmode
    fn EnvironmentBlendMode(&self) -> XREnvironmentBlendMode {
        self.blend_mode
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-requestreferencespace
    fn RequestReferenceSpace(&self, ty: XRReferenceSpaceType, comp: InCompartment) -> Rc<Promise> {
        let p = Promise::new_in_current_compartment(&self.global(), comp);

        // https://immersive-web.github.io/webxr/#create-a-reference-space

        // XXXManishearth reject based on session type
        // https://github.com/immersive-web/webxr/blob/master/spatial-tracking-explainer.md#practical-usage-guidelines

        match ty {
            XRReferenceSpaceType::Bounded_floor | XRReferenceSpaceType::Unbounded => {
                // XXXManishearth eventually support these
                p.reject_error(Error::NotSupported)
            },
            ty => {
                p.resolve_native(&XRReferenceSpace::new(&self.global(), self, ty));
            },
        }

        p
    }

    /// https://immersive-web.github.io/webxr/#dom-xrsession-getinputsources
    fn GetInputSources(&self) -> Vec<DomRoot<XRInputSource>> {
        self.input_sources
            .borrow()
            .iter()
            .map(|x| DomRoot::from_ref(&**x))
            .collect()
    }
}

#[derive(Clone, Copy, Debug)]
pub struct ApiSpace;
// The pose of an object in native-space. Should never be exposed.
pub type ApiPose = TypedRigidTransform3D<f32, ApiSpace, webxr_api::Native>;
// The pose of the viewer in some api-space.
pub type ApiViewerPose = TypedRigidTransform3D<f32, webxr_api::Viewer, ApiSpace>;
// A transform between objects in some API-space
pub type ApiRigidTransform = TypedRigidTransform3D<f32, ApiSpace, ApiSpace>;

#[allow(unsafe_code)]
pub fn cast_transform<T, U, V, W>(
    transform: TypedRigidTransform3D<f32, T, U>,
) -> TypedRigidTransform3D<f32, V, W> {
    unsafe { mem::transmute(transform) }
}
<|endoftext|>