<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::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding::XRRigidTransformMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::dompointreadonly::DOMPointReadOnly;
use crate::dom::globalscope::GlobalScope;
use crate::dom::vrframedata::create_typed_array;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use euclid::{RigidTransform3D, Rotation3D, Vector3D};
use js::jsapi::{Heap, JSContext, JSObject};
use std::ptr::NonNull;

#[dom_struct]
pub struct XRRigidTransform {
    reflector_: Reflector,
    position: MutNullableDom<DOMPointReadOnly>,
    orientation: MutNullableDom<DOMPointReadOnly>,
    #[ignore_malloc_size_of = "defined in euclid"]
    transform: RigidTransform3D<f64>,
    inverse: MutNullableDom<XRRigidTransform>,
    matrix: Heap<*mut JSObject>,
}

impl XRRigidTransform {
    fn new_inherited(transform: RigidTransform3D<f64>) -> XRRigidTransform {
        XRRigidTransform {
            reflector_: Reflector::new(),
            position: MutNullableDom::default(),
            orientation: MutNullableDom::default(),
            transform,
            inverse: MutNullableDom::default(),
            matrix: Heap::default(),
        }
    }

    pub fn new(
        global: &GlobalScope,
        transform: RigidTransform3D<f64>,
    ) -> DomRoot<XRRigidTransform> {
        reflect_dom_object(
            Box::new(XRRigidTransform::new_inherited(transform)),
            global,
            XRRigidTransformBinding::Wrap,
        )
    }

    pub fn identity(window: &GlobalScope) -> DomRoot<XRRigidTransform> {
        let transform = RigidTransform3D::identity();
        XRRigidTransform::new(window, transform)
    }

    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-xrrigidtransform
    pub fn Constructor(
        window: &Window,
        position: &DOMPointInit,
        orientation: &DOMPointInit,
    ) -> Fallible<DomRoot<Self>> {
        if position.w != 1.0 {
            return Err(Error::Type(format!(
                "XRRigidTransform must be constructed with a position that has a w value of of 1.0, not {}",
                position.w
            )));
        }

        let translate = Vector3D::new(position.x as f64, position.y as f64, position.z as f64);
        let rotate = Rotation3D::unit_quaternion(
            orientation.x as f64,
            orientation.y as f64,
            orientation.z as f64,
            orientation.w as f64,
        );
        let transform = RigidTransform3D::new(rotate, translate);
        Ok(XRRigidTransform::new(&window.global(), transform))
    }
}

impl XRRigidTransformMethods for XRRigidTransform {
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-position
    fn Position(&self) -> DomRoot<DOMPointReadOnly> {
        self.position.or_init(|| {
            let t = &self.transform.translation;
            DOMPointReadOnly::new(&self.global(), t.x, t.y, t.z, 1.0)
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-orientation
    fn Orientation(&self) -> DomRoot<DOMPointReadOnly> {
        self.position.or_init(|| {
            let r = &self.transform.rotation;
            DOMPointReadOnly::new(&self.global(), r.i, r.j, r.k, r.r)
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-inverse
    fn Inverse(&self) -> DomRoot<XRRigidTransform> {
        self.inverse.or_init(|| {
            let transform = XRRigidTransform::new(&self.global(), self.transform.inverse());
            transform.inverse.set(Some(self));
            transform
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-matrix
    #[allow(unsafe_code)]
    unsafe fn Matrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        if self.matrix.get().is_null() {
            let cx = self.global().get_cx();
            // According to the spec all matrices are column-major,
            // however euclid uses row vectors so we use .to_row_major_array()
            let arr = self.transform.to_transform().cast().to_row_major_array();
            create_typed_array(cx, &arr, &self.matrix);
        }
        NonNull::new(self.matrix.get()).unwrap()
    }
}

impl XRRigidTransform {
    /// https://immersive-web.github.io/webxr/#dom-xrpose-transform
    pub fn transform(&self) -> RigidTransform3D<f64> {
        self.transform
    }
}
<commit_msg>Fix orientation getter in XRRigidTransform<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::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding::XRRigidTransformMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::dompointreadonly::DOMPointReadOnly;
use crate::dom::globalscope::GlobalScope;
use crate::dom::vrframedata::create_typed_array;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use euclid::{RigidTransform3D, Rotation3D, Vector3D};
use js::jsapi::{Heap, JSContext, JSObject};
use std::ptr::NonNull;

#[dom_struct]
pub struct XRRigidTransform {
    reflector_: Reflector,
    position: MutNullableDom<DOMPointReadOnly>,
    orientation: MutNullableDom<DOMPointReadOnly>,
    #[ignore_malloc_size_of = "defined in euclid"]
    transform: RigidTransform3D<f64>,
    inverse: MutNullableDom<XRRigidTransform>,
    matrix: Heap<*mut JSObject>,
}

impl XRRigidTransform {
    fn new_inherited(transform: RigidTransform3D<f64>) -> XRRigidTransform {
        XRRigidTransform {
            reflector_: Reflector::new(),
            position: MutNullableDom::default(),
            orientation: MutNullableDom::default(),
            transform,
            inverse: MutNullableDom::default(),
            matrix: Heap::default(),
        }
    }

    pub fn new(
        global: &GlobalScope,
        transform: RigidTransform3D<f64>,
    ) -> DomRoot<XRRigidTransform> {
        reflect_dom_object(
            Box::new(XRRigidTransform::new_inherited(transform)),
            global,
            XRRigidTransformBinding::Wrap,
        )
    }

    pub fn identity(window: &GlobalScope) -> DomRoot<XRRigidTransform> {
        let transform = RigidTransform3D::identity();
        XRRigidTransform::new(window, transform)
    }

    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-xrrigidtransform
    pub fn Constructor(
        window: &Window,
        position: &DOMPointInit,
        orientation: &DOMPointInit,
    ) -> Fallible<DomRoot<Self>> {
        if position.w != 1.0 {
            return Err(Error::Type(format!(
                "XRRigidTransform must be constructed with a position that has a w value of of 1.0, not {}",
                position.w
            )));
        }

        let translate = Vector3D::new(position.x as f64, position.y as f64, position.z as f64);
        let rotate = Rotation3D::unit_quaternion(
            orientation.x as f64,
            orientation.y as f64,
            orientation.z as f64,
            orientation.w as f64,
        );
        let transform = RigidTransform3D::new(rotate, translate);
        Ok(XRRigidTransform::new(&window.global(), transform))
    }
}

impl XRRigidTransformMethods for XRRigidTransform {
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-position
    fn Position(&self) -> DomRoot<DOMPointReadOnly> {
        self.position.or_init(|| {
            let t = &self.transform.translation;
            DOMPointReadOnly::new(&self.global(), t.x, t.y, t.z, 1.0)
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-orientation
    fn Orientation(&self) -> DomRoot<DOMPointReadOnly> {
        self.orientation.or_init(|| {
            let r = &self.transform.rotation;
            DOMPointReadOnly::new(&self.global(), r.i, r.j, r.k, r.r)
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-inverse
    fn Inverse(&self) -> DomRoot<XRRigidTransform> {
        self.inverse.or_init(|| {
            let transform = XRRigidTransform::new(&self.global(), self.transform.inverse());
            transform.inverse.set(Some(self));
            transform
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-matrix
    #[allow(unsafe_code)]
    unsafe fn Matrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        if self.matrix.get().is_null() {
            let cx = self.global().get_cx();
            // According to the spec all matrices are column-major,
            // however euclid uses row vectors so we use .to_row_major_array()
            let arr = self.transform.to_transform().cast().to_row_major_array();
            create_typed_array(cx, &arr, &self.matrix);
        }
        NonNull::new(self.matrix.get()).unwrap()
    }
}

impl XRRigidTransform {
    /// https://immersive-web.github.io/webxr/#dom-xrpose-transform
    pub fn transform(&self) -> RigidTransform3D<f64> {
        self.transform
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::str::Words;
use std::io::{BufferedReader, File};
use cgmath::vector::{Vector3, Vector2};
use core::types::{MBool, MInt};
use visualizer::types::{VertexCoord, TextureCoord, Normal};

struct Face {
    vertex: [MInt, ..3],
    texture: [MInt, ..3],
    normal: [MInt, ..3],
}

pub struct Model {
    coords: Vec<VertexCoord>,
    normals: Vec<Normal>,
    texture_coords: Vec<TextureCoord>,
    faces: Vec<Face>,
}

// TODO: unwrap() -> ...
impl Model {
    pub fn new(path: &Path) -> Model {
        let mut obj = Model {
            coords: Vec::new(),
            normals: Vec::new(),
            texture_coords: Vec::new(),
            faces: Vec::new(),
        };
        obj.read(path);
        obj
    }

    fn read_v_or_vn(words: &mut Words) -> VertexCoord {
        Vector3 {
            x: from_str(words.next().unwrap()).unwrap(),
            y: from_str(words.next().unwrap()).unwrap(),
            z: from_str(words.next().unwrap()).unwrap(),
        }
    }

    fn read_vt(words: &mut Words) -> TextureCoord {
        Vector2 {
            x: from_str(words.next().unwrap()).unwrap(),
            y: 1.0 - from_str(words.next().unwrap()).unwrap(), // flip
        }
    }

    fn read_f(words: &mut Words) -> Face {
        let mut face = Face {
            vertex: [0, 0, 0],
            texture: [0, 0, 0],
            normal: [0, 0, 0],
        };
        let mut i = 0;
        for group in *words {
            let mut w = group.split('/');
            face.vertex[i] = from_str(w.next().unwrap()).unwrap();
            face.texture[i] = from_str(w.next().unwrap()).unwrap();
            face.normal[i] = from_str(w.next().unwrap()).unwrap();
            i += 1;
        }
        face
    }

    fn read_line(&mut self, line: &str) {
        let mut words = line.words();
        fn is_correct_tag(tag: &str) -> MBool {
            tag.len() != 0 && tag[0] != ('#' as u8)
        }
        match words.next() {
            Some(tag) if is_correct_tag(tag) => {
                let w = &mut words;
                match tag {
                    &"v" => self.coords.push(Model::read_v_or_vn(w)),
                    &"vn" => self.normals.push(Model::read_v_or_vn(w)),
                    &"vt" => self.texture_coords.push(Model::read_vt(w)),
                    &"f" => self.faces.push(Model::read_f(w)),
                    _ => {},
                }
            }
            _ => {},
        };
    }

    fn read(&mut self, path: &Path) {
        let mut file = BufferedReader::new(File::open(path));
        for line in file.lines() {
            self.read_line(line.unwrap());
        }
    }

    pub fn build(&self) -> Vec<VertexCoord> {
        let mut mesh = Vec::new();
        for face in self.faces.iter() {
            for i in range(0, 3) {
                let vertex_id = face.vertex[i as uint] - 1;
                mesh.push(*self.coords.get(vertex_id as uint));
            }
        }
        mesh
    }

    pub fn build_tex_coord(&self) -> Vec<TextureCoord> {
        let mut tex_coords = Vec::new();
        for face in self.faces.iter() {
            for i in range(0, 3) {
                let texture_coord_id = face.texture[i as uint] as uint - 1;
                tex_coords.push(*self.texture_coords.get(texture_coord_id));
            }
        }
        tex_coords
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>obj.rs: Fixed plain unwrap calls<commit_after>// See LICENSE file for copyright and license details.

use std::str::Words;
use std::str::CharSplits;
use std::from_str::FromStr;
use std::io::{BufferedReader, File};
use cgmath::vector::{Vector3, Vector2};
use core::types::{MBool, MInt};
use visualizer::types::{VertexCoord, TextureCoord, Normal};

struct Face {
    vertex: [MInt, ..3],
    texture: [MInt, ..3],
    normal: [MInt, ..3],
}

pub struct Model {
    coords: Vec<VertexCoord>,
    normals: Vec<Normal>,
    texture_coords: Vec<TextureCoord>,
    faces: Vec<Face>,
}

fn parse_word<T: FromStr>(words: &mut Words) -> T {
    let str = words.next().expect("Can not read next word");
    from_str(str).expect("Can not convert from string")
}

fn parse_charsplit<T: FromStr>(words: &mut CharSplits<char>) -> T {
    let str = words.next().expect("Can not read next word");
    from_str(str).expect("Can not convert from string")
}

impl Model {
    pub fn new(path: &Path) -> Model {
        let mut obj = Model {
            coords: Vec::new(),
            normals: Vec::new(),
            texture_coords: Vec::new(),
            faces: Vec::new(),
        };
        obj.read(path);
        obj
    }

    fn read_v_or_vn(words: &mut Words) -> VertexCoord {
        Vector3 {
            x: parse_word(words),
            y: parse_word(words),
            z: parse_word(words),
        }
    }

    fn read_vt(words: &mut Words) -> TextureCoord {
        Vector2 {
            x: parse_word(words),
            y: 1.0 - parse_word(words), // flip
        }
    }

    fn read_f(words: &mut Words) -> Face {
        let mut face = Face {
            vertex: [0, 0, 0],
            texture: [0, 0, 0],
            normal: [0, 0, 0],
        };
        let mut i = 0;
        for group in *words {
            let mut w = group.split('/');
            face.vertex[i] = parse_charsplit(&mut w);
            face.texture[i] = parse_charsplit(&mut w);
            face.normal[i] = parse_charsplit(&mut w);
            i += 1;
        }
        face
    }

    fn read_line(&mut self, line: &str) {
        let mut words = line.words();
        fn is_correct_tag(tag: &str) -> MBool {
            tag.len() != 0 && tag[0] != ('#' as u8)
        }
        match words.next() {
            Some(tag) if is_correct_tag(tag) => {
                let w = &mut words;
                match tag {
                    &"v" => self.coords.push(Model::read_v_or_vn(w)),
                    &"vn" => self.normals.push(Model::read_v_or_vn(w)),
                    &"vt" => self.texture_coords.push(Model::read_vt(w)),
                    &"f" => self.faces.push(Model::read_f(w)),
                    _ => {},
                }
            }
            _ => {},
        };
    }

    fn read(&mut self, path: &Path) {
        let mut file = BufferedReader::new(File::open(path));
        for line in file.lines() {
            match line {
                Ok(line) => self.read_line(line),
                Err(msg) => fail!("Obj: read error: {}", msg),
            }
        }
    }

    pub fn build(&self) -> Vec<VertexCoord> {
        let mut mesh = Vec::new();
        for face in self.faces.iter() {
            for i in range(0, 3) {
                let vertex_id = face.vertex[i as uint] - 1;
                mesh.push(*self.coords.get(vertex_id as uint));
            }
        }
        mesh
    }

    pub fn build_tex_coord(&self) -> Vec<TextureCoord> {
        let mut tex_coords = Vec::new();
        for face in self.faces.iter() {
            for i in range(0, 3) {
                let texture_coord_id = face.texture[i as uint] as uint - 1;
                tex_coords.push(*self.texture_coords.get(texture_coord_id));
            }
        }
        tex_coords
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>///
/// Describes the possible behaviors of the `Tree::remove_node` method.
///
pub enum RemoveBehavior {
    ///
    /// All children will be dropped recursively.  In other words, the entire sub-tree of the `Node`
    /// being removed will be dropped from the tree.  Those `Node`s will no longer exist and
    /// cannot be accessed even if you have the `NodeId` the previously pointed to them.
    ///
    /// This means even without using `Clone` you might end up with copies of invalid `NodeId`s.
    /// Use this behavior with caution.
    ///
    DropChildren,

    ///
    /// If the removed `Node` (let's call it `A`) has a parent, `A`'s parent will become the
    /// parent of `A`'s children.  This effectively just shifts them up one level in the `Tree`.
    ///
    /// If `A` doesn't have a parent, then this behaves exactly like
    /// `RemoveBehavior::OrphanChildren`.
    ///
    LiftChildren,

    ///
    /// All children will have their parent references cleared.  This means nothing will point to
    /// them, but they will still exist in the tree.  Those `Node`s can still be accessed provided
    /// that you have the `NodeId` that points to them.
    ///
    OrphanChildren,
}

<commit_msg>add behavior doc tests<commit_after>///
/// Describes the possible behaviors of the `Tree::remove_node` method.
///
pub enum RemoveBehavior {
    ///
    /// All children will be dropped recursively.  In other words, the entire sub-tree of the `Node`
    /// being removed will be dropped from the tree.  Those `Node`s will no longer exist and
    /// cannot be accessed even if you have the `NodeId` the previously pointed to them.
    ///
    /// This means even without using `Clone` you might end up with copies of invalid `NodeId`s.
    /// Use this behavior with caution.
    ///
    /// ```
    /// use id_tree::Tree;
    /// use id_tree::Node;
    /// use id_tree::RemoveBehavior;
    ///
    /// let mut tree: Tree<i32> = Tree::new();
    /// let root_id = tree.set_root(Node::new(0));
    ///
    /// let child_id = tree.insert_with_parent(Node::new(1), &root_id).ok().unwrap();
    /// let grandchild_id = tree.insert_with_parent(Node::new(2), &child_id).ok().unwrap();
    ///
    /// let child = tree.remove_node(child_id, RemoveBehavior::DropChildren).ok().unwrap();
    ///
    /// assert!(tree.get(&grandchild_id).is_none());
    /// assert_eq!(tree.get(&root_id).unwrap().children().len(), 0);
    /// ```
    ///
    DropChildren,

    ///
    /// If the removed `Node` (let's call it `A`) has a parent, `A`'s parent will become the
    /// parent of `A`'s children.  This effectively just shifts them up one level in the `Tree`.
    ///
    /// If `A` doesn't have a parent, then this behaves exactly like
    /// `RemoveBehavior::OrphanChildren`.
    ///
    /// ```
    /// use id_tree::Tree;
    /// use id_tree::Node;
    /// use id_tree::RemoveBehavior;
    ///
    /// let mut tree: Tree<i32> = Tree::new();
    /// let root_id = tree.set_root(Node::new(0));
    ///
    /// let child_id = tree.insert_with_parent(Node::new(1), &root_id).ok().unwrap();
    /// let grandchild_id = tree.insert_with_parent(Node::new(2), &child_id).ok().unwrap();
    ///
    /// let child = tree.remove_node(child_id, RemoveBehavior::LiftChildren).ok().unwrap();
    ///
    /// assert!(tree.get(&grandchild_id).is_some());
    /// assert!(tree.get(&root_id).unwrap().children().contains(&grandchild_id));
    /// ```
    ///
    LiftChildren,

    ///
    /// All children will have their parent references cleared.  This means nothing will point to
    /// them, but they will still exist in the tree.  Those `Node`s can still be accessed provided
    /// that you have the `NodeId` that points to them.
    ///
    /// ```
    /// use id_tree::Tree;
    /// use id_tree::Node;
    /// use id_tree::RemoveBehavior;
    ///
    /// let mut tree: Tree<i32> = Tree::new();
    /// let root_id = tree.set_root(Node::new(0));
    ///
    /// let child_id = tree.insert_with_parent(Node::new(1), &root_id).ok().unwrap();
    /// let grandchild_id = tree.insert_with_parent(Node::new(2), &child_id).ok().unwrap();
    ///
    /// let child = tree.remove_node(child_id, RemoveBehavior::OrphanChildren).ok().unwrap();
    ///
    /// assert!(tree.get(&grandchild_id).is_some());
    /// assert_eq!(tree.get(&root_id).unwrap().children().len(), 0);
    /// ```
    ///
    OrphanChildren,
}

<|endoftext|>


<commit_before>use std::ops::Deref;
use std::cell::RefCell;
use std::cell::Ref;

#[derive(Debug)]
pub struct LowercaseText {
    inner: String,
}

impl LowercaseText {
    pub fn new(original_text: &str) -> Self {
        let inner = original_text.to_lowercase();
        Self { inner }
    }
}

impl Deref for LowercaseText {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

#[derive(Debug)]
pub struct Text<'a> {
    original: &'a str,
    lowercase: RefCell<Option<LowercaseText>>,
}

impl<'a> Text<'a> {
    pub fn new(original_text: &'a str) -> Self {
        Self {
            original: original_text,
            lowercase: RefCell::new(None),
        }
    }

    pub fn lowercase(&self) -> Ref<'_, LowercaseText> {
        if self.lowercase.borrow().is_none() {
            let lowercase_text = LowercaseText::new(self.original);
            self.lowercase.replace(Some(lowercase_text));
        }

        let ref_opt_lowercase = self.lowercase.borrow();
        Ref::map(ref_opt_lowercase, |r| r.as_ref().unwrap())
    }
}

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

    #[test]
    fn test_text() {
        let text = Text::new("Hello THERE");
        assert_eq!(text.lowercase().deref().deref(), "hello there");
    }
}
<commit_msg>Fix formatting<commit_after>use std::cell::{Ref, RefCell};
use std::ops::Deref;

#[derive(Debug)]
pub struct LowercaseText {
    inner: String,
}

impl LowercaseText {
    pub fn new(original_text: &str) -> Self {
        let inner = original_text.to_lowercase();
        Self { inner }
    }
}

impl Deref for LowercaseText {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

#[derive(Debug)]
pub struct Text<'a> {
    original: &'a str,
    lowercase: RefCell<Option<LowercaseText>>,
}

impl<'a> Text<'a> {
    pub fn new(original_text: &'a str) -> Self {
        Self {
            original: original_text,
            lowercase: RefCell::new(None),
        }
    }

    pub fn lowercase(&self) -> Ref<'_, LowercaseText> {
        if self.lowercase.borrow().is_none() {
            let lowercase_text = LowercaseText::new(self.original);
            self.lowercase.replace(Some(lowercase_text));
        }

        let ref_opt_lowercase = self.lowercase.borrow();
        Ref::map(ref_opt_lowercase, |r| r.as_ref().unwrap())
    }
}

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

    #[test]
    fn test_text() {
        let text = Text::new("Hello THERE");
        assert_eq!(text.lowercase().deref().deref(), "hello there");
    }
}
<|endoftext|>


<commit_before>use sass::variable::SassVariable;

use std::fmt;
use std::borrow::Cow;
use std::borrow::Cow::Borrowed;
use std::collections::HashMap;

pub fn evaluate(original: &str, variables: &HashMap<String, String>) -> String {
    let vt = ValueTokenizer::new(original);
    vt.into_iter().map(|part|
        match part {
            ValuePart::Variable(ref name) => {
                match (*variables).get(&(*name).to_string()) {
                    Some(v) => v.to_owned(),
                    None => name.to_string(),
                }
            },
            ValuePart::String(ref s) => s.to_string(),
        }
    ).collect::<Vec<_>>().connect(" ")
}

#[derive(Debug, Clone, PartialEq)]
pub enum ValuePart<'a> {
    Variable(Cow<'a, str>),
    String(Cow<'a, str>),
}

impl<'a> fmt::Display for ValuePart<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let val = match self {
            &ValuePart::Variable(ref s) => s,
            &ValuePart::String(ref s) => s,
        };
        write!(f, "{}", val)
    }
}

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Option<ValuePart<'a>> {
        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        match self.bytes[i..limit].iter().position(|&c| c == b' ' ) {
            Some(pos) => { i += pos; },
            None      => { i = limit },
        }
        self.offset = i + 1;
        if self.bytes[start] == b'$' {
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else {
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        }
    }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = ValuePart<'a>;

    fn next(&mut self) -> Option<ValuePart<'a>> {
        if self.offset < self.value_str.len() {
            return self.parse()
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::borrow::Cow::Borrowed;
    use std::collections::HashMap;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"bar"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$bar"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"baz"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$quux"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_subtitutes_variable_values() {
        let mut vars = HashMap::new();
        vars.insert("$bar".to_string(), "4".to_string());
        vars.insert("$quux".to_string(), "3px 10px".to_string());
        let answer = evaluate("foo $bar baz $quux", &vars);
        assert_eq!("foo 4 baz 3px 10px", answer);
    }
}
<commit_msg>Remove an unused import<commit_after>use std::fmt;
use std::borrow::Cow;
use std::borrow::Cow::Borrowed;
use std::collections::HashMap;

pub fn evaluate(original: &str, variables: &HashMap<String, String>) -> String {
    let vt = ValueTokenizer::new(original);
    vt.into_iter().map(|part|
        match part {
            ValuePart::Variable(ref name) => {
                match (*variables).get(&(*name).to_string()) {
                    Some(v) => v.to_owned(),
                    None => name.to_string(),
                }
            },
            ValuePart::String(ref s) => s.to_string(),
        }
    ).collect::<Vec<_>>().connect(" ")
}

#[derive(Debug, Clone, PartialEq)]
pub enum ValuePart<'a> {
    Variable(Cow<'a, str>),
    String(Cow<'a, str>),
}

impl<'a> fmt::Display for ValuePart<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let val = match self {
            &ValuePart::Variable(ref s) => s,
            &ValuePart::String(ref s) => s,
        };
        write!(f, "{}", val)
    }
}

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Option<ValuePart<'a>> {
        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        match self.bytes[i..limit].iter().position(|&c| c == b' ' ) {
            Some(pos) => { i += pos; },
            None      => { i = limit },
        }
        self.offset = i + 1;
        if self.bytes[start] == b'$' {
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else {
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        }
    }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = ValuePart<'a>;

    fn next(&mut self) -> Option<ValuePart<'a>> {
        if self.offset < self.value_str.len() {
            return self.parse()
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::borrow::Cow::Borrowed;
    use std::collections::HashMap;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"bar"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$bar"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"baz"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$quux"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_subtitutes_variable_values() {
        let mut vars = HashMap::new();
        vars.insert("$bar".to_string(), "4".to_string());
        vars.insert("$quux".to_string(), "3px 10px".to_string());
        let answer = evaluate("foo $bar baz $quux", &vars);
        assert_eq!("foo 4 baz 3px 10px", answer);
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_join"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Konstantin Pospelov <kupospelov@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate clap;

#[macro_use]
extern crate uucore;

use std::fs::File;
use std::io::{BufRead, BufReader, Lines, Stdin, stdin};
use std::cmp::Ordering;
use clap::{App, Arg};

static NAME: &'static str = "join";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

#[derive(PartialEq)]
enum FileNum {
    None,
    File1,
    File2,
}

struct Settings {
    key1: usize,
    key2: usize,
    print_unpaired: FileNum,
    ignore_case: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            key1: 0,
            key2: 0,
            print_unpaired: FileNum::None,
            ignore_case: false,
        }
    }
}

struct Line {
    fields: Vec<String>,
}

impl Line {
    fn new(string: String) -> Line {
        Line { fields: string.split_whitespace().map(|s| String::from(s)).collect() }
    }

    /// Get field at index.
    fn get_field(&self, index: usize) -> &str {
        if index < self.fields.len() {
            &self.fields[index]
        } else {
            ""
        }
    }

    /// Iterate each field except the one at the index.
    fn foreach_except<F>(&self, index: usize, f: &F)
    where
        F: Fn(&String),
    {
        for (i, field) in self.fields.iter().enumerate() {
            if i != index {
                f(&field);
            }
        }
    }
}

struct State<'a> {
    key: usize,
    print_unpaired: bool,
    lines: Lines<Box<BufRead + 'a>>,
    seq: Vec<Line>,
}

impl<'a> State<'a> {
    fn new(name: &str, stdin: &'a Stdin, key: usize, print_unpaired: bool) -> State<'a> {
        let f = if name == "-" {
            Box::new(stdin.lock()) as Box<BufRead>
        } else {
            match File::open(name) {
                Ok(file) => Box::new(BufReader::new(file)) as Box<BufRead>,
                Err(err) => crash!(1, "{}: {}", name, err),
            }
        };

        State {
            key: key,
            print_unpaired: print_unpaired,
            lines: f.lines(),
            seq: Vec::new(),
        }
    }

    /// Compare the key fields of the two current lines.
    fn compare(&self, other: &State, ignore_case: bool) -> Ordering {
        let key1 = self.seq[0].get_field(self.key);
        let key2 = other.seq[0].get_field(other.key);

        compare(key1, key2, ignore_case)
    }

    /// Skip the current unpaired line.
    fn skip_line(&mut self) {
        if self.print_unpaired {
            self.print_unpaired_line(&self.seq[0]);
        }

        self.next_line();
    }

    /// Move to the next line, if any.
    fn next_line(&mut self) {
        match self.read_line() {
            Some(line) => self.seq[0] = line,
            None => self.seq.clear(),
        }
    }

    /// Keep reading line sequence until the key does not change, return
    /// the first line whose key differs.
    fn extend(&mut self, ignore_case: bool) -> Option<Line> {
        while let Some(line) = self.read_line() {
            let diff = compare(
                self.seq[0].get_field(self.key),
                line.get_field(self.key),
                ignore_case,
            );

            if diff == Ordering::Equal {
                self.seq.push(line);
            } else {
                return Some(line);
            }
        }

        return None;
    }

    /// Combine two line sequences.
    fn combine(&self, other: &State) {
        let key = self.seq[0].get_field(self.key);

        for line1 in &self.seq {
            for line2 in &other.seq {
                print!("{}", key);
                line1.foreach_except(self.key, &print_field);
                line2.foreach_except(other.key, &print_field);
                println!();
            }
        }
    }

    /// Reset with the next line.
    fn reset(&mut self, next_line: Option<Line>) {
        self.seq.clear();

        if let Some(line) = next_line {
            self.seq.push(line);
        }
    }

    fn has_line(&self) -> bool {
        !self.seq.is_empty()
    }

    fn initialize(&mut self) {
        if let Some(line) = self.read_line() {
            self.seq.push(line);
        }
    }

    fn finalize(&mut self) {
        if self.has_line() && self.print_unpaired {
            self.print_unpaired_line(&self.seq[0]);

            while let Some(line) = self.read_line() {
                self.print_unpaired_line(&line);
            }
        }
    }

    fn read_line(&mut self) -> Option<Line> {
        match self.lines.next() {
            Some(value) => Some(Line::new(crash_if_err!(1, value))),
            None => None,
        }
    }

    fn print_unpaired_line(&self, line: &Line) {
        print!("{}", line.get_field(self.key));
        line.foreach_except(self.key, &print_field);
        println!();
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = App::new(NAME)
        .version(VERSION)
        .about(
            "For each pair of input lines with identical join fields, write a line to
standard output. The default join field is the first, delimited by blanks.

When FILE1 or FILE2 (not both) is -, read standard input.")
        .help_message("display this help and exit")
        .version_message("display version and exit")
        .arg(Arg::with_name("a")
            .short("a")
            .takes_value(true)
            .possible_values(&["1", "2"])
            .value_name("FILENUM")
            .help("also print unpairable lines from file FILENUM, where
FILENUM is 1 or 2, corresponding to FILE1 or FILE2"))
        .arg(Arg::with_name("i")
            .short("i")
            .long("ignore-case")
            .help("ignore differences in case when comparing fields"))
        .arg(Arg::with_name("j")
            .short("j")
            .takes_value(true)
            .value_name("FIELD")
            .help("equivalent to '-1 FIELD -2 FIELD'"))
        .arg(Arg::with_name("1")
            .short("1")
            .takes_value(true)
            .value_name("FIELD")
            .help("join on this FIELD of file 1"))
        .arg(Arg::with_name("2")
            .short("2")
            .takes_value(true)
            .value_name("FIELD")
            .help("join on this FIELD of file 2"))
        .arg(Arg::with_name("file1")
            .required(true)
            .value_name("FILE1")
            .hidden(true))
        .arg(Arg::with_name("file2")
            .required(true)
            .value_name("FILE2")
            .hidden(true))
        .get_matches_from(args);

    let keys = parse_field_number(matches.value_of("j"));
    let key1 = parse_field_number(matches.value_of("1"));
    let key2 = parse_field_number(matches.value_of("2"));

    let mut settings: Settings = Default::default();
    settings.print_unpaired = match matches.value_of("a") {
        Some(value) => {
            match value {
                "1" => FileNum::File1,
                "2" => FileNum::File2,
                value => crash!(1, "invalid file number: {}", value),
            }
        }
        None => FileNum::None,
    };
    settings.ignore_case = matches.is_present("i");
    settings.key1 = get_field_number(keys, key1);
    settings.key2 = get_field_number(keys, key2);

    let file1 = matches.value_of("file1").unwrap();
    let file2 = matches.value_of("file2").unwrap();

    if file1 == "-" && file2 == "-" {
        crash!(1, "both files cannot be standard input");
    }

    exec(file1, file2, &settings)
}

fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
    let stdin = stdin();

    let mut state1 = State::new(
        &file1,
        &stdin,
        settings.key1,
        settings.print_unpaired == FileNum::File1,
    );

    let mut state2 = State::new(
        &file2,
        &stdin,
        settings.key2,
        settings.print_unpaired == FileNum::File2,
    );

    state1.initialize();
    state2.initialize();

    while state1.has_line() && state2.has_line() {
        let diff = state1.compare(&state2, settings.ignore_case);

        match diff {
            Ordering::Less => {
                state1.skip_line();
            }
            Ordering::Greater => {
                state2.skip_line();
            }
            Ordering::Equal => {
                let next_line1 = state1.extend(settings.ignore_case);
                let next_line2 = state2.extend(settings.ignore_case);

                state1.combine(&state2);

                state1.reset(next_line1);
                state2.reset(next_line2);
            }
        }
    }

    state1.finalize();
    state2.finalize();

    0
}

/// Check that keys for both files and for a particular file are not
/// contradictory and return the zero-based key index.
fn get_field_number(keys: Option<usize>, key: Option<usize>) -> usize {
    if let Some(keys) = keys {
        if let Some(key) = key {
            if keys != key {
                crash!(1, "incompatible join fields {}, {}", keys, key);
            }
        }

        return keys - 1;
    }

    match key {
        Some(key) => key - 1,
        None => 0,
    }
}

/// Parse the specified field string as a natural number and return it.
fn parse_field_number(value: Option<&str>) -> Option<usize> {
    match value {
        Some(value) => {
            match value.parse() {
                Ok(result) if result > 0 => Some(result),
                _ => crash!(1, "invalid field number: '{}'", value),
            }
        }
        None => None,
    }
}

fn compare(field1: &str, field2: &str, ignore_case: bool) -> Ordering {
    if ignore_case {
        field1.to_lowercase().cmp(&field2.to_lowercase())
    } else {
        field1.cmp(field2)
    }
}

fn print_field(field: &String) {
    print!("{}{}", ' ', field);
}
<commit_msg>join: simplify closure for line parsing<commit_after>#![crate_name = "uu_join"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Konstantin Pospelov <kupospelov@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate clap;

#[macro_use]
extern crate uucore;

use std::fs::File;
use std::io::{BufRead, BufReader, Lines, Stdin, stdin};
use std::cmp::Ordering;
use clap::{App, Arg};

static NAME: &'static str = "join";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

#[derive(PartialEq)]
enum FileNum {
    None,
    File1,
    File2,
}

struct Settings {
    key1: usize,
    key2: usize,
    print_unpaired: FileNum,
    ignore_case: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            key1: 0,
            key2: 0,
            print_unpaired: FileNum::None,
            ignore_case: false,
        }
    }
}

struct Line {
    fields: Vec<String>,
}

impl Line {
    fn new(string: String) -> Line {
        Line { fields: string.split_whitespace().map(String::from).collect() }
    }

    /// Get field at index.
    fn get_field(&self, index: usize) -> &str {
        if index < self.fields.len() {
            &self.fields[index]
        } else {
            ""
        }
    }

    /// Iterate each field except the one at the index.
    fn foreach_except<F>(&self, index: usize, f: &F)
    where
        F: Fn(&String),
    {
        for (i, field) in self.fields.iter().enumerate() {
            if i != index {
                f(&field);
            }
        }
    }
}

struct State<'a> {
    key: usize,
    print_unpaired: bool,
    lines: Lines<Box<BufRead + 'a>>,
    seq: Vec<Line>,
}

impl<'a> State<'a> {
    fn new(name: &str, stdin: &'a Stdin, key: usize, print_unpaired: bool) -> State<'a> {
        let f = if name == "-" {
            Box::new(stdin.lock()) as Box<BufRead>
        } else {
            match File::open(name) {
                Ok(file) => Box::new(BufReader::new(file)) as Box<BufRead>,
                Err(err) => crash!(1, "{}: {}", name, err),
            }
        };

        State {
            key: key,
            print_unpaired: print_unpaired,
            lines: f.lines(),
            seq: Vec::new(),
        }
    }

    /// Compare the key fields of the two current lines.
    fn compare(&self, other: &State, ignore_case: bool) -> Ordering {
        let key1 = self.seq[0].get_field(self.key);
        let key2 = other.seq[0].get_field(other.key);

        compare(key1, key2, ignore_case)
    }

    /// Skip the current unpaired line.
    fn skip_line(&mut self) {
        if self.print_unpaired {
            self.print_unpaired_line(&self.seq[0]);
        }

        self.next_line();
    }

    /// Move to the next line, if any.
    fn next_line(&mut self) {
        match self.read_line() {
            Some(line) => self.seq[0] = line,
            None => self.seq.clear(),
        }
    }

    /// Keep reading line sequence until the key does not change, return
    /// the first line whose key differs.
    fn extend(&mut self, ignore_case: bool) -> Option<Line> {
        while let Some(line) = self.read_line() {
            let diff = compare(
                self.seq[0].get_field(self.key),
                line.get_field(self.key),
                ignore_case,
            );

            if diff == Ordering::Equal {
                self.seq.push(line);
            } else {
                return Some(line);
            }
        }

        return None;
    }

    /// Combine two line sequences.
    fn combine(&self, other: &State) {
        let key = self.seq[0].get_field(self.key);

        for line1 in &self.seq {
            for line2 in &other.seq {
                print!("{}", key);
                line1.foreach_except(self.key, &print_field);
                line2.foreach_except(other.key, &print_field);
                println!();
            }
        }
    }

    /// Reset with the next line.
    fn reset(&mut self, next_line: Option<Line>) {
        self.seq.clear();

        if let Some(line) = next_line {
            self.seq.push(line);
        }
    }

    fn has_line(&self) -> bool {
        !self.seq.is_empty()
    }

    fn initialize(&mut self) {
        if let Some(line) = self.read_line() {
            self.seq.push(line);
        }
    }

    fn finalize(&mut self) {
        if self.has_line() && self.print_unpaired {
            self.print_unpaired_line(&self.seq[0]);

            while let Some(line) = self.read_line() {
                self.print_unpaired_line(&line);
            }
        }
    }

    fn read_line(&mut self) -> Option<Line> {
        match self.lines.next() {
            Some(value) => Some(Line::new(crash_if_err!(1, value))),
            None => None,
        }
    }

    fn print_unpaired_line(&self, line: &Line) {
        print!("{}", line.get_field(self.key));
        line.foreach_except(self.key, &print_field);
        println!();
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = App::new(NAME)
        .version(VERSION)
        .about(
            "For each pair of input lines with identical join fields, write a line to
standard output. The default join field is the first, delimited by blanks.

When FILE1 or FILE2 (not both) is -, read standard input.")
        .help_message("display this help and exit")
        .version_message("display version and exit")
        .arg(Arg::with_name("a")
            .short("a")
            .takes_value(true)
            .possible_values(&["1", "2"])
            .value_name("FILENUM")
            .help("also print unpairable lines from file FILENUM, where
FILENUM is 1 or 2, corresponding to FILE1 or FILE2"))
        .arg(Arg::with_name("i")
            .short("i")
            .long("ignore-case")
            .help("ignore differences in case when comparing fields"))
        .arg(Arg::with_name("j")
            .short("j")
            .takes_value(true)
            .value_name("FIELD")
            .help("equivalent to '-1 FIELD -2 FIELD'"))
        .arg(Arg::with_name("1")
            .short("1")
            .takes_value(true)
            .value_name("FIELD")
            .help("join on this FIELD of file 1"))
        .arg(Arg::with_name("2")
            .short("2")
            .takes_value(true)
            .value_name("FIELD")
            .help("join on this FIELD of file 2"))
        .arg(Arg::with_name("file1")
            .required(true)
            .value_name("FILE1")
            .hidden(true))
        .arg(Arg::with_name("file2")
            .required(true)
            .value_name("FILE2")
            .hidden(true))
        .get_matches_from(args);

    let keys = parse_field_number(matches.value_of("j"));
    let key1 = parse_field_number(matches.value_of("1"));
    let key2 = parse_field_number(matches.value_of("2"));

    let mut settings: Settings = Default::default();
    settings.print_unpaired = match matches.value_of("a") {
        Some(value) => {
            match value {
                "1" => FileNum::File1,
                "2" => FileNum::File2,
                value => crash!(1, "invalid file number: {}", value),
            }
        }
        None => FileNum::None,
    };
    settings.ignore_case = matches.is_present("i");
    settings.key1 = get_field_number(keys, key1);
    settings.key2 = get_field_number(keys, key2);

    let file1 = matches.value_of("file1").unwrap();
    let file2 = matches.value_of("file2").unwrap();

    if file1 == "-" && file2 == "-" {
        crash!(1, "both files cannot be standard input");
    }

    exec(file1, file2, &settings)
}

fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
    let stdin = stdin();

    let mut state1 = State::new(
        &file1,
        &stdin,
        settings.key1,
        settings.print_unpaired == FileNum::File1,
    );

    let mut state2 = State::new(
        &file2,
        &stdin,
        settings.key2,
        settings.print_unpaired == FileNum::File2,
    );

    state1.initialize();
    state2.initialize();

    while state1.has_line() && state2.has_line() {
        let diff = state1.compare(&state2, settings.ignore_case);

        match diff {
            Ordering::Less => {
                state1.skip_line();
            }
            Ordering::Greater => {
                state2.skip_line();
            }
            Ordering::Equal => {
                let next_line1 = state1.extend(settings.ignore_case);
                let next_line2 = state2.extend(settings.ignore_case);

                state1.combine(&state2);

                state1.reset(next_line1);
                state2.reset(next_line2);
            }
        }
    }

    state1.finalize();
    state2.finalize();

    0
}

/// Check that keys for both files and for a particular file are not
/// contradictory and return the zero-based key index.
fn get_field_number(keys: Option<usize>, key: Option<usize>) -> usize {
    if let Some(keys) = keys {
        if let Some(key) = key {
            if keys != key {
                crash!(1, "incompatible join fields {}, {}", keys, key);
            }
        }

        return keys - 1;
    }

    match key {
        Some(key) => key - 1,
        None => 0,
    }
}

/// Parse the specified field string as a natural number and return it.
fn parse_field_number(value: Option<&str>) -> Option<usize> {
    match value {
        Some(value) => {
            match value.parse() {
                Ok(result) if result > 0 => Some(result),
                _ => crash!(1, "invalid field number: '{}'", value),
            }
        }
        None => None,
    }
}

fn compare(field1: &str, field2: &str, ignore_case: bool) -> Ordering {
    if ignore_case {
        field1.to_lowercase().cmp(&field2.to_lowercase())
    } else {
        field1.cmp(field2)
    }
}

fn print_field(field: &String) {
    print!("{}{}", ' ', field);
}
<|endoftext|>


<commit_before>//! The oldest module, the data module stores all the data needed to collect
//! and also the search, dir algorithms
pub mod bktree;
pub mod collection;
pub mod ipc;

use super::ctrl::{CollectionPathAlive, ForwardNetMessage, NetMessages, Status, UiUpdateMsg};
use collection::{Collection, Container};

use std::{
    path::Path, // path, clear
    sync::{mpsc::Sender, Arc as SArc, Mutex as SMutex},
};

// todo: hide it
pub struct CollectionOutputData {
    pub nr_found_songs: u32,
}

pub fn search_in_single_path(
    has_ui: bool,
    collection_data: SArc<SMutex<Container>>,
    collection_protected: SArc<SMutex<Collection>>,
    mutex_to_ui_msg: SArc<SMutex<Sender<UiUpdateMsg>>>,
    index: usize,
    elem: &str,
) -> CollectionOutputData {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // send start animation for that path
        mutex_to_ui_msg
            .lock()
            .and_then(|locked_to_start_ui_message| {
                trace!("send startAnimation for path {:?}", index);
                locked_to_start_ui_message
                    .send(UiUpdateMsg::CollectionUpdate(
                        CollectionPathAlive::BusyPath(index),
                        Status::ON,
                    ))
                    .unwrap_or_else(|_| warn!("... lost start animation for index {:?}", index));
                trace!("start busy animation");
                Ok(())
            })
            .unwrap_or_else(|_| error!("... that should not happen here at start"));
    }
    // this collection lock is fine, it's not the main Container, only some data
    let locked_collection = &mut *collection_protected.lock().unwrap();

    // do it: main task here is to visit and dive deep
    //        into the subfolders of this folder
    match locked_collection.visit_path(
        collection_data,
        Path::new(elem),
        &collection::Collection::visit_files,
    ) {
        Ok(local_stats) => {
            if has_ui {
                // send stop animation for that path
                trace!("send stopAnimation for path {:?}", index);
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_to_stop_ui_message| {
                        locked_to_stop_ui_message
                            .send(UiUpdateMsg::CollectionUpdate(
                                CollectionPathAlive::BusyPath(index),
                                Status::OFF,
                            ))
                            .unwrap_or_else(|_| warn!("... lost stop animation for {:?}", index));
                        trace!("stop busy animation");
                        Ok(())
                    })
                    .unwrap_or_else(|_| {
                        error!("... that should not happen here at stop");
                    });
            } else {
                let text = format!(
                    "\n\
                             analyzed: {an:>width$}, faulty: {fa:>width$}\n\
                             searched: {se:>width$}, other: {ot:>width$}",
                    an = local_stats.analyzed,
                    fa = local_stats.faulty,
                    se = local_stats.searched,
                    ot = local_stats.other,
                    width = 3
                );
                println!("[{:?}] done {}", index, text);
            }
            // return this here
            CollectionOutputData {
                nr_found_songs: local_stats.analyzed,
            }
        }
        Err(_e) => {
            let text = format!("An error has occurred in search path [{}]!!", index);
            if has_ui {
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_update_message| {
                        locked_update_message
                            .send(UiUpdateMsg::NetUpdate(ForwardNetMessage::Stats(
                                NetMessages::Debug(text),
                            )))
                            .unwrap();
                        Ok(())
                    })
                    .unwrap();
            } else {
                println!("{:?}", text);
            }
            // return this here
            CollectionOutputData { nr_found_songs: 0 }
        }
    }
}
<commit_msg>[collection] added todo for path, subpaths<commit_after>//! The oldest module, the data module stores all the data needed to collect
//! and also the search, dir algorithms
pub mod bktree;
pub mod collection;
pub mod ipc;

use super::ctrl::{CollectionPathAlive, ForwardNetMessage, NetMessages, Status, UiUpdateMsg};
use collection::{Collection, Container};

use std::{
    path::Path, // path, clear
    sync::{mpsc::Sender, Arc as SArc, Mutex as SMutex},
};

// todo: hide it
pub struct CollectionOutputData {
    pub nr_found_songs: u32,
}

pub fn search_in_single_path(
    has_ui: bool,
    collection_data: SArc<SMutex<Container>>,
    collection_protected: SArc<SMutex<Collection>>,
    mutex_to_ui_msg: SArc<SMutex<Sender<UiUpdateMsg>>>,
    index: usize,
    elem: &str,
) -> CollectionOutputData {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // send start animation for that path
        mutex_to_ui_msg
            .lock()
            .and_then(|locked_to_start_ui_message| {
                trace!("send startAnimation for path {:?}", index);
                locked_to_start_ui_message
                    .send(UiUpdateMsg::CollectionUpdate(
                        CollectionPathAlive::BusyPath(index),
                        Status::ON,
                    ))
                    .unwrap_or_else(|_| warn!("... lost start animation for index {:?}", index));
                trace!("start busy animation");
                Ok(())
            })
            .unwrap_or_else(|_| error!("... that should not happen here at start"));
    }
    // this collection lock is fine, it's not the main Container, only some data
    let locked_collection = &mut *collection_protected.lock().unwrap();

    // do it: main task here is to visit and dive deep
    //        into the subfolders of this folder
    // todo: in some time, check if path a subpath of another also being searched
    //       super-path (which I use for testing for duplicates), and exclude the subpath!
    match locked_collection.visit_path(
        collection_data,
        Path::new(elem),
        &collection::Collection::visit_files,
    ) {
        Ok(local_stats) => {
            if has_ui {
                // send stop animation for that path
                trace!("send stopAnimation for path {:?}", index);
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_to_stop_ui_message| {
                        locked_to_stop_ui_message
                            .send(UiUpdateMsg::CollectionUpdate(
                                CollectionPathAlive::BusyPath(index),
                                Status::OFF,
                            ))
                            .unwrap_or_else(|_| warn!("... lost stop animation for {:?}", index));
                        trace!("stop busy animation");
                        Ok(())
                    })
                    .unwrap_or_else(|_| {
                        error!("... that should not happen here at stop");
                    });
            } else {
                let text = format!(
                    "\n\
                             analyzed: {an:>width$}, faulty: {fa:>width$}\n\
                             searched: {se:>width$}, other: {ot:>width$}",
                    an = local_stats.analyzed,
                    fa = local_stats.faulty,
                    se = local_stats.searched,
                    ot = local_stats.other,
                    width = 3
                );
                println!("[{:?}] done {}", index, text);
            }
            // return this here
            CollectionOutputData {
                nr_found_songs: local_stats.analyzed,
            }
        }
        Err(_e) => {
            let text = format!("An error has occurred in search path [{}]!!", index);
            if has_ui {
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_update_message| {
                        locked_update_message
                            .send(UiUpdateMsg::NetUpdate(ForwardNetMessage::Stats(
                                NetMessages::Debug(text),
                            )))
                            .unwrap();
                        Ok(())
                    })
                    .unwrap();
            } else {
                println!("{:?}", text);
            }
            // return this here
            CollectionOutputData { nr_found_songs: 0 }
        }
    }
}
<|endoftext|>


<commit_before>use std::error;
use std::fmt;
use std::str::{self, FromStr};

use serde::{de, ser};

/// A parsed TOML datetime value
///
/// This structure is intended to represent the datetime primitive type that can
/// be encoded into TOML documents. This type is a parsed version that contains
/// all metadata internally.
///
/// Currently this type is intentionally conservative and only supports
/// `to_string` as an accessor. Over time though it's intended that it'll grow
/// more support!
///
/// Note that if you're using `Deserialize` to deserialize a TOML document, you
/// can use this as a placeholder for where you're expecting a datetime to be
/// specified.
///
/// Also note though that while this type implements `Serialize` and
/// `Deserialize` it's only recommended to use this type with the TOML format,
/// otherwise encoded in other formats it may look a little odd.
#[derive(PartialEq, Clone)]
pub struct Datetime {
    date: Option<Date>,
    time: Option<Time>,
    offset: Option<Offset>,
}

/// Error returned from parsing a `Datetime` in the `FromStr` implementation.
#[derive(Debug, Clone)]
pub struct DatetimeParseError {
    _private: (),
}

// Currently serde itself doesn't have a datetime type, so we map our `Datetime`
// to a special valid in the serde data model. Namely one with thiese special
// fields/struct names.
//
// In general the TOML encoder/decoder will catch this and not literally emit
// these strings but rather emit datetimes as they're intended.
pub const FIELD: &str = "$__toml_private_datetime";
pub const NAME: &str = "$__toml_private_Datetime";

#[derive(PartialEq, Clone)]
struct Date {
    year: u16,
    month: u8,
    day: u8,
}

#[derive(PartialEq, Clone)]
struct Time {
    hour: u8,
    minute: u8,
    second: u8,
    nanosecond: u32,
}

#[derive(PartialEq, Clone)]
enum Offset {
    Z,
    Custom { hours: i8, minutes: u8 },
}

impl fmt::Debug for Datetime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

impl fmt::Display for Datetime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(ref date) = self.date {
            write!(f, "{}", date)?;
        }
        if let Some(ref time) = self.time {
            if self.date.is_some() {
                write!(f, "T")?;
            }
            write!(f, "{}", time)?;
        }
        if let Some(ref offset) = self.offset {
            write!(f, "{}", offset)?;
        }
        Ok(())
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
    }
}

impl fmt::Display for Time {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
        if self.nanosecond != 0 {
            let s = format!("{:09}", self.nanosecond);
            write!(f, ".{}", s.trim_end_matches('0'))?;
        }
        Ok(())
    }
}

impl fmt::Display for Offset {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Offset::Z => write!(f, "Z"),
            Offset::Custom { hours, minutes } => write!(f, "{:+03}:{:02}", hours, minutes),
        }
    }
}

impl FromStr for Datetime {
    type Err = DatetimeParseError;

    fn from_str(date: &str) -> Result<Datetime, DatetimeParseError> {
        // Accepted formats:
        //
        // 0000-00-00T00:00:00.00Z
        // 0000-00-00T00:00:00.00
        // 0000-00-00
        // 00:00:00.00
        if date.len() < 3 {
            return Err(DatetimeParseError { _private: () });
        }
        let mut offset_allowed = true;
        let mut chars = date.chars();

        // First up, parse the full date if we can
        let full_date = if chars.clone().nth(2) == Some(':') {
            offset_allowed = false;
            None
        } else {
            let y1 = u16::from(digit(&mut chars)?);
            let y2 = u16::from(digit(&mut chars)?);
            let y3 = u16::from(digit(&mut chars)?);
            let y4 = u16::from(digit(&mut chars)?);

            match chars.next() {
                Some('-') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }

            let m1 = digit(&mut chars)?;
            let m2 = digit(&mut chars)?;

            match chars.next() {
                Some('-') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }

            let d1 = digit(&mut chars)?;
            let d2 = digit(&mut chars)?;

            let date = Date {
                year: y1 * 1000 + y2 * 100 + y3 * 10 + y4,
                month: m1 * 10 + m2,
                day: d1 * 10 + d2,
            };

            if date.month < 1 || date.month > 12 {
                return Err(DatetimeParseError { _private: () });
            }
            if date.day < 1 || date.day > 31 {
                return Err(DatetimeParseError { _private: () });
            }

            Some(date)
        };

        // Next parse the "partial-time" if available
        let next = chars.clone().next();
        let partial_time = if full_date.is_some()
            && (next == Some('T') || next == Some('t') || next == Some(' '))
        {
            chars.next();
            true
        } else {
            full_date.is_none()
        };

        let time = if partial_time {
            let h1 = digit(&mut chars)?;
            let h2 = digit(&mut chars)?;
            match chars.next() {
                Some(':') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }
            let m1 = digit(&mut chars)?;
            let m2 = digit(&mut chars)?;
            match chars.next() {
                Some(':') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }
            let s1 = digit(&mut chars)?;
            let s2 = digit(&mut chars)?;

            let mut nanosecond = 0;
            if chars.clone().next() == Some('.') {
                chars.next();
                let whole = chars.as_str();

                let mut end = whole.len();
                for (i, byte) in whole.bytes().enumerate() {
                    match byte {
                        b'0'..=b'9' => {
                            if i < 9 {
                                let p = 10_u32.pow(8 - i as u32);
                                nanosecond += p * u32::from(byte - b'0');
                            }
                        }
                        _ => {
                            end = i;
                            break;
                        }
                    }
                }
                if end == 0 {
                    return Err(DatetimeParseError { _private: () });
                }
                chars = whole[end..].chars();
            }

            let time = Time {
                hour: h1 * 10 + h2,
                minute: m1 * 10 + m2,
                second: s1 * 10 + s2,
                nanosecond,
            };

            if time.hour > 24 {
                return Err(DatetimeParseError { _private: () });
            }
            if time.minute > 59 {
                return Err(DatetimeParseError { _private: () });
            }
            if time.second > 59 {
                return Err(DatetimeParseError { _private: () });
            }
            if time.nanosecond > 999_999_999 {
                return Err(DatetimeParseError { _private: () });
            }

            Some(time)
        } else {
            offset_allowed = false;
            None
        };

        // And finally, parse the offset
        let offset = if offset_allowed {
            let next = chars.clone().next();
            if next == Some('Z') || next == Some('z') {
                chars.next();
                Some(Offset::Z)
            } else if next.is_none() {
                None
            } else {
                let sign = match next {
                    Some('+') => 1,
                    Some('-') => -1,
                    _ => return Err(DatetimeParseError { _private: () }),
                };
                chars.next();
                let h1 = digit(&mut chars)? as i8;
                let h2 = digit(&mut chars)? as i8;
                match chars.next() {
                    Some(':') => {}
                    _ => return Err(DatetimeParseError { _private: () }),
                }
                let m1 = digit(&mut chars)?;
                let m2 = digit(&mut chars)?;

                Some(Offset::Custom {
                    hours: sign * (h1 * 10 + h2),
                    minutes: m1 * 10 + m2,
                })
            }
        } else {
            None
        };

        // Return an error if we didn't hit eof, otherwise return our parsed
        // date
        if chars.next().is_some() {
            return Err(DatetimeParseError { _private: () });
        }

        Ok(Datetime {
            date: full_date,
            time,
            offset,
        })
    }
}

fn digit(chars: &mut str::Chars<'_>) -> Result<u8, DatetimeParseError> {
    match chars.next() {
        Some(c) if '0' <= c && c <= '9' => Ok(c as u8 - b'0'),
        _ => Err(DatetimeParseError { _private: () }),
    }
}

impl ser::Serialize for Datetime {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        use serde::ser::SerializeStruct;

        let mut s = serializer.serialize_struct(NAME, 1)?;
        s.serialize_field(FIELD, &self.to_string())?;
        s.end()
    }
}

impl<'de> de::Deserialize<'de> for Datetime {
    fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct DatetimeVisitor;

        impl<'de> de::Visitor<'de> for DatetimeVisitor {
            type Value = Datetime;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("a TOML datetime")
            }

            fn visit_map<V>(self, mut visitor: V) -> Result<Datetime, V::Error>
            where
                V: de::MapAccess<'de>,
            {
                let value = visitor.next_key::<DatetimeKey>()?;
                if value.is_none() {
                    return Err(de::Error::custom("datetime key not found"));
                }
                let v: DatetimeFromString = visitor.next_value()?;
                Ok(v.value)
            }
        }

        static FIELDS: [&str; 1] = [FIELD];
        deserializer.deserialize_struct(NAME, &FIELDS, DatetimeVisitor)
    }
}

struct DatetimeKey;

impl<'de> de::Deserialize<'de> for DatetimeKey {
    fn deserialize<D>(deserializer: D) -> Result<DatetimeKey, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct FieldVisitor;

        impl<'de> de::Visitor<'de> for FieldVisitor {
            type Value = ();

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("a valid datetime field")
            }

            fn visit_str<E>(self, s: &str) -> Result<(), E>
            where
                E: de::Error,
            {
                if s == FIELD {
                    Ok(())
                } else {
                    Err(de::Error::custom("expected field with custom name"))
                }
            }
        }

        deserializer.deserialize_identifier(FieldVisitor)?;
        Ok(DatetimeKey)
    }
}

pub struct DatetimeFromString {
    pub value: Datetime,
}

impl<'de> de::Deserialize<'de> for DatetimeFromString {
    fn deserialize<D>(deserializer: D) -> Result<DatetimeFromString, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct Visitor;

        impl<'de> de::Visitor<'de> for Visitor {
            type Value = DatetimeFromString;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("string containing a datetime")
            }

            fn visit_str<E>(self, s: &str) -> Result<DatetimeFromString, E>
            where
                E: de::Error,
            {
                match s.parse() {
                    Ok(date) => Ok(DatetimeFromString { value: date }),
                    Err(e) => Err(de::Error::custom(e)),
                }
            }
        }

        deserializer.deserialize_str(Visitor)
    }
}

impl fmt::Display for DatetimeParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "failed to parse datetime".fmt(f)
    }
}

impl error::Error for DatetimeParseError {}
<commit_msg>Fix comment typo<commit_after>use std::error;
use std::fmt;
use std::str::{self, FromStr};

use serde::{de, ser};

/// A parsed TOML datetime value
///
/// This structure is intended to represent the datetime primitive type that can
/// be encoded into TOML documents. This type is a parsed version that contains
/// all metadata internally.
///
/// Currently this type is intentionally conservative and only supports
/// `to_string` as an accessor. Over time though it's intended that it'll grow
/// more support!
///
/// Note that if you're using `Deserialize` to deserialize a TOML document, you
/// can use this as a placeholder for where you're expecting a datetime to be
/// specified.
///
/// Also note though that while this type implements `Serialize` and
/// `Deserialize` it's only recommended to use this type with the TOML format,
/// otherwise encoded in other formats it may look a little odd.
#[derive(PartialEq, Clone)]
pub struct Datetime {
    date: Option<Date>,
    time: Option<Time>,
    offset: Option<Offset>,
}

/// Error returned from parsing a `Datetime` in the `FromStr` implementation.
#[derive(Debug, Clone)]
pub struct DatetimeParseError {
    _private: (),
}

// Currently serde itself doesn't have a datetime type, so we map our `Datetime`
// to a special valid in the serde data model. Namely one with these special
// fields/struct names.
//
// In general the TOML encoder/decoder will catch this and not literally emit
// these strings but rather emit datetimes as they're intended.
pub const FIELD: &str = "$__toml_private_datetime";
pub const NAME: &str = "$__toml_private_Datetime";

#[derive(PartialEq, Clone)]
struct Date {
    year: u16,
    month: u8,
    day: u8,
}

#[derive(PartialEq, Clone)]
struct Time {
    hour: u8,
    minute: u8,
    second: u8,
    nanosecond: u32,
}

#[derive(PartialEq, Clone)]
enum Offset {
    Z,
    Custom { hours: i8, minutes: u8 },
}

impl fmt::Debug for Datetime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

impl fmt::Display for Datetime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(ref date) = self.date {
            write!(f, "{}", date)?;
        }
        if let Some(ref time) = self.time {
            if self.date.is_some() {
                write!(f, "T")?;
            }
            write!(f, "{}", time)?;
        }
        if let Some(ref offset) = self.offset {
            write!(f, "{}", offset)?;
        }
        Ok(())
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
    }
}

impl fmt::Display for Time {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
        if self.nanosecond != 0 {
            let s = format!("{:09}", self.nanosecond);
            write!(f, ".{}", s.trim_end_matches('0'))?;
        }
        Ok(())
    }
}

impl fmt::Display for Offset {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Offset::Z => write!(f, "Z"),
            Offset::Custom { hours, minutes } => write!(f, "{:+03}:{:02}", hours, minutes),
        }
    }
}

impl FromStr for Datetime {
    type Err = DatetimeParseError;

    fn from_str(date: &str) -> Result<Datetime, DatetimeParseError> {
        // Accepted formats:
        //
        // 0000-00-00T00:00:00.00Z
        // 0000-00-00T00:00:00.00
        // 0000-00-00
        // 00:00:00.00
        if date.len() < 3 {
            return Err(DatetimeParseError { _private: () });
        }
        let mut offset_allowed = true;
        let mut chars = date.chars();

        // First up, parse the full date if we can
        let full_date = if chars.clone().nth(2) == Some(':') {
            offset_allowed = false;
            None
        } else {
            let y1 = u16::from(digit(&mut chars)?);
            let y2 = u16::from(digit(&mut chars)?);
            let y3 = u16::from(digit(&mut chars)?);
            let y4 = u16::from(digit(&mut chars)?);

            match chars.next() {
                Some('-') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }

            let m1 = digit(&mut chars)?;
            let m2 = digit(&mut chars)?;

            match chars.next() {
                Some('-') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }

            let d1 = digit(&mut chars)?;
            let d2 = digit(&mut chars)?;

            let date = Date {
                year: y1 * 1000 + y2 * 100 + y3 * 10 + y4,
                month: m1 * 10 + m2,
                day: d1 * 10 + d2,
            };

            if date.month < 1 || date.month > 12 {
                return Err(DatetimeParseError { _private: () });
            }
            if date.day < 1 || date.day > 31 {
                return Err(DatetimeParseError { _private: () });
            }

            Some(date)
        };

        // Next parse the "partial-time" if available
        let next = chars.clone().next();
        let partial_time = if full_date.is_some()
            && (next == Some('T') || next == Some('t') || next == Some(' '))
        {
            chars.next();
            true
        } else {
            full_date.is_none()
        };

        let time = if partial_time {
            let h1 = digit(&mut chars)?;
            let h2 = digit(&mut chars)?;
            match chars.next() {
                Some(':') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }
            let m1 = digit(&mut chars)?;
            let m2 = digit(&mut chars)?;
            match chars.next() {
                Some(':') => {}
                _ => return Err(DatetimeParseError { _private: () }),
            }
            let s1 = digit(&mut chars)?;
            let s2 = digit(&mut chars)?;

            let mut nanosecond = 0;
            if chars.clone().next() == Some('.') {
                chars.next();
                let whole = chars.as_str();

                let mut end = whole.len();
                for (i, byte) in whole.bytes().enumerate() {
                    match byte {
                        b'0'..=b'9' => {
                            if i < 9 {
                                let p = 10_u32.pow(8 - i as u32);
                                nanosecond += p * u32::from(byte - b'0');
                            }
                        }
                        _ => {
                            end = i;
                            break;
                        }
                    }
                }
                if end == 0 {
                    return Err(DatetimeParseError { _private: () });
                }
                chars = whole[end..].chars();
            }

            let time = Time {
                hour: h1 * 10 + h2,
                minute: m1 * 10 + m2,
                second: s1 * 10 + s2,
                nanosecond,
            };

            if time.hour > 24 {
                return Err(DatetimeParseError { _private: () });
            }
            if time.minute > 59 {
                return Err(DatetimeParseError { _private: () });
            }
            if time.second > 59 {
                return Err(DatetimeParseError { _private: () });
            }
            if time.nanosecond > 999_999_999 {
                return Err(DatetimeParseError { _private: () });
            }

            Some(time)
        } else {
            offset_allowed = false;
            None
        };

        // And finally, parse the offset
        let offset = if offset_allowed {
            let next = chars.clone().next();
            if next == Some('Z') || next == Some('z') {
                chars.next();
                Some(Offset::Z)
            } else if next.is_none() {
                None
            } else {
                let sign = match next {
                    Some('+') => 1,
                    Some('-') => -1,
                    _ => return Err(DatetimeParseError { _private: () }),
                };
                chars.next();
                let h1 = digit(&mut chars)? as i8;
                let h2 = digit(&mut chars)? as i8;
                match chars.next() {
                    Some(':') => {}
                    _ => return Err(DatetimeParseError { _private: () }),
                }
                let m1 = digit(&mut chars)?;
                let m2 = digit(&mut chars)?;

                Some(Offset::Custom {
                    hours: sign * (h1 * 10 + h2),
                    minutes: m1 * 10 + m2,
                })
            }
        } else {
            None
        };

        // Return an error if we didn't hit eof, otherwise return our parsed
        // date
        if chars.next().is_some() {
            return Err(DatetimeParseError { _private: () });
        }

        Ok(Datetime {
            date: full_date,
            time,
            offset,
        })
    }
}

fn digit(chars: &mut str::Chars<'_>) -> Result<u8, DatetimeParseError> {
    match chars.next() {
        Some(c) if '0' <= c && c <= '9' => Ok(c as u8 - b'0'),
        _ => Err(DatetimeParseError { _private: () }),
    }
}

impl ser::Serialize for Datetime {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        use serde::ser::SerializeStruct;

        let mut s = serializer.serialize_struct(NAME, 1)?;
        s.serialize_field(FIELD, &self.to_string())?;
        s.end()
    }
}

impl<'de> de::Deserialize<'de> for Datetime {
    fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct DatetimeVisitor;

        impl<'de> de::Visitor<'de> for DatetimeVisitor {
            type Value = Datetime;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("a TOML datetime")
            }

            fn visit_map<V>(self, mut visitor: V) -> Result<Datetime, V::Error>
            where
                V: de::MapAccess<'de>,
            {
                let value = visitor.next_key::<DatetimeKey>()?;
                if value.is_none() {
                    return Err(de::Error::custom("datetime key not found"));
                }
                let v: DatetimeFromString = visitor.next_value()?;
                Ok(v.value)
            }
        }

        static FIELDS: [&str; 1] = [FIELD];
        deserializer.deserialize_struct(NAME, &FIELDS, DatetimeVisitor)
    }
}

struct DatetimeKey;

impl<'de> de::Deserialize<'de> for DatetimeKey {
    fn deserialize<D>(deserializer: D) -> Result<DatetimeKey, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct FieldVisitor;

        impl<'de> de::Visitor<'de> for FieldVisitor {
            type Value = ();

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("a valid datetime field")
            }

            fn visit_str<E>(self, s: &str) -> Result<(), E>
            where
                E: de::Error,
            {
                if s == FIELD {
                    Ok(())
                } else {
                    Err(de::Error::custom("expected field with custom name"))
                }
            }
        }

        deserializer.deserialize_identifier(FieldVisitor)?;
        Ok(DatetimeKey)
    }
}

pub struct DatetimeFromString {
    pub value: Datetime,
}

impl<'de> de::Deserialize<'de> for DatetimeFromString {
    fn deserialize<D>(deserializer: D) -> Result<DatetimeFromString, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct Visitor;

        impl<'de> de::Visitor<'de> for Visitor {
            type Value = DatetimeFromString;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("string containing a datetime")
            }

            fn visit_str<E>(self, s: &str) -> Result<DatetimeFromString, E>
            where
                E: de::Error,
            {
                match s.parse() {
                    Ok(date) => Ok(DatetimeFromString { value: date }),
                    Err(e) => Err(de::Error::custom(e)),
                }
            }
        }

        deserializer.deserialize_str(Visitor)
    }
}

impl fmt::Display for DatetimeParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "failed to parse datetime".fmt(f)
    }
}

impl error::Error for DatetimeParseError {}
<|endoftext|>


<commit_before>use DatabaseName;
use DocumentId;
use Error;
use Revision;
use serde;
use serde_json;
use serializable_document::SerializableDocument;
use std;
use transport::{HyperTransport, Transport};

mod state {

    use Attachment;
    use AttachmentName;
    use DatabaseName;
    use DocumentId;
    use Revision;
    use std;
    use transport::Transport;

    // Base contains meta-information for all documents, including deleted
    // documents.
    #[derive(Debug)]
    pub struct Base<T: Transport> {
        pub transport: std::sync::Arc<T>,
        pub db_name: DatabaseName,
        pub doc_id: DocumentId,
        pub revision: Revision,
    }

    // Extra contains meta-information for non-deleted documents that doesn't
    // exist for deleted documents.
    #[derive(Debug, Default)]
    pub struct Extra {
        pub attachments: std::collections::HashMap<AttachmentName, Attachment>,
    }
}

pub type Document = BasicDocument<HyperTransport>;

#[derive(Debug)]
pub enum BasicDocument<T: Transport> {
    #[doc(hidden)]
    Deleted {
        base: state::Base<T>,
    },

    #[doc(hidden)]
    Exists {
        base: state::Base<T>,
        extra: state::Extra,
        content: serde_json::Value,
    },
}

impl<T: Transport> BasicDocument<T> {
    #[doc(hidden)]
    pub fn from_serializable_document(transport: &std::sync::Arc<T>,
                                      db_name: &DatabaseName,
                                      doc: SerializableDocument)
                                      -> Self {

        let base = state::Base {
            transport: transport.clone(),
            db_name: db_name.clone(),
            doc_id: doc.id,
            revision: doc.revision,
        };

        match doc.deleted {
            true => BasicDocument::Deleted { base: base },
            false => {
                BasicDocument::Exists {
                    base: base,
                    extra: state::Extra { attachments: doc.attachments },
                    content: doc.content,
                }
            }

        }
    }

    pub fn database_name(&self) -> &DatabaseName {
        match self {
            &BasicDocument::Deleted { ref base, .. } => &base.db_name,
            &BasicDocument::Exists { ref base, .. } => &base.db_name,
        }
    }

    pub fn id(&self) -> &DocumentId {
        match self {
            &BasicDocument::Deleted { ref base, .. } => &base.doc_id,
            &BasicDocument::Exists { ref base, .. } => &base.doc_id,
        }
    }

    pub fn revision(&self) -> &Revision {
        match self {
            &BasicDocument::Deleted { ref base, .. } => &base.revision,
            &BasicDocument::Exists { ref base, .. } => &base.revision,
        }
    }

    pub fn get_content<C: serde::Deserialize>(&self) -> Result<C, Error> {
        match self {
            &BasicDocument::Deleted { .. } => Err(Error::DocumentIsDeleted),
            &BasicDocument::Exists { ref content, .. } => {
                serde_json::from_value(content.clone()).map_err(|e| Error::JsonDecode { cause: e })
            }
        }
    }

    pub fn set_content<C: serde::Serialize>(&mut self, new_content: &C) -> Result<(), Error> {
        match self {
            &mut BasicDocument::Deleted { .. } => Err(Error::DocumentIsDeleted),
            &mut BasicDocument::Exists { ref mut content, .. } => {
                *content = serde_json::to_value(new_content);
                Ok(())
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use DatabaseName;
    use DocumentId;
    use Error;
    use Revision;
    use serde_json;
    use std;
    use super::{BasicDocument, state};
    use transport::MockTransport;

    fn new_mock_base<N, I>(db_name: N, doc_id: I, revision: Revision) -> state::Base<MockTransport>
        where N: Into<DatabaseName>,
              I: Into<DocumentId>
    {
        state::Base {
            transport: std::sync::Arc::new(MockTransport::new()),
            db_name: db_name.into(),
            doc_id: doc_id.into(),
            revision: revision,
        }
    }

    #[test]
    fn get_content_ok() {

        let content = serde_json::builder::ObjectBuilder::new()
                          .insert("field_1", 42)
                          .insert("field_2", "foo")
                          .unwrap();

        let doc = BasicDocument::Exists {
            base: new_mock_base("database_name",
                                "document_id",
                                "1-1234567890abcdef1234567890abcdef".parse().unwrap()),
            extra: state::Extra { attachments: std::collections::HashMap::new() },
            content: content.clone(),
        };

        let expected = content;
        let got = doc.get_content().unwrap();
        assert_eq!(expected, got);
    }

    #[test]
    fn get_content_nok_document_is_deleted() {

        let doc = BasicDocument::Deleted {
            base: new_mock_base("database_name",
                                "document_id",
                                "1-1234567890abcdef1234567890abcdef".parse().unwrap()),
        };

        match doc.get_content::<serde_json::Value>() {
            Err(Error::DocumentIsDeleted) => (),
            x @ _ => unexpected_result!(x),
        }
    }

    #[test]
    fn get_content_nok_decode_error() {

        let doc = BasicDocument::Exists {
            base: new_mock_base("database_name",
                                "document_id",
                                "1-1234567890abcdef1234567890abcdef".parse().unwrap()),
            extra: Default::default(),
            content: serde_json::builder::ObjectBuilder::new().unwrap(),
        };

        match doc.get_content::<i32>() {
            Err(Error::JsonDecode { .. }) => (),
            x @ _ => unexpected_result!(x),
        }
    }
}
<commit_msg>Fold document::state submodule into its parent<commit_after>use Attachment;
use AttachmentName;
use DatabaseName;
use DocumentId;
use Error;
use Revision;
use serde;
use serde_json;
use serializable_document::SerializableDocument;
use std;
use transport::{HyperTransport, Transport};

// Base contains meta-information for all documents, including deleted
// documents.
#[derive(Debug)]
pub struct DocumentBase<T: Transport> {
    pub transport: std::sync::Arc<T>,
    pub db_name: DatabaseName,
    pub doc_id: DocumentId,
    pub revision: Revision,
}

// Extra contains meta-information for non-deleted documents that doesn't
// exist for deleted documents.
#[derive(Debug, Default)]
pub struct DocumentExtra {
    pub attachments: std::collections::HashMap<AttachmentName, Attachment>,
}

pub type Document = BasicDocument<HyperTransport>;

#[derive(Debug)]
pub enum BasicDocument<T: Transport> {
    #[doc(hidden)]
    Deleted {
        base: DocumentBase<T>,
    },

    #[doc(hidden)]
    Exists {
        base: DocumentBase<T>,
        extra: DocumentExtra,
        content: serde_json::Value,
    },
}

impl<T: Transport> BasicDocument<T> {
    #[doc(hidden)]
    pub fn from_serializable_document(transport: &std::sync::Arc<T>,
                                      db_name: &DatabaseName,
                                      doc: SerializableDocument)
                                      -> Self {

        let base = DocumentBase {
            transport: transport.clone(),
            db_name: db_name.clone(),
            doc_id: doc.id,
            revision: doc.revision,
        };

        match doc.deleted {
            true => BasicDocument::Deleted { base: base },
            false => {
                BasicDocument::Exists {
                    base: base,
                    extra: DocumentExtra { attachments: doc.attachments },
                    content: doc.content,
                }
            }

        }
    }

    pub fn database_name(&self) -> &DatabaseName {
        match self {
            &BasicDocument::Deleted { ref base, .. } => &base.db_name,
            &BasicDocument::Exists { ref base, .. } => &base.db_name,
        }
    }

    pub fn id(&self) -> &DocumentId {
        match self {
            &BasicDocument::Deleted { ref base, .. } => &base.doc_id,
            &BasicDocument::Exists { ref base, .. } => &base.doc_id,
        }
    }

    pub fn revision(&self) -> &Revision {
        match self {
            &BasicDocument::Deleted { ref base, .. } => &base.revision,
            &BasicDocument::Exists { ref base, .. } => &base.revision,
        }
    }

    pub fn get_content<C: serde::Deserialize>(&self) -> Result<C, Error> {
        match self {
            &BasicDocument::Deleted { .. } => Err(Error::DocumentIsDeleted),
            &BasicDocument::Exists { ref content, .. } => {
                serde_json::from_value(content.clone()).map_err(|e| Error::JsonDecode { cause: e })
            }
        }
    }

    pub fn set_content<C: serde::Serialize>(&mut self, new_content: &C) -> Result<(), Error> {
        match self {
            &mut BasicDocument::Deleted { .. } => Err(Error::DocumentIsDeleted),
            &mut BasicDocument::Exists { ref mut content, .. } => {
                *content = serde_json::to_value(new_content);
                Ok(())
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use DatabaseName;
    use DocumentId;
    use Error;
    use Revision;
    use serde_json;
    use std;
    use super::{BasicDocument, DocumentBase, DocumentExtra};
    use transport::MockTransport;

    fn new_mock_base<N, I>(db_name: N, doc_id: I, revision: Revision) -> DocumentBase<MockTransport>
        where N: Into<DatabaseName>,
              I: Into<DocumentId>
    {
        DocumentBase {
            transport: std::sync::Arc::new(MockTransport::new()),
            db_name: db_name.into(),
            doc_id: doc_id.into(),
            revision: revision,
        }
    }

    #[test]
    fn get_content_ok() {

        let content = serde_json::builder::ObjectBuilder::new()
                          .insert("field_1", 42)
                          .insert("field_2", "foo")
                          .unwrap();

        let doc = BasicDocument::Exists {
            base: new_mock_base("database_name",
                                "document_id",
                                "1-1234567890abcdef1234567890abcdef".parse().unwrap()),
            extra: DocumentExtra { attachments: std::collections::HashMap::new() },
            content: content.clone(),
        };

        let expected = content;
        let got = doc.get_content().unwrap();
        assert_eq!(expected, got);
    }

    #[test]
    fn get_content_nok_document_is_deleted() {

        let doc = BasicDocument::Deleted {
            base: new_mock_base("database_name",
                                "document_id",
                                "1-1234567890abcdef1234567890abcdef".parse().unwrap()),
        };

        match doc.get_content::<serde_json::Value>() {
            Err(Error::DocumentIsDeleted) => (),
            x @ _ => unexpected_result!(x),
        }
    }

    #[test]
    fn get_content_nok_decode_error() {

        let doc = BasicDocument::Exists {
            base: new_mock_base("database_name",
                                "document_id",
                                "1-1234567890abcdef1234567890abcdef".parse().unwrap()),
            extra: Default::default(),
            content: serde_json::builder::ObjectBuilder::new().unwrap(),
        };

        match doc.get_content::<i32>() {
            Err(Error::JsonDecode { .. }) => (),
            x @ _ => unexpected_result!(x),
        }
    }
}
<|endoftext|>