<commit_before>// run-pass

#![allow(non_camel_case_types)]
#![allow(dead_code)]
#![feature(never_type)]

use std::mem::size_of;
use std::num::NonZeroU8;

struct t {a: u8, b: i8}
struct u {a: u8, b: i8, c: u8}
struct v {a: u8, b: i8, c: v2, d: u32}
struct v2 {u: char, v: u8}
struct w {a: isize, b: ()}
struct x {a: isize, b: (), c: ()}
struct y {x: isize}

enum e1 {
    a(u8, u32), b(u32), c
}
enum e2 {
    a(u32), b
}

#[repr(C, u8)]
enum e3 {
    a([u16; 0], u8), b
}

struct ReorderedStruct {
    a: u8,
    b: u16,
    c: u8
}

enum ReorderedEnum {
    A(u8, u16, u8),
    B(u8, u16, u8),
}

enum ReorderedEnum2 {
    A(u8, u32, u8),
    B(u16, u8, u16, u8),

    // 0x100 niche variants.
    _00, _01, _02, _03, _04, _05, _06, _07, _08, _09, _0A, _0B, _0C, _0D, _0E, _0F,
    _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _1A, _1B, _1C, _1D, _1E, _1F,
    _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _2A, _2B, _2C, _2D, _2E, _2F,
    _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _3A, _3B, _3C, _3D, _3E, _3F,
    _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _4A, _4B, _4C, _4D, _4E, _4F,
    _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _5A, _5B, _5C, _5D, _5E, _5F,
    _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _6A, _6B, _6C, _6D, _6E, _6F,
    _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _7A, _7B, _7C, _7D, _7E, _7F,
    _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _8A, _8B, _8C, _8D, _8E, _8F,
    _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _9A, _9B, _9C, _9D, _9E, _9F,
    _A0, _A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8, _A9, _AA, _AB, _AC, _AD, _AE, _AF,
    _B0, _B1, _B2, _B3, _B4, _B5, _B6, _B7, _B8, _B9, _BA, _BB, _BC, _BD, _BE, _BF,
    _C0, _C1, _C2, _C3, _C4, _C5, _C6, _C7, _C8, _C9, _CA, _CB, _CC, _CD, _CE, _CF,
    _D0, _D1, _D2, _D3, _D4, _D5, _D6, _D7, _D8, _D9, _DA, _DB, _DC, _DD, _DE, _DF,
    _E0, _E1, _E2, _E3, _E4, _E5, _E6, _E7, _E8, _E9, _EA, _EB, _EC, _ED, _EE, _EF,
    _F0, _F1, _F2, _F3, _F4, _F5, _F6, _F7, _F8, _F9, _FA, _FB, _FC, _FD, _FE, _FF,
}

enum EnumEmpty {}

enum EnumSingle1 {
    A,
}

enum EnumSingle2 {
    A = 42 as isize,
}

enum EnumSingle3 {
    A,
    B(!),
}

#[repr(u8)]
enum EnumSingle4 {
    A,
}

#[repr(u8)]
enum EnumSingle5 {
    A = 42 as u8,
}

enum EnumWithMaybeUninhabitedVariant<T> {
    A(&'static ()),
    B(&'static (), T),
    C,
}

enum NicheFilledEnumWithAbsentVariant {
    A(&'static ()),
    B((), !),
    C,
}

enum Option2<A, B> {
    Some(A, B),
    None
}

pub enum CanBeNicheFilledButShouldnt {
    A(NonZeroU8, u32),
    B
}
pub enum AlwaysTagged {
    A(u8, u32),
    B
}

pub fn main() {
    assert_eq!(size_of::<u8>(), 1 as usize);
    assert_eq!(size_of::<u32>(), 4 as usize);
    assert_eq!(size_of::<char>(), 4 as usize);
    assert_eq!(size_of::<i8>(), 1 as usize);
    assert_eq!(size_of::<i32>(), 4 as usize);
    assert_eq!(size_of::<t>(), 2 as usize);
    assert_eq!(size_of::<u>(), 3 as usize);
    // Alignment causes padding before the char and the u32.

    assert_eq!(size_of::<v>(),
                16 as usize);
    assert_eq!(size_of::<isize>(), size_of::<usize>());
    assert_eq!(size_of::<w>(), size_of::<isize>());
    assert_eq!(size_of::<x>(), size_of::<isize>());
    assert_eq!(size_of::<isize>(), size_of::<y>());

    // Make sure enum types are the appropriate size, mostly
    // around ensuring alignment is handled properly

    assert_eq!(size_of::<e1>(), 8 as usize);
    assert_eq!(size_of::<e2>(), 8 as usize);
    assert_eq!(size_of::<e3>(), 4 as usize);
    assert_eq!(size_of::<ReorderedStruct>(), 4);
    assert_eq!(size_of::<ReorderedEnum>(), 6);
    assert_eq!(size_of::<ReorderedEnum2>(), 8);

    assert_eq!(size_of::<EnumEmpty>(), 0);
    assert_eq!(size_of::<EnumSingle1>(), 0);
    assert_eq!(size_of::<EnumSingle2>(), 0);
    assert_eq!(size_of::<EnumSingle3>(), 0);
    assert_eq!(size_of::<EnumSingle4>(), 1);
    assert_eq!(size_of::<EnumSingle5>(), 1);

    assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<!>>(),
               size_of::<EnumWithMaybeUninhabitedVariant<()>>());
    assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>());

    assert_eq!(size_of::<Option<Option<(bool, &())>>>(), size_of::<(bool, &())>());
    assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>());
    assert_eq!(size_of::<Option<Option2<bool, &()>>>(), size_of::<(bool, &())>());
    assert_eq!(size_of::<Option<Option2<&(), bool>>>(), size_of::<(bool, &())>());

    assert_eq!(size_of::<CanBeNicheFilledButShouldnt>(), 8);
    assert_eq!(size_of::<Option<CanBeNicheFilledButShouldnt>>(), 8);
    assert_eq!(size_of::<Option<Option<CanBeNicheFilledButShouldnt>>>(), 8);
    assert_eq!(size_of::<AlwaysTagged>(), 8);
    assert_eq!(size_of::<Option<AlwaysTagged>>(), 8);
    assert_eq!(size_of::<Option<Option<AlwaysTagged>>>(), 8);
}
<commit_msg>document test changes<commit_after>// run-pass

#![allow(non_camel_case_types)]
#![allow(dead_code)]
#![feature(never_type)]

use std::mem::size_of;
use std::num::NonZeroU8;

struct t {a: u8, b: i8}
struct u {a: u8, b: i8, c: u8}
struct v {a: u8, b: i8, c: v2, d: u32}
struct v2 {u: char, v: u8}
struct w {a: isize, b: ()}
struct x {a: isize, b: (), c: ()}
struct y {x: isize}

enum e1 {
    a(u8, u32), b(u32), c
}
enum e2 {
    a(u32), b
}

#[repr(C, u8)]
enum e3 {
    a([u16; 0], u8), b
}

struct ReorderedStruct {
    a: u8,
    b: u16,
    c: u8
}

enum ReorderedEnum {
    A(u8, u16, u8),
    B(u8, u16, u8),
}

enum ReorderedEnum2 {
    A(u8, u32, u8),
    B(u16, u8, u16, u8),

    // 0x100 niche variants.
    _00, _01, _02, _03, _04, _05, _06, _07, _08, _09, _0A, _0B, _0C, _0D, _0E, _0F,
    _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _1A, _1B, _1C, _1D, _1E, _1F,
    _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _2A, _2B, _2C, _2D, _2E, _2F,
    _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _3A, _3B, _3C, _3D, _3E, _3F,
    _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _4A, _4B, _4C, _4D, _4E, _4F,
    _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _5A, _5B, _5C, _5D, _5E, _5F,
    _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _6A, _6B, _6C, _6D, _6E, _6F,
    _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _7A, _7B, _7C, _7D, _7E, _7F,
    _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _8A, _8B, _8C, _8D, _8E, _8F,
    _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _9A, _9B, _9C, _9D, _9E, _9F,
    _A0, _A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8, _A9, _AA, _AB, _AC, _AD, _AE, _AF,
    _B0, _B1, _B2, _B3, _B4, _B5, _B6, _B7, _B8, _B9, _BA, _BB, _BC, _BD, _BE, _BF,
    _C0, _C1, _C2, _C3, _C4, _C5, _C6, _C7, _C8, _C9, _CA, _CB, _CC, _CD, _CE, _CF,
    _D0, _D1, _D2, _D3, _D4, _D5, _D6, _D7, _D8, _D9, _DA, _DB, _DC, _DD, _DE, _DF,
    _E0, _E1, _E2, _E3, _E4, _E5, _E6, _E7, _E8, _E9, _EA, _EB, _EC, _ED, _EE, _EF,
    _F0, _F1, _F2, _F3, _F4, _F5, _F6, _F7, _F8, _F9, _FA, _FB, _FC, _FD, _FE, _FF,
}

enum EnumEmpty {}

enum EnumSingle1 {
    A,
}

enum EnumSingle2 {
    A = 42 as isize,
}

enum EnumSingle3 {
    A,
    B(!),
}

#[repr(u8)]
enum EnumSingle4 {
    A,
}

#[repr(u8)]
enum EnumSingle5 {
    A = 42 as u8,
}

enum EnumWithMaybeUninhabitedVariant<T> {
    A(&'static ()),
    B(&'static (), T),
    C,
}

enum NicheFilledEnumWithAbsentVariant {
    A(&'static ()),
    B((), !),
    C,
}

enum Option2<A, B> {
    Some(A, B),
    None
}

// Two layouts are considered for `CanBeNicheFilledButShouldnt`:
//   Niche-filling:
//     { u32 (4 bytes), NonZeroU8 + tag in niche (1 byte), padding (3 bytes) }
//   Tagged:
//     { tag (1 byte), NonZeroU8 (1 byte), padding (2 bytes), u32 (4 bytes) }
// Both are the same size (due to padding),
// but the tagged layout is better as the tag creates a niche with 254 invalid values,
// allowing types like `Option<Option<CanBeNicheFilledButShouldnt>>` to fit into 8 bytes.
pub enum CanBeNicheFilledButShouldnt {
    A(NonZeroU8, u32),
    B
}
pub enum AlwaysTaggedBecauseItHasNoNiche {
    A(u8, u32),
    B
}

pub fn main() {
    assert_eq!(size_of::<u8>(), 1 as usize);
    assert_eq!(size_of::<u32>(), 4 as usize);
    assert_eq!(size_of::<char>(), 4 as usize);
    assert_eq!(size_of::<i8>(), 1 as usize);
    assert_eq!(size_of::<i32>(), 4 as usize);
    assert_eq!(size_of::<t>(), 2 as usize);
    assert_eq!(size_of::<u>(), 3 as usize);
    // Alignment causes padding before the char and the u32.

    assert_eq!(size_of::<v>(),
                16 as usize);
    assert_eq!(size_of::<isize>(), size_of::<usize>());
    assert_eq!(size_of::<w>(), size_of::<isize>());
    assert_eq!(size_of::<x>(), size_of::<isize>());
    assert_eq!(size_of::<isize>(), size_of::<y>());

    // Make sure enum types are the appropriate size, mostly
    // around ensuring alignment is handled properly

    assert_eq!(size_of::<e1>(), 8 as usize);
    assert_eq!(size_of::<e2>(), 8 as usize);
    assert_eq!(size_of::<e3>(), 4 as usize);
    assert_eq!(size_of::<ReorderedStruct>(), 4);
    assert_eq!(size_of::<ReorderedEnum>(), 6);
    assert_eq!(size_of::<ReorderedEnum2>(), 8);

    assert_eq!(size_of::<EnumEmpty>(), 0);
    assert_eq!(size_of::<EnumSingle1>(), 0);
    assert_eq!(size_of::<EnumSingle2>(), 0);
    assert_eq!(size_of::<EnumSingle3>(), 0);
    assert_eq!(size_of::<EnumSingle4>(), 1);
    assert_eq!(size_of::<EnumSingle5>(), 1);

    assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<!>>(),
               size_of::<EnumWithMaybeUninhabitedVariant<()>>());
    assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>());

    assert_eq!(size_of::<Option<Option<(bool, &())>>>(), size_of::<(bool, &())>());
    assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>());
    assert_eq!(size_of::<Option<Option2<bool, &()>>>(), size_of::<(bool, &())>());
    assert_eq!(size_of::<Option<Option2<&(), bool>>>(), size_of::<(bool, &())>());

    assert_eq!(size_of::<CanBeNicheFilledButShouldnt>(), 8);
    assert_eq!(size_of::<Option<CanBeNicheFilledButShouldnt>>(), 8);
    assert_eq!(size_of::<Option<Option<CanBeNicheFilledButShouldnt>>>(), 8);
    assert_eq!(size_of::<AlwaysTaggedBecauseItHasNoNiche>(), 8);
    assert_eq!(size_of::<Option<AlwaysTaggedBecauseItHasNoNiche>>(), 8);
    assert_eq!(size_of::<Option<Option<AlwaysTaggedBecauseItHasNoNiche>>>(), 8);
}
<|endoftext|>


<commit_before>use simple::{SimpleFbx, ObjectId, ObjectType};

pub struct Animation {
    curve_nodes: Vec<ObjectId>,
}

impl Animation {
    pub fn from_simple(fbx: &SimpleFbx) -> Option<Self> {
        // Find the first animation stack we can see TODO: Support multiple
        if let Some((&stack_id, ref _stack)) = fbx.objects.iter()
            .find(|&(_, o)| o.class.type_name() == "AnimationStack") {
            // Found a stack to attach to, now we need all its curve nodes
            let mut curve_nodes = Vec::new();
            for layer in fbx.children_of(stack_id) {
                for node in fbx.children_of(layer.id) {
                    curve_nodes.push(node.id);
                }
            }

            Some(Animation {
                curve_nodes: curve_nodes,
            })
        } else {
            // Nothing found
            None
        }
    }

    pub fn frame_count(&self, fbx: &SimpleFbx) -> i32 {
        // Find the first curve node with curves attached
        for &node in &self.curve_nodes {
            let curves = fbx.driven_properties_of(node);
            if curves.len() > 0 {
                let curve_obj = &fbx.objects[&curves[0].driver];

                // We've found a curve, return its frame count
                if let ObjectType::AnimationCurve(ref curve) = curve_obj.class {
                    return curve.frames
                } else {
                    panic!("Non-curve when curve was expected");
                }
            }
        }

        // We didn't find anything, just return 0
        0
    }

    pub fn transform_fbx_to_frame(&self, _fbx: &mut SimpleFbx, _frame: i32) {
    }
}

#[cfg(test)]
mod tests {
    use simple::{ObjectType, AnimationCurve};
    use super::*;

    #[test]
    fn it_doesnt_create_an_animation_for_animationless_fbx() {
        let anim = Animation::from_simple(&SimpleFbx::new());
        assert!(anim.is_none());
    }

    #[test]
    fn it_gives_zero_frames_for_empty_stack() {
        let mut fbx = SimpleFbx::new();
        fbx.new_object(ObjectType::AnimationStack);

        let anim = Animation::from_simple(&fbx).unwrap();

        assert!(anim.frame_count(&fbx) == 0);
    }

    #[test]
    fn it_gives_frames_for_stack_layer_node_and_curve() {
        let mut fbx = SimpleFbx::new();
        let stack_id = fbx.new_object(ObjectType::AnimationStack);
        let layer_id = fbx.new_object(ObjectType::AnimationLayer);
        fbx.connect_parent_child(stack_id, layer_id);
        let node_id = fbx.new_object(ObjectType::AnimationCurveNode);
        fbx.connect_parent_child(layer_id, node_id);
        let curve_id = fbx.new_object(ObjectType::AnimationCurve(AnimationCurve {
            frames: 2,
        }));
        fbx.connect_property_object(node_id, "d|Blah", curve_id);

        let anim = Animation::from_simple(&fbx).unwrap();

        assert!(anim.frame_count(&fbx) == 2);
    }
}
<commit_msg>Add transform property animation test<commit_after>use simple::{SimpleFbx, ObjectId, ObjectType};

pub struct Animation {
    curve_nodes: Vec<ObjectId>,
}

impl Animation {
    pub fn from_simple(fbx: &SimpleFbx) -> Option<Self> {
        // Find the first animation stack we can see TODO: Support multiple
        if let Some((&stack_id, ref _stack)) = fbx.objects.iter()
            .find(|&(_, o)| o.class.type_name() == "AnimationStack") {
            // Found a stack to attach to, now we need all its curve nodes
            let mut curve_nodes = Vec::new();
            for layer in fbx.children_of(stack_id) {
                for node in fbx.children_of(layer.id) {
                    curve_nodes.push(node.id);
                }
            }

            Some(Animation {
                curve_nodes: curve_nodes,
            })
        } else {
            // Nothing found
            None
        }
    }

    pub fn frame_count(&self, fbx: &SimpleFbx) -> i32 {
        // Find the first curve node with curves attached
        for &node in &self.curve_nodes {
            let curves = fbx.driven_properties_of(node);
            if curves.len() > 0 {
                let curve_obj = &fbx.objects[&curves[0].driver];

                // We've found a curve, return its frame count
                if let ObjectType::AnimationCurve(ref curve) = curve_obj.class {
                    return curve.frames
                } else {
                    panic!("Non-curve when curve was expected");
                }
            }
        }

        // We didn't find anything, just return 0
        0
    }

    pub fn transform_fbx_to_frame(&self, _fbx: &mut SimpleFbx, _frame: i32) {
    }
}

#[cfg(test)]
mod tests {
    use simple::{ObjectType, AnimationCurve};
    use super::*;

    #[test]
    fn it_doesnt_create_an_animation_for_animationless_fbx() {
        let anim = Animation::from_simple(&SimpleFbx::new());
        assert!(anim.is_none());
    }

    #[test]
    fn it_gives_zero_frames_for_empty_stack() {
        let mut fbx = SimpleFbx::new();
        fbx.new_object(ObjectType::AnimationStack);

        let anim = Animation::from_simple(&fbx).unwrap();

        assert!(anim.frame_count(&fbx) == 0);
    }

    #[test]
    fn it_gives_frames_for_stack_layer_node_and_curve() {
        let fbx = init_fbx_with_node();

        let anim = Animation::from_simple(&fbx).unwrap();

        assert!(anim.frame_count(&fbx) == 2);
    }

    #[test]
    fn it_transforms_property() {
        let mut fbx = init_fbx_with_node();
        let anim = Animation::from_simple(&fbx).unwrap();

        anim.transform_fbx_to_frame(&mut fbx, 2);

        let model = fbx.objects.iter().find(|&(_, o)| o.class.type_name() == "Model").unwrap();
        assert!(model.1.properties["Blah"].values[0].get_i32().unwrap() == 2);
    }

    fn init_fbx_with_node() -> SimpleFbx {
        let mut fbx = SimpleFbx::new();

        let stack_id = fbx.new_object(ObjectType::AnimationStack);

        let layer_id = fbx.new_object(ObjectType::AnimationLayer);
        fbx.connect_parent_child(stack_id, layer_id);

        let node_id = fbx.new_object(ObjectType::AnimationCurveNode);
        fbx.connect_parent_child(layer_id, node_id);

        let curve_id = fbx.new_object(ObjectType::AnimationCurve(AnimationCurve {
            frames: 2,
        }));
        fbx.connect_property_object(node_id, "d|Blah", curve_id);

        fbx
    }
}
<|endoftext|>


<commit_before>use super::{
    sign_up::{
        enter_sign_up_step, exit_sign_up_step, process_sign_up_input, prompt_sign_up_step,
        SignUpData, SignUpState,
    },
    ProcessSignUpInputResult, SessionOutput as Output,
};
use crate::{
    entity::{Entity, Player, Realm},
    text_utils::is_letter,
};
use lazy_static::lazy_static;
use maplit::hashmap;
use serde_json::json;
use std::collections::HashMap;

#[derive(Clone, Debug)]
pub struct SignInState {
    step: SignInStep,
    data: SignInData,
}

impl SignInState {
    pub fn get_sign_in_user_name(&self) -> Option<&str> {
        if self.step == SignInStep::SignedIn {
            if let Some(player) = &self.data.player {
                Some(player.name())
            } else if let Some(sign_up_state) = &self.data.sign_up_state {
                Some(&sign_up_state.data.user_name)
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn get_sign_up_data(&self) -> Option<&SignUpData> {
        if self.step == SignInStep::SignedIn {
            self.data.sign_up_state.as_ref().map(|state| &state.data)
        } else {
            None
        }
    }

    pub fn is_session_closed(&self) -> bool {
        self.step == SignInStep::SessionClosed
    }

    pub fn new() -> Self {
        new_state(
            SignInStep::AskingUserName,
            SignInData {
                player: None,
                sign_up_state: None,
            },
        )
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum SignInStep {
    AskingPassword,
    AskingSignUpData,
    AskingUserName,
    SessionClosed,
    SignedIn,
}

#[derive(Clone, Debug)]
struct SignInData {
    player: Option<Player>,
    sign_up_state: Option<SignUpState>,
}

struct StepImpl {
    enter: fn(&SignInData, &Realm) -> Output,
    exit: fn(&SignInData) -> Output,
    prompt: fn(&SignInData) -> Output,
    process_input: fn(&SignInState, &Realm, &str, String) -> ProcessInputResult,
}

pub struct ProcessInputResult {
    pub new_state: SignInState,
    pub output: Output,
    pub log_messages: Vec<String>,
}

impl ProcessInputResult {
    fn with_state(new_state: SignInState) -> Self {
        Self {
            new_state,
            output: Output::None,
            log_messages: Vec::new(),
        }
    }
}

pub fn process_input(
    state: &SignInState,
    realm: &Realm,
    source: &str,
    input: String,
) -> ProcessInputResult {
    lazy_static! {
        static ref SIGN_IN_STEPS: HashMap<SignInStep, StepImpl> = get_sign_in_steps();
    }

    match SIGN_IN_STEPS.get(&state.step) {
        Some(step) => {
            let ProcessInputResult {
                new_state,
                output,
                log_messages,
            } = (step.process_input)(state, realm, source, input);
            if let Some(new_step) = SIGN_IN_STEPS.get(&new_state.step) {
                let prompt_output = (new_step.prompt)(&new_state.data);
                let outputs = if new_state.step == state.step {
                    vec![output, prompt_output]
                } else {
                    let exit_output = (step.exit)(&new_state.data);
                    let enter_output = (new_step.enter)(&new_state.data, realm);
                    vec![output, exit_output, enter_output, prompt_output]
                };

                ProcessInputResult {
                    new_state,
                    output: Output::combine(outputs).process_highlights(),
                    log_messages,
                }
            } else {
                let exit_output = (step.exit)(&new_state.data);
                ProcessInputResult {
                    new_state,
                    output: Output::combine(vec![output, exit_output]).process_highlights(),
                    log_messages,
                }
            }
        }
        None => ProcessInputResult::with_state(state.clone()),
    }
}

fn get_sign_in_steps() -> HashMap<SignInStep, StepImpl> {
    hashmap! {
        SignInStep::AskingUserName => StepImpl {
            enter: |_, _| Output::None,
            exit: |_| Output::None,
            prompt: |_| Output::Str("What is your name? "),
            process_input: process_asking_user_name_input,
        },

        SignInStep::AskingPassword => StepImpl {
            enter: |_, _| Output::Json(json!({ "inputType": "password" })),
            exit: |_| Output::Json(json!({ "inputType": "text" })),
            prompt: |_| Output::Str("Please enter your password: "),
            process_input: process_asking_password_input,
        },

        SignInStep::AskingSignUpData => StepImpl {
            enter: |data, realm| enter_sign_up_step(data.sign_up_state.as_ref().unwrap(), realm),
            exit: |data| exit_sign_up_step(data.sign_up_state.as_ref().unwrap()),
            prompt: |data| prompt_sign_up_step(data.sign_up_state.as_ref().unwrap()),
            process_input: |state, realm, _, input| {
                let ProcessSignUpInputResult {
                    new_state: new_sign_up_state,
                    output,
                    log_messages
                } = process_sign_up_input(state.data.sign_up_state.as_ref().unwrap(), realm, input);
                ProcessInputResult {
                    new_state: new_state(
                        if new_sign_up_state.is_session_closed() {
                            SignInStep::SessionClosed
                        } else if new_sign_up_state.is_signed_up() {
                            SignInStep::SignedIn
                        } else {
                            SignInStep::AskingSignUpData
                        },
                        SignInData {
                            player: state.data.player.clone(),
                            sign_up_state: Some(new_sign_up_state)
                        }
                    ),
                    output,
                    log_messages
                }
            }
        }
    }
}

fn new_state(step: SignInStep, data: SignInData) -> SignInState {
    SignInState { step, data }
}

fn process_asking_user_name_input(
    state: &SignInState,
    realm: &Realm,
    _: &str,
    input: String,
) -> ProcessInputResult {
    lazy_static! {
        static ref RESERVED_NAMES: Vec<String> = vec![
            "Admin".to_owned(),
            "Administrator".to_owned(),
            "Root".to_owned()
        ];
    }
    let user_name = validate_user_name(input.to_lowercase());
    if user_name.len() < 3 {
        ProcessInputResult {
            new_state: state.clone(),
            output: if user_name.is_empty() {
                Output::None
            } else {
                Output::Str("I'm sorry, but your name should consist of at least 3 letters.\n")
            },
            log_messages: vec![],
        }
    } else if let Some(player) = realm.player_by_name(&user_name) {
        ProcessInputResult::with_state(new_state(
            SignInStep::AskingPassword,
            SignInData {
                player: Some(player.clone()),
                ..state.data.clone()
            },
        ))
    } else if RESERVED_NAMES.contains(&user_name) {
        ProcessInputResult {
            new_state: state.clone(),
            output: Output::Str("Yeah right, like I believe that...\n"),
            log_messages: vec![],
        }
    } else {
        ProcessInputResult::with_state(new_state(
            SignInStep::AskingSignUpData,
            SignInData {
                sign_up_state: Some(SignUpState::new(user_name)),
                ..state.data.clone()
            },
        ))
    }
}

fn process_asking_password_input(
    state: &SignInState,
    _: &Realm,
    _: &str,
    input: String,
) -> ProcessInputResult {
    if let Some(player) = &state.data.player {
        let name = player.name();
        if player.matches_password(&input) {
            let log_messages = vec![format!("Authentication success for player \"{}\"", name)];

            if player.session_id().is_some() {
                ProcessInputResult {
                    new_state: new_state(SignInStep::SessionClosed, state.data.clone()),
                    output: Output::Str(
                        "Cannot sign you in because you're already signed in from another \
                        location.\n",
                    ),
                    log_messages,
                }
            } else {
                ProcessInputResult {
                    new_state: new_state(SignInStep::SignedIn, state.data.clone()),
                    output: Output::String(format!(
                        "Welcome back, {}. Type *help* if you're feeling lost.\n",
                        name
                    )),
                    log_messages,
                }
            }
        } else {
            ProcessInputResult {
                new_state: state.clone(),
                output: Output::Str("Password incorrect.\n"),
                log_messages: vec![format!("Authentication failed for player \"{}\"", name)],
            }
        }
    } else {
        ProcessInputResult::with_state(state.clone())
    }
}

fn validate_user_name(input: String) -> String {
    let mut user_name = String::new();
    let mut num_special_chars = 0;
    for character in input.chars() {
        if is_letter(character) {
            user_name.push(if user_name.is_empty() {
                character.to_ascii_uppercase()
            } else {
                character
            });
        } else if !user_name.is_empty()
            && num_special_chars == 0
            && (character == '\'' || character == '-')
        {
            user_name.push(character);
            num_special_chars += 1;
        }
        if user_name.len() >= 12 {
            break;
        }
    }
    if user_name.len() > 1 && !is_letter(user_name.chars().rev().next().unwrap_or_default()) {
        user_name = user_name.chars().take(user_name.len() - 1).collect();
    }
    user_name
}
<commit_msg>Fix sign-up regression<commit_after>use super::{
    sign_up::{
        enter_sign_up_step, exit_sign_up_step, process_sign_up_input, prompt_sign_up_step,
        SignUpData, SignUpState,
    },
    ProcessSignUpInputResult, SessionOutput as Output,
};
use crate::{
    entity::{Entity, Player, Realm},
    text_utils::is_letter,
};
use lazy_static::lazy_static;
use maplit::hashmap;
use serde_json::json;
use std::collections::HashMap;

#[derive(Clone, Debug)]
pub struct SignInState {
    step: SignInStep,
    data: SignInData,
}

impl SignInState {
    pub fn get_sign_in_user_name(&self) -> Option<&str> {
        if self.step == SignInStep::SignedIn {
            if let Some(player) = &self.data.player {
                Some(player.name())
            } else if let Some(sign_up_state) = &self.data.sign_up_state {
                Some(&sign_up_state.data.user_name)
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn get_sign_up_data(&self) -> Option<&SignUpData> {
        if self.step == SignInStep::SignedIn {
            self.data.sign_up_state.as_ref().map(|state| &state.data)
        } else {
            None
        }
    }

    pub fn is_session_closed(&self) -> bool {
        self.step == SignInStep::SessionClosed
    }

    pub fn new() -> Self {
        new_state(
            SignInStep::AskingUserName,
            SignInData {
                player: None,
                sign_up_state: None,
            },
        )
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum SignInStep {
    AskingPassword,
    AskingSignUpData,
    AskingUserName,
    SessionClosed,
    SignedIn,
}

#[derive(Clone, Debug)]
struct SignInData {
    player: Option<Player>,
    sign_up_state: Option<SignUpState>,
}

struct StepImpl {
    enter: fn(&SignInData, &Realm) -> Output,
    exit: fn(&SignInData) -> Output,
    prompt: fn(&SignInData) -> Output,
    process_input: fn(&SignInState, &Realm, &str, String) -> ProcessInputResult,
}

pub struct ProcessInputResult {
    pub new_state: SignInState,
    pub output: Output,
    pub log_messages: Vec<String>,
}

impl ProcessInputResult {
    fn with_state(new_state: SignInState) -> Self {
        Self {
            new_state,
            output: Output::None,
            log_messages: Vec::new(),
        }
    }
}

pub fn process_input(
    state: &SignInState,
    realm: &Realm,
    source: &str,
    input: String,
) -> ProcessInputResult {
    lazy_static! {
        static ref SIGN_IN_STEPS: HashMap<SignInStep, StepImpl> = get_sign_in_steps();
    }

    match SIGN_IN_STEPS.get(&state.step) {
        Some(step) => {
            let ProcessInputResult {
                new_state,
                output,
                log_messages,
            } = (step.process_input)(state, realm, source, input);
            if let Some(new_step) = SIGN_IN_STEPS.get(&new_state.step) {
                let prompt_output = (new_step.prompt)(&new_state.data);
                let outputs = if new_state.step == state.step
                    && new_state.data.sign_up_state == state.data.sign_up_state
                {
                    vec![output, prompt_output]
                } else {
                    let exit_output = (step.exit)(&new_state.data);
                    let enter_output = (new_step.enter)(&new_state.data, realm);
                    vec![output, exit_output, enter_output, prompt_output]
                };

                ProcessInputResult {
                    new_state,
                    output: Output::combine(outputs).process_highlights(),
                    log_messages,
                }
            } else {
                let exit_output = (step.exit)(&new_state.data);
                ProcessInputResult {
                    new_state,
                    output: Output::combine(vec![output, exit_output]).process_highlights(),
                    log_messages,
                }
            }
        }
        None => ProcessInputResult::with_state(state.clone()),
    }
}

fn get_sign_in_steps() -> HashMap<SignInStep, StepImpl> {
    hashmap! {
        SignInStep::AskingUserName => StepImpl {
            enter: |_, _| Output::None,
            exit: |_| Output::None,
            prompt: |_| Output::Str("What is your name? "),
            process_input: process_asking_user_name_input,
        },

        SignInStep::AskingPassword => StepImpl {
            enter: |_, _| Output::Json(json!({ "inputType": "password" })),
            exit: |_| Output::Json(json!({ "inputType": "text" })),
            prompt: |_| Output::Str("Please enter your password: "),
            process_input: process_asking_password_input,
        },

        SignInStep::AskingSignUpData => StepImpl {
            enter: |data, realm| enter_sign_up_step(data.sign_up_state.as_ref().unwrap(), realm),
            exit: |data| exit_sign_up_step(data.sign_up_state.as_ref().unwrap()),
            prompt: |data| prompt_sign_up_step(data.sign_up_state.as_ref().unwrap()),
            process_input: |state, realm, _, input| {
                let ProcessSignUpInputResult {
                    new_state: new_sign_up_state,
                    output,
                    log_messages
                } = process_sign_up_input(state.data.sign_up_state.as_ref().unwrap(), realm, input);
                ProcessInputResult {
                    new_state: new_state(
                        if new_sign_up_state.is_session_closed() {
                            SignInStep::SessionClosed
                        } else if new_sign_up_state.is_signed_up() {
                            SignInStep::SignedIn
                        } else {
                            SignInStep::AskingSignUpData
                        },
                        SignInData {
                            player: state.data.player.clone(),
                            sign_up_state: Some(new_sign_up_state)
                        }
                    ),
                    output,
                    log_messages
                }
            }
        }
    }
}

fn new_state(step: SignInStep, data: SignInData) -> SignInState {
    SignInState { step, data }
}

fn process_asking_user_name_input(
    state: &SignInState,
    realm: &Realm,
    _: &str,
    input: String,
) -> ProcessInputResult {
    lazy_static! {
        static ref RESERVED_NAMES: Vec<String> = vec![
            "Admin".to_owned(),
            "Administrator".to_owned(),
            "Root".to_owned()
        ];
    }
    let user_name = validate_user_name(input.to_lowercase());
    if user_name.len() < 3 {
        ProcessInputResult {
            new_state: state.clone(),
            output: if user_name.is_empty() {
                Output::None
            } else {
                Output::Str("I'm sorry, but your name should consist of at least 3 letters.\n")
            },
            log_messages: vec![],
        }
    } else if let Some(player) = realm.player_by_name(&user_name) {
        ProcessInputResult::with_state(new_state(
            SignInStep::AskingPassword,
            SignInData {
                player: Some(player.clone()),
                ..state.data.clone()
            },
        ))
    } else if RESERVED_NAMES.contains(&user_name) {
        ProcessInputResult {
            new_state: state.clone(),
            output: Output::Str("Yeah right, like I believe that...\n"),
            log_messages: vec![],
        }
    } else {
        ProcessInputResult::with_state(new_state(
            SignInStep::AskingSignUpData,
            SignInData {
                sign_up_state: Some(SignUpState::new(user_name)),
                ..state.data.clone()
            },
        ))
    }
}

fn process_asking_password_input(
    state: &SignInState,
    _: &Realm,
    _: &str,
    input: String,
) -> ProcessInputResult {
    if let Some(player) = &state.data.player {
        let name = player.name();
        if player.matches_password(&input) {
            let log_messages = vec![format!("Authentication success for player \"{}\"", name)];

            if player.session_id().is_some() {
                ProcessInputResult {
                    new_state: new_state(SignInStep::SessionClosed, state.data.clone()),
                    output: Output::Str(
                        "Cannot sign you in because you're already signed in from another \
                        location.\n",
                    ),
                    log_messages,
                }
            } else {
                ProcessInputResult {
                    new_state: new_state(SignInStep::SignedIn, state.data.clone()),
                    output: Output::String(format!(
                        "Welcome back, {}. Type *help* if you're feeling lost.\n",
                        name
                    )),
                    log_messages,
                }
            }
        } else {
            ProcessInputResult {
                new_state: state.clone(),
                output: Output::Str("Password incorrect.\n"),
                log_messages: vec![format!("Authentication failed for player \"{}\"", name)],
            }
        }
    } else {
        ProcessInputResult::with_state(state.clone())
    }
}

fn validate_user_name(input: String) -> String {
    let mut user_name = String::new();
    let mut num_special_chars = 0;
    for character in input.chars() {
        if is_letter(character) {
            user_name.push(if user_name.is_empty() {
                character.to_ascii_uppercase()
            } else {
                character
            });
        } else if !user_name.is_empty()
            && num_special_chars == 0
            && (character == '\'' || character == '-')
        {
            user_name.push(character);
            num_special_chars += 1;
        }
        if user_name.len() >= 12 {
            break;
        }
    }
    if user_name.len() > 1 && !is_letter(user_name.chars().rev().next().unwrap_or_default()) {
        user_name = user_name.chars().take(user_name.len() - 1).collect();
    }
    user_name
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use escape::escape_html;
use token::Token;
use book::{Book, Number};
use error::{Error,Result};

use mustache;

/// Renderer for HTML
///
/// Also used by Epub.
pub struct HtmlRenderer<'a> {
    pub current_chapter: i32,
    epub3: bool,
    book: &'a Book,
    pub current_numbering: bool,
    pub current_hide: bool,
    table_head: bool,
    footnote_number: u32,
    pub footnotes: Vec<(String, String)>,
    verbatim: bool,
}

impl<'a> HtmlRenderer<'a> {
    /// Creates a new HTML renderer
    pub fn new(book: &'a Book) -> HtmlRenderer<'a> {
        HtmlRenderer {
            book: book,
            current_chapter: 1,
            current_numbering: book.get_bool("numbering").unwrap(),
            current_hide: false,
            table_head: false,
            footnote_number: 0,
            footnotes: vec!(),
            epub3: false,
            verbatim: false
        }
    }

    /// Render books as a standalone HTML file
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::new();

        for &(n, ref v) in &self.book.chapters {
            self.current_hide = false;
            let book_numbering = self.book.get_bool("numbering").unwrap();
            match n {
                Number::Unnumbered => self.current_numbering = false,
                Number::Default => self.current_numbering = book_numbering,
                Number::Specified(n) => {
                    self.current_numbering = book_numbering;
                    self.current_chapter = n;
                },
                Number::Hidden => {
                    self.current_numbering = false;
                    self.current_hide = true;
                },
            }
            content.push_str(&self.render_html(v));
        }

        let template = mustache::compile_str(try!(self.book.get_template("html.template")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("style",
                        &try!(self.book.get_template("html.css")))
            .build();

        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// display side notes if option is to true
    pub fn render_side_notes(&mut self, res: &mut String) {
        if self.book.get_bool("side_notes").unwrap() {
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
\n{} {}\n
\n", note_number, footnote));
            }
        }
    }

    // display end notes, if side_notes option is set to false
    pub fn render_end_notes(&mut self, res: &mut String) {
        if !self.footnotes.is_empty() {
            res.push_str("
Notes
");
            res.push_str("
");
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
{}	{}
\n", note_number, footnote));
            }
            res.push_str("
");
        }
    }

    ///
    pub fn render_html(&mut self, tokens: &[Token])-> String {
        let mut res = String::new();
        for token in tokens {
            res.push_str(&self.parse_token(&token));
            self.render_side_notes(&mut res);
        }
        self.render_end_notes(&mut res);
        res
    }

    /// Transform a vector of `Token`s to HTML format.
    pub fn render_vec(&mut self, tokens: &[Token]) -> String {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&self.parse_token(&token));
        }
        res
    }

    /// Parse a single token.
    pub fn parse_token(&mut self, token: &Token) -> String {
        match *token {
            Token::Str(ref text) => if self.verbatim {
                escape_html(text)
            } else {
                self.book.clean(text.clone())
            },
            Token::Paragraph(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Header(n, ref vec) => {
                if n == 1 && self.current_hide {
                    return String::new();
                }
                let s = if n == 1 && self.current_numbering {
                    let chapter = self.current_chapter;
                    self.current_chapter += 1;
                    self.book.get_header(chapter, &self.render_vec(vec)).unwrap()
                } else {
                    self.render_vec(vec)
                };
                format!("<h{}>{}</h{}>\n", n, self.book.clean(s), n)
            },
            Token::Emphasis(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Strong(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Code(ref vec) => format!("{}", self.render_vec(vec)),
            Token::BlockQuote(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::CodeBlock(ref language, ref vec) => {
                self.verbatim = true;
                let s = self.render_vec(vec);
                let output = if language.is_empty() {
                    format!("
{}
\n", s)
                } else {
                    format!("
{}
\n", language, s)
                };
                self.verbatim = false;
                output
            },
            Token::Rule => String::from("
***
\n"),
            Token::SoftBreak => String::from(" "),
            Token::HardBreak => String::from("
\n"),
            Token::List(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::OrderedList(n, ref vec) => format!("
\n{}
\n",
                                                      if n != 1 {
                                                          format!(" start = \"{}\"", n)
                                                      } else {
                                                          String::new()
                                                      },
                                                      self.render_vec(vec)),
            Token::Item(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Link(ref url, ref title, ref vec) => format!("{}",
                                                                url,
                                                                if title.is_empty() {
                                                                    String::new()
                                                                } else {
                                                                    format!(" title = \"{}\"", title)
                                                                },
                                                                escape_html(&self.render_vec(vec))),
            Token::Image(ref url, ref title, ref alt) => format!("",
                                                                 url,
                                                                 title,
                                                                 self.render_vec(alt)),
            Token::Table(_, ref vec) => format!("

\n{}

\n", self.render_vec(vec)),
            Token::TableRow(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::TableCell(ref vec) => {
                let tag = if self.table_head {"th"} else {"td"};
                format!("<{}>{}</{}>", tag, self.render_vec(vec), tag)
            },
            Token::TableHead(ref vec) => {
                self.table_head = true;
                let s = self.render_vec(vec);
                self.table_head = false;
                format!("
\n{}
\n", s)
            },
            Token::Footnote(ref vec) => {
                self.footnote_number += 1;
                let number = self.footnote_number;
                assert!(!vec.is_empty());

                let note_number = format!("

  [{}]:

", number, number);

                let inner = format!("{}",
                                    if self.epub3 {r#"epub:type="footnote"#}else{""},
                                    number, self.render_vec(vec));
                self.footnotes.push((note_number, inner));

                format!("{}",
                        if self.epub3 {"epub:type = \"noteref\""} else {""},
                        number, number, number)
            },
        }
    }
}

<commit_msg>html: fixed escaping bug introduced while I was trying to fix previous escaping bug...<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use escape::escape_html;
use token::Token;
use book::{Book, Number};
use error::{Error,Result};

use mustache;

/// Renderer for HTML
///
/// Also used by Epub.
pub struct HtmlRenderer<'a> {
    pub current_chapter: i32,
    epub3: bool,
    book: &'a Book,
    pub current_numbering: bool,
    pub current_hide: bool,
    table_head: bool,
    footnote_number: u32,
    pub footnotes: Vec<(String, String)>,
    verbatim: bool,
}

impl<'a> HtmlRenderer<'a> {
    /// Creates a new HTML renderer
    pub fn new(book: &'a Book) -> HtmlRenderer<'a> {
        HtmlRenderer {
            book: book,
            current_chapter: 1,
            current_numbering: book.get_bool("numbering").unwrap(),
            current_hide: false,
            table_head: false,
            footnote_number: 0,
            footnotes: vec!(),
            epub3: false,
            verbatim: false
        }
    }

    /// Render books as a standalone HTML file
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::new();

        for &(n, ref v) in &self.book.chapters {
            self.current_hide = false;
            let book_numbering = self.book.get_bool("numbering").unwrap();
            match n {
                Number::Unnumbered => self.current_numbering = false,
                Number::Default => self.current_numbering = book_numbering,
                Number::Specified(n) => {
                    self.current_numbering = book_numbering;
                    self.current_chapter = n;
                },
                Number::Hidden => {
                    self.current_numbering = false;
                    self.current_hide = true;
                },
            }
            content.push_str(&self.render_html(v));
        }

        let template = mustache::compile_str(try!(self.book.get_template("html.template")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("style",
                        &try!(self.book.get_template("html.css")))
            .build();

        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// display side notes if option is to true
    pub fn render_side_notes(&mut self, res: &mut String) {
        if self.book.get_bool("side_notes").unwrap() {
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
\n{} {}\n
\n", note_number, footnote));
            }
        }
    }

    // display end notes, if side_notes option is set to false
    pub fn render_end_notes(&mut self, res: &mut String) {
        if !self.footnotes.is_empty() {
            res.push_str("
Notes
");
            res.push_str("
");
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
{}	{}
\n", note_number, footnote));
            }
            res.push_str("
");
        }
    }

    ///
    pub fn render_html(&mut self, tokens: &[Token])-> String {
        let mut res = String::new();
        for token in tokens {
            res.push_str(&self.parse_token(&token));
            self.render_side_notes(&mut res);
        }
        self.render_end_notes(&mut res);
        res
    }

    /// Transform a vector of `Token`s to HTML format.
    pub fn render_vec(&mut self, tokens: &[Token]) -> String {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&self.parse_token(&token));
        }
        res
    }

    /// Parse a single token.
    pub fn parse_token(&mut self, token: &Token) -> String {
        match *token {
            Token::Str(ref text) => if self.verbatim {
                escape_html(text)
            } else {
                escape_html(&self.book.clean(text.clone()))
            },
            Token::Paragraph(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Header(n, ref vec) => {
                if n == 1 && self.current_hide {
                    return String::new();
                }
                let s = if n == 1 && self.current_numbering {
                    let chapter = self.current_chapter;
                    self.current_chapter += 1;
                    self.book.get_header(chapter, &self.render_vec(vec)).unwrap()
                } else {
                    self.render_vec(vec)
                };
                format!("<h{}>{}</h{}>\n", n, self.book.clean(s), n)
            },
            Token::Emphasis(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Strong(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Code(ref vec) => format!("{}", self.render_vec(vec)),
            Token::BlockQuote(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::CodeBlock(ref language, ref vec) => {
                self.verbatim = true;
                let s = self.render_vec(vec);
                let output = if language.is_empty() {
                    format!("
{}
\n", s)
                } else {
                    format!("
{}
\n", language, s)
                };
                self.verbatim = false;
                output
            },
            Token::Rule => String::from("
***
\n"),
            Token::SoftBreak => String::from(" "),
            Token::HardBreak => String::from("
\n"),
            Token::List(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::OrderedList(n, ref vec) => format!("
\n{}
\n",
                                                      if n != 1 {
                                                          format!(" start = \"{}\"", n)
                                                      } else {
                                                          String::new()
                                                      },
                                                      self.render_vec(vec)),
            Token::Item(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Link(ref url, ref title, ref vec) => format!("{}",
                                                                url,
                                                                if title.is_empty() {
                                                                    String::new()
                                                                } else {
                                                                    format!(" title = \"{}\"", title)
                                                                },
                                                                self.render_vec(vec)),
            Token::Image(ref url, ref title, ref alt) => format!("",
                                                                 url,
                                                                 title,
                                                                 self.render_vec(alt)),
            Token::Table(_, ref vec) => format!("

\n{}

\n", self.render_vec(vec)),
            Token::TableRow(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::TableCell(ref vec) => {
                let tag = if self.table_head {"th"} else {"td"};
                format!("<{}>{}</{}>", tag, self.render_vec(vec), tag)
            },
            Token::TableHead(ref vec) => {
                self.table_head = true;
                let s = self.render_vec(vec);
                self.table_head = false;
                format!("
\n{}
\n", s)
            },
            Token::Footnote(ref vec) => {
                self.footnote_number += 1;
                let number = self.footnote_number;
                assert!(!vec.is_empty());

                let note_number = format!("

  [{}]

", number, number);

                let inner = format!("{}",
                                    if self.epub3 {r#"epub:type="footnote"#}else{""},
                                    number, self.render_vec(vec));
                self.footnotes.push((note_number, inner));

                format!("{}",
                        if self.epub3 {"epub:type = \"noteref\""} else {""},
                        number, number, number)
            },
        }
    }
}

<|endoftext|>


<commit_before>use super::misc::ToFloat;
use std::ops::{Add, Sub, Div};

/// An iterator of a sequence of evenly spaced floats.
///
/// Iterator element type is `F`.
pub struct Linspace<F> {
    start: F,
    step: F,
    len: usize,
}

impl<F> Iterator for Linspace<F> where
    F: Copy + Add<Output=F>,
{
    type Item = F;

    #[inline]
    fn next(&mut self) -> Option<F>
    {
        if self.len == 0 {
            None
        } else {
            self.len -= 1;
            let elt = self.start;
            self.start = self.start + self.step;
            Some(elt)
        }
    }
}

/// Return an iterator with `n` elements, where the first
/// element is `a` and the last element is `b`.
///
/// Iterator element type is `F`.
///
/// ```
/// use itertools as it;
/// let mut xs = it::linspace::<f32>(0., 1., 5);
/// assert_eq!(xs.collect::<Vec<_>>(),
///            vec![0., 0.25, 0.5, 0.75, 1.0]);
/// ```
#[inline]
pub fn linspace<F>(a: F, b: F, n: usize) -> Linspace<F> where
    F: Copy + Sub<Output=F> + Div<Output=F>,
    usize: ToFloat<F>,
{
    let nf: F = n.to_float();
    let step = (b - a)/(nf - 1usize.to_float());
    Linspace {
        start: a,
        step: step,
        len: n,
    }
}
<commit_msg>linspace: Impl proper size_hint and exact size<commit_after>use super::misc::ToFloat;
use std::ops::{Add, Sub, Div};

/// An iterator of a sequence of evenly spaced floats.
///
/// Iterator element type is `F`.
pub struct Linspace<F> {
    start: F,
    step: F,
    len: usize,
}

impl<F> Iterator for Linspace<F> where
    F: Copy + Add<Output=F>,
{
    type Item = F;

    #[inline]
    fn next(&mut self) -> Option<F>
    {
        if self.len == 0 {
            None
        } else {
            self.len -= 1;
            let elt = self.start;
            self.start = self.start + self.step;
            Some(elt)
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>)
    {
        (self.len, Some(self.len))
    }
}

impl<F> ExactSizeIterator for Linspace<F> where
    Linspace<F>: Iterator
{ }

/// Return an iterator with `n` elements, where the first
/// element is `a` and the last element is `b`.
///
/// Iterator element type is `F`.
///
/// ```
/// use itertools as it;
/// let mut xs = it::linspace::<f32>(0., 1., 5);
/// assert_eq!(xs.collect::<Vec<_>>(),
///            vec![0., 0.25, 0.5, 0.75, 1.0]);
/// ```
#[inline]
pub fn linspace<F>(a: F, b: F, n: usize) -> Linspace<F> where
    F: Copy + Sub<Output=F> + Div<Output=F>,
    usize: ToFloat<F>,
{
    let nf: F = n.to_float();
    let step = (b - a)/(nf - 1usize.to_float());
    Linspace {
        start: a,
        step: step,
        len: n,
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use module::Module;
use storage::file::id::*;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use super::parser::{FileHeaderParser, Parser, ParserError};

use self::header::spec::*;
use self::header::data::*;

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    pub owning_module_name  : &'static str,
    pub header              : FileHeaderData,
    pub data                : String,
    pub id                  : FileID,
}

impl File {

    pub fn owner_name(&self) -> &'static str {
        self.owning_module_name
    }

    pub fn header(&self) -> &FileHeaderData {
        &self.header
    }

    pub fn set_header(&mut self, new_header: FileHeaderData) {
        self.header = new_header;
    }

    pub fn data(&self) -> &String {
        &self.data
    }

    pub fn contents(&self) -> (&FileHeaderData, &String) {
        (self.header(), self.data())
    }

    pub fn id(&self) -> &FileID {
        &self.id
    }

    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

}

impl Display for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl Debug for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.id,
               self.header,
               self.data);
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use storage::file::header::match_header_spec;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::header::data::FileHeaderData as FHD;
    use storage::file::header::spec::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<commit_msg>Add docs to File<commit_after>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use module::Module;
use storage::file::id::*;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use super::parser::{FileHeaderParser, Parser, ParserError};

use self::header::spec::*;
use self::header::data::*;

/**
 * Internal abstract view on a file. Does not neccessarily exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    /// The name of the module which owns this file
    pub owning_module_name  : &'static str,

    /// The header of the file
    pub header              : FileHeaderData,

    /// The content part of the file
    pub data                : String,

    /// The ID of the file
    pub id                  : FileID,
}

impl File {

    /**
     * Get the owner module name of the file
     */
    pub fn owner_name(&self) -> &'static str {
        self.owning_module_name
    }

    /**
     * Get the header of the file
     */
    pub fn header(&self) -> &FileHeaderData {
        &self.header
    }

    /**
     * Set the header of the file
     */
    pub fn set_header(&mut self, new_header: FileHeaderData) {
        self.header = new_header;
    }

    /**
     * Set the data of the file
     */
    pub fn data(&self) -> &String {
        &self.data
    }

    /**
     * Set the (header, data) of the file
     */
    pub fn contents(&self) -> (&FileHeaderData, &String) {
        (self.header(), self.data())
    }

    /**
     * Set the id of the file
     */
    pub fn id(&self) -> &FileID {
        &self.id
    }

    /**
     * Check whether the header or the data of the file match some regex
     */
    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

}

impl Display for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl Debug for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.id,
               self.header,
               self.data);
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use storage::file::header::match_header_spec;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::header::data::FileHeaderData as FHD;
    use storage::file::header::spec::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<|endoftext|>


<commit_before>#![allow(non_snake_case)]

use serde::{Serialize, Deserialize, Serializer, Deserializer};
use serde::de::{Visitor, MapAccess};
use serde::de::Error as DeError;
use serde::ser::SerializeStruct;
use serde_json::Value as JsonValue;
use serde_json::map::Map;
use std::fmt;

#[derive(Debug)]
pub enum LocationStrategy {
    Css,
    LinkText,
    PartialLinkText,
    XPath,
}

impl Serialize for LocationStrategy {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        match self {
            &LocationStrategy::Css => s.serialize_str("css selector"),
            &LocationStrategy::LinkText => s.serialize_str("link text"),
            &LocationStrategy::PartialLinkText => s.serialize_str("partial link text"),
            &LocationStrategy::XPath => s.serialize_str("xpath"),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct WebDriverError {
    pub error: String,
    pub message: String,
    pub stacktrace: Option<String>,
}

#[derive(Serialize)]
pub struct NewSessionCmd {
    capabilities: JsonValue,
    desiredCapabilities: JsonValue,
}

impl NewSessionCmd {
    pub fn new() -> Self {
        NewSessionCmd {
            capabilities: json!({
                "alwaysMatch": {
                    // ask chrome to be w3c compliant
                    "goog:chromeOptions": {
                        "w3c": true
                    }
                }
            }),
            desiredCapabilities: JsonValue::Object(Map::new()),
        }
    }

// TODO firefox specifc prefs
// [moz:firefoxOptions][prefs][name] = value;

}

#[derive(Debug, Deserialize)]
pub struct Session {
    pub sessionId: String,
}

#[derive(Serialize)]
pub struct GoCmd {
    pub url: String,
}

#[derive(Debug, Deserialize)]
pub struct Value<T> {
    pub value: T,
}

#[derive(Debug, Deserialize)]
pub struct CurrentTitle {
    pub title: String,
}

#[derive(Serialize)]
pub struct SwitchFrameCmd {
    pub id: JsonValue,
}

impl SwitchFrameCmd {
    pub fn from(id: JsonValue) -> Self {
        SwitchFrameCmd { id: id }
    }
}

#[derive(Serialize)]
pub struct SwitchWindowCmd {
    handle: String,
}

impl SwitchWindowCmd {
    pub fn from(handle: &str) -> Self {
        SwitchWindowCmd { handle: handle.to_string() }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Empty {}

#[derive(Serialize)]
pub struct FindElementCmd<'a> {
    pub using: LocationStrategy,
    pub value: &'a str,
}

#[derive(PartialEq, Debug)]
pub struct ElementReference {
    pub reference: String,
}

impl ElementReference {
    pub fn from_str(handle: &str) -> ElementReference {
        ElementReference { reference: handle.to_string() }
    }
}

impl Serialize for ElementReference {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut ss = s.serialize_struct("ElementReference", 1)?;
        ss.serialize_field("element-6066-11e4-a52e-4f735466cecf", &self.reference)?;
        ss.end()
    }
}

impl<'de> Deserialize<'de> for ElementReference {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        enum Field { Reference };

        impl<'de> Deserialize<'de> for Field {
            fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
                struct FieldVisitor;
                impl<'de> Visitor<'de> for FieldVisitor {
                    type Value = Field;
                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                        formatter.write_str("element-6066-11e4-a52e-4f735466cecf")
                    }

                    fn visit_str<E: DeError>(self, value: &str) -> Result<Field, E>
                    {
                        match value {
                            "element-6066-11e4-a52e-4f735466cecf" => Ok(Field::Reference),
                            _ => Err(DeError::unknown_field(value, FIELDS)),
                        }
                    }
                }

                d.deserialize_identifier(FieldVisitor)
            }
        }

        struct ElementReferenceVisitor;
        impl<'de> Visitor<'de> for ElementReferenceVisitor {
            type Value = ElementReference;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct ElementReference")
            }

            fn visit_map<V>(self, mut visitor: V) -> Result<ElementReference, V::Error>
                where V: MapAccess<'de>
            {
                let mut reference = None;
                while let Some(key) = visitor.next_key()? {
                    match key {
                        Field::Reference => {
                            if reference.is_some() {
                                return Err(DeError::duplicate_field("element-6066-11e4-a52e-4f735466cecf"));
                            }
                            reference = Some(visitor.next_value()?);
                        }
                    }
                }
                match reference {
                    Some(r) => Ok(ElementReference { reference: r }),
                    None => return Err(DeError::missing_field("element-6066-11e4-a52e-4f735466cecf")),
                }
            }
        }

        const FIELDS: &'static [&'static str] = &["element-6066-11e4-a52e-4f735466cecf"];
        d.deserialize_struct("ElementReference", FIELDS, ElementReferenceVisitor)
    }
}

#[derive(Debug, Deserialize)]
pub struct Cookie {
    pub name: String,
    pub value: String,
    pub path: String,
    pub domain: String,
    pub secure: bool,
    pub httpOnly: bool,
    // TODO: expiry:
}

#[derive(Serialize)]
pub struct ExecuteCmd {
    pub script: String,
    pub args: Vec<JsonValue>,
}

#[cfg(test)]
mod tests {
    use serde_json::{from_str, to_string};
    use super::*;

    #[test]
    fn element_ref_serialize() {
        let r: ElementReference = from_str("{\"element-6066-11e4-a52e-4f735466cecf\": \"ZZZZ\"}").unwrap();
        assert_eq!(r.reference, "ZZZZ");
        let r2 = from_str(&to_string(&r).unwrap()).unwrap();
        assert_eq!(r, r2);
    }
}
<commit_msg>Remove NewSessionCmd::desiredCapabilities<commit_after>#![allow(non_snake_case)]

use serde::{Serialize, Deserialize, Serializer, Deserializer};
use serde::de::{Visitor, MapAccess};
use serde::de::Error as DeError;
use serde::ser::SerializeStruct;
use serde_json::Value as JsonValue;
use std::fmt;

#[derive(Debug)]
pub enum LocationStrategy {
    Css,
    LinkText,
    PartialLinkText,
    XPath,
}

impl Serialize for LocationStrategy {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        match self {
            &LocationStrategy::Css => s.serialize_str("css selector"),
            &LocationStrategy::LinkText => s.serialize_str("link text"),
            &LocationStrategy::PartialLinkText => s.serialize_str("partial link text"),
            &LocationStrategy::XPath => s.serialize_str("xpath"),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct WebDriverError {
    pub error: String,
    pub message: String,
    pub stacktrace: Option<String>,
}

#[derive(Serialize)]
pub struct NewSessionCmd {
    capabilities: JsonValue,
}

impl NewSessionCmd {
    pub fn new() -> Self {
        NewSessionCmd {
            capabilities: json!({
                "alwaysMatch": {
                    // ask chrome to be w3c compliant
                    "goog:chromeOptions": {
                        "w3c": true
                    }
                }
            }),
        }
    }

// TODO firefox specifc prefs
// [moz:firefoxOptions][prefs][name] = value;

}

#[derive(Debug, Deserialize)]
pub struct Session {
    pub sessionId: String,
}

#[derive(Serialize)]
pub struct GoCmd {
    pub url: String,
}

#[derive(Debug, Deserialize)]
pub struct Value<T> {
    pub value: T,
}

#[derive(Debug, Deserialize)]
pub struct CurrentTitle {
    pub title: String,
}

#[derive(Serialize)]
pub struct SwitchFrameCmd {
    pub id: JsonValue,
}

impl SwitchFrameCmd {
    pub fn from(id: JsonValue) -> Self {
        SwitchFrameCmd { id: id }
    }
}

#[derive(Serialize)]
pub struct SwitchWindowCmd {
    handle: String,
}

impl SwitchWindowCmd {
    pub fn from(handle: &str) -> Self {
        SwitchWindowCmd { handle: handle.to_string() }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Empty {}

#[derive(Serialize)]
pub struct FindElementCmd<'a> {
    pub using: LocationStrategy,
    pub value: &'a str,
}

#[derive(PartialEq, Debug)]
pub struct ElementReference {
    pub reference: String,
}

impl ElementReference {
    pub fn from_str(handle: &str) -> ElementReference {
        ElementReference { reference: handle.to_string() }
    }
}

impl Serialize for ElementReference {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut ss = s.serialize_struct("ElementReference", 1)?;
        ss.serialize_field("element-6066-11e4-a52e-4f735466cecf", &self.reference)?;
        ss.end()
    }
}

impl<'de> Deserialize<'de> for ElementReference {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        enum Field { Reference };

        impl<'de> Deserialize<'de> for Field {
            fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
                struct FieldVisitor;
                impl<'de> Visitor<'de> for FieldVisitor {
                    type Value = Field;
                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                        formatter.write_str("element-6066-11e4-a52e-4f735466cecf")
                    }

                    fn visit_str<E: DeError>(self, value: &str) -> Result<Field, E>
                    {
                        match value {
                            "element-6066-11e4-a52e-4f735466cecf" => Ok(Field::Reference),
                            _ => Err(DeError::unknown_field(value, FIELDS)),
                        }
                    }
                }

                d.deserialize_identifier(FieldVisitor)
            }
        }

        struct ElementReferenceVisitor;
        impl<'de> Visitor<'de> for ElementReferenceVisitor {
            type Value = ElementReference;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct ElementReference")
            }

            fn visit_map<V>(self, mut visitor: V) -> Result<ElementReference, V::Error>
                where V: MapAccess<'de>
            {
                let mut reference = None;
                while let Some(key) = visitor.next_key()? {
                    match key {
                        Field::Reference => {
                            if reference.is_some() {
                                return Err(DeError::duplicate_field("element-6066-11e4-a52e-4f735466cecf"));
                            }
                            reference = Some(visitor.next_value()?);
                        }
                    }
                }
                match reference {
                    Some(r) => Ok(ElementReference { reference: r }),
                    None => return Err(DeError::missing_field("element-6066-11e4-a52e-4f735466cecf")),
                }
            }
        }

        const FIELDS: &'static [&'static str] = &["element-6066-11e4-a52e-4f735466cecf"];
        d.deserialize_struct("ElementReference", FIELDS, ElementReferenceVisitor)
    }
}

#[derive(Debug, Deserialize)]
pub struct Cookie {
    pub name: String,
    pub value: String,
    pub path: String,
    pub domain: String,
    pub secure: bool,
    pub httpOnly: bool,
    // TODO: expiry:
}

#[derive(Serialize)]
pub struct ExecuteCmd {
    pub script: String,
    pub args: Vec<JsonValue>,
}

#[cfg(test)]
mod tests {
    use serde_json::{from_str, to_string};
    use super::*;

    #[test]
    fn element_ref_serialize() {
        let r: ElementReference = from_str("{\"element-6066-11e4-a52e-4f735466cecf\": \"ZZZZ\"}").unwrap();
        assert_eq!(r.reference, "ZZZZ");
        let r2 = from_str(&to_string(&r).unwrap()).unwrap();
        assert_eq!(r, r2);
    }
}
<|endoftext|>