<commit_before>use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use tensorflow_sys as tf;

use crate::eager::Context;
use crate::{AnyTensor, DataType, Result, Status, Tensor, TensorType};

/// A handle to a tensor on a device.
///
/// Constructing a TensorHandle requires a reference to an execute context so that the
/// generated handle will not out live the context.
/// ```
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let opts = ContextOptions::new();
/// let ctx = Context::new(opts).unwrap();
///
/// let t = Tensor::from(&[3i32]);
/// let h = TensorHandle::new(&ctx, &t).unwrap();
/// let v: Tensor<i32> = h.resolve().unwrap();
/// assert_eq!(&v[..], &[3i32]);
/// ```
///
/// Since TensorHandle cannot be alive beyond the lifetime of the context, the following
/// code will not compile.
/// ```compile_fail
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let h = {
///     let opts = ContextOptions::new();
///     let ctx = Context::new(opts).unwrap();
///
///     let t = Tensor::from(&[3i32]);
///     let h = TensorHandle::new(&ctx, &t).unwrap();
///     h
/// };
/// ```
#[derive(Debug)]
pub struct TensorHandle<'a> {
    inner: *mut tf::TFE_TensorHandle,
    // TensorHandle should not live longer than a given context.
    ctx: PhantomData<&'a Context>,
}

impl<'a> Drop for TensorHandle<'a> {
    fn drop(&mut self) {
        unsafe {
            tf::TFE_DeleteTensorHandle(self.inner);
        }
    }
}

impl<'a> TensorHandle<'a> {
    /// Create a TensorHandle from the input Tensor
    pub fn new<T: TensorType>(_ctx: &'a Context, t: &Tensor<T>) -> Result<TensorHandle<'a>> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_NewTensorHandle(t.inner()?, status.inner) };

        if inner.is_null() {
            Err(status)
        } else {
            Ok(TensorHandle {
                inner,
                ctx: PhantomData,
            })
        }
    }

    /// Return the DataType that corresponds to this type.
    pub fn data_type(&self) -> DataType {
        unsafe { DataType::from_c(tf::TFE_TensorHandleDataType(self.inner)) }
    }

    /// Return the number of dimensions.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn num_dims(&self) -> Result<usize> {
        let status = Status::new();
        let num_dims = unsafe { tf::TFE_TensorHandleNumDims(self.inner, status.inner) };
        if status.is_ok() {
            // num_dims >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_dims as usize)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements
    pub fn num_elements(&self) -> Result<u64> {
        let status = Status::new();
        let num_elements = unsafe { tf::TFE_TensorHandleNumElements(self.inner, status.inner) };
        if status.is_ok() {
            // num_elements >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_elements as u64)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements for a given dim_index.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn dim(&self, dim_index: i32) -> Result<u64> {
        let status = Status::new();
        let dim = unsafe { tf::TFE_TensorHandleDim(self.inner, dim_index, status.inner) };
        if status.is_ok() {
            // dim >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(dim as u64)
        } else {
            Err(status)
        }
    }

    /// Return the device of the operation that produced the current TensorHandle.
    ///
    /// If the TensorHandle was produced by a copy, returns the destination device of the copy.
    /// Note that the returned device name is not always the device holding the tensor handle's memory.
    /// If you want the latter, use backing_device_name.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Returns the name of the device in whose memory underlying the current TensorHandle resides.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn backing_device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleBackingDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Return a new TensorHandle that shares the underlying tensor with the current TensorHandle.
    pub fn copy_sharing_tensor(&self) -> Result<Self> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_TensorHandleCopySharingTensor(self.inner, status.inner) };
        if status.is_ok() {
            Ok(Self {
                inner,
                ctx: self.ctx,
            })
        } else {
            Err(status)
        }
    }

    /// This function will block till the operation that produces the current TensorHandle has completed.
    /// The memory returned might alias the internal memory used by TensorFlow.
    /// Hence, callers should not mutate this memory.
    pub fn resolve<T: TensorType>(&self) -> Result<Tensor<T>> {
        let mut status = Status::new();
        let tf_tensor = unsafe { tf::TFE_TensorHandleResolve(self.inner, status.inner) };
        if !status.is_ok() {
            return Err(status);
        }
        if self.data_type() != T::data_type() {
            let msg = format!(
                "The expected data type ({}) and underlying data type ({}) did not match.",
                T::data_type(),
                self.data_type()
            );

            status.set_lossy(crate::Code::InvalidArgument, &msg);
            return Err(status);
        }

        // Safely unwrap since data_type was checked beforehand.
        unsafe { Ok(Tensor::from_tf_tensor(tf_tensor).unwrap()) }
    }

    /// Create a new TensorHandle with the same contents as the current TensorHandle but placed
    /// in the memory of the device name 'device_name'.
    /// If source and destination are the same device, then this creates a new handle
    /// that shares the underlying buffer. Otherwise, it currently requires at least
    /// one of the source or destination devices to be CPU (i.e., for the source or
    /// destination tensor to be placed in host memory).
    /// If async execution is enabled, the copy may be enqueued and the call will
    /// return "non-ready" TensorHandle. Else, this function returns after the copy has
    /// been done.
    pub fn copy_to_device<'b>(
        &self,
        ctx: &'b Context,
        device_name: &str,
    ) -> Result<TensorHandle<'b>> {
        let status = Status::new();

        let device_name = CString::new(device_name)?;
        unsafe {
            let inner = tf::TFE_TensorHandleCopyToDevice(
                self.inner,
                ctx.inner,
                device_name.as_ptr(),
                status.inner,
            );

            if status.is_ok() {
                Ok(TensorHandle {
                    inner,
                    ctx: PhantomData,
                })
            } else {
                Err(status)
            }
        }
    }
}

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

    #[test]
    fn test_tensor_handle() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();

        assert_eq!(h.data_type(), DataType::Int32);
        assert_eq!(h.num_elements().unwrap(), 6);
        assert_eq!(h.num_dims().unwrap(), 2);
        assert_eq!(h.dim(0).unwrap(), 2);
        assert_eq!(h.dim(1).unwrap(), 3);
    }

    #[test]
    fn test_copy_sharing_tensor() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();
        let h_copy = h.copy_sharing_tensor().unwrap();
        let t2 = h_copy.resolve::<i32>().unwrap();

        // t and t2 may share the same memory, but it's difficuly to check
        // since the `resolve` does not guarantee that.
        assert_eq!(&t[..], &t2[..]);
    }

    #[cfg(feature = "tensorflow_gpu")]
    #[test]
    fn test_copy_to_device() {
        let values = [0_i32, 1, 2, 3];
        let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";

        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();
        let devices = ctx.device_list().unwrap();
        assert!(
            devices.iter().any(|d| d.device_type == "GPU"),
            "Skip test_copy_to_device because no GPU device was found"
        );

        let t = Tensor::new(&[2, 2]).with_values(&values).unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();
        let h_gpu = TensorHandle::copy_to_device(&h, &ctx, target_device).unwrap();
        assert_eq!(h_gpu.device_name().unwrap(), target_device);
        let t2 = h_gpu.resolve::<i32>().unwrap();

        assert_eq!(&t[..], &t2[..]);
    }

    #[cfg(feature = "tensorflow_gpu")]
    #[test]
    fn test_copy_to_device_lifetime() {
        let values = [0_i32, 1, 2, 3];
        let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";

        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();
        let devices = ctx.device_list().unwrap();
        assert!(
            devices.iter().any(|d| d.device_type == "GPU"),
            "Skip test_copy_to_device because no GPU device was found"
        );

        let h_gpu = {
            // Create a temporal Context
            let opts = ContextOptions::new();
            let ctx2 = Context::new(opts).unwrap();
            let t = Tensor::new(&[2, 2]).with_values(&values).unwrap();

            // Create a TensorHandle managed by the context `ctx2`.
            let h = TensorHandle::new(&ctx2, &t).unwrap();

            // Copy to GPU. This creates a new handle managed by the context `ctx`.
            h.copy_to_device(&ctx, target_device).unwrap()
        };
        assert_eq!(h_gpu.device_name().unwrap(), target_device);
        let t2 = h_gpu.resolve::<i32>().unwrap();

        assert_eq!(&values[..], &t2[..]);
    }
}
<commit_msg>Ignore unittest that requires GPU by default.<commit_after>use std::ffi::{CStr, CString};
use std::marker::PhantomData;

use tensorflow_sys as tf;

use crate::eager::Context;
use crate::{AnyTensor, DataType, Result, Status, Tensor, TensorType};

/// A handle to a tensor on a device.
///
/// Constructing a TensorHandle requires a reference to an execute context so that the
/// generated handle will not out live the context.
/// ```
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let opts = ContextOptions::new();
/// let ctx = Context::new(opts).unwrap();
///
/// let t = Tensor::from(&[3i32]);
/// let h = TensorHandle::new(&ctx, &t).unwrap();
/// let v: Tensor<i32> = h.resolve().unwrap();
/// assert_eq!(&v[..], &[3i32]);
/// ```
///
/// Since TensorHandle cannot be alive beyond the lifetime of the context, the following
/// code will not compile.
/// ```compile_fail
/// # use tensorflow::Tensor;
/// use tensorflow::eager::{ContextOptions, Context, TensorHandle};
///
/// let h = {
///     let opts = ContextOptions::new();
///     let ctx = Context::new(opts).unwrap();
///
///     let t = Tensor::from(&[3i32]);
///     let h = TensorHandle::new(&ctx, &t).unwrap();
///     h
/// };
/// ```
#[derive(Debug)]
pub struct TensorHandle<'a> {
    inner: *mut tf::TFE_TensorHandle,
    // TensorHandle should not live longer than a given context.
    ctx: PhantomData<&'a Context>,
}

impl<'a> Drop for TensorHandle<'a> {
    fn drop(&mut self) {
        unsafe {
            tf::TFE_DeleteTensorHandle(self.inner);
        }
    }
}

impl<'a> TensorHandle<'a> {
    /// Create a TensorHandle from the input Tensor
    pub fn new<T: TensorType>(_ctx: &'a Context, t: &Tensor<T>) -> Result<TensorHandle<'a>> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_NewTensorHandle(t.inner()?, status.inner) };

        if inner.is_null() {
            Err(status)
        } else {
            Ok(TensorHandle {
                inner,
                ctx: PhantomData,
            })
        }
    }

    /// Return the DataType that corresponds to this type.
    pub fn data_type(&self) -> DataType {
        unsafe { DataType::from_c(tf::TFE_TensorHandleDataType(self.inner)) }
    }

    /// Return the number of dimensions.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn num_dims(&self) -> Result<usize> {
        let status = Status::new();
        let num_dims = unsafe { tf::TFE_TensorHandleNumDims(self.inner, status.inner) };
        if status.is_ok() {
            // num_dims >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_dims as usize)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements
    pub fn num_elements(&self) -> Result<u64> {
        let status = Status::new();
        let num_elements = unsafe { tf::TFE_TensorHandleNumElements(self.inner, status.inner) };
        if status.is_ok() {
            // num_elements >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(num_elements as u64)
        } else {
            Err(status)
        }
    }

    /// Return the number of elements for a given dim_index.
    ///
    /// This function will block till the operation that produces the TensorHandle has completed.
    pub fn dim(&self, dim_index: i32) -> Result<u64> {
        let status = Status::new();
        let dim = unsafe { tf::TFE_TensorHandleDim(self.inner, dim_index, status.inner) };
        if status.is_ok() {
            // dim >= 0 when the status is ok, so we can safely cast it to u64.
            Ok(dim as u64)
        } else {
            Err(status)
        }
    }

    /// Return the device of the operation that produced the current TensorHandle.
    ///
    /// If the TensorHandle was produced by a copy, returns the destination device of the copy.
    /// Note that the returned device name is not always the device holding the tensor handle's memory.
    /// If you want the latter, use backing_device_name.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Returns the name of the device in whose memory underlying the current TensorHandle resides.
    ///
    /// This function will block till the operation that produces the current TensorHandle has completed.
    pub fn backing_device_name(&self) -> Result<String> {
        let status = Status::new();
        unsafe {
            let device_name = tf::TFE_TensorHandleBackingDeviceName(self.inner, status.inner);
            if status.is_ok() {
                Ok(CStr::from_ptr(device_name).to_str()?.to_string())
            } else {
                Err(status)
            }
        }
    }

    /// Return a new TensorHandle that shares the underlying tensor with the current TensorHandle.
    pub fn copy_sharing_tensor(&self) -> Result<Self> {
        let status = Status::new();
        let inner = unsafe { tf::TFE_TensorHandleCopySharingTensor(self.inner, status.inner) };
        if status.is_ok() {
            Ok(Self {
                inner,
                ctx: self.ctx,
            })
        } else {
            Err(status)
        }
    }

    /// This function will block till the operation that produces the current TensorHandle has completed.
    /// The memory returned might alias the internal memory used by TensorFlow.
    /// Hence, callers should not mutate this memory.
    pub fn resolve<T: TensorType>(&self) -> Result<Tensor<T>> {
        let mut status = Status::new();
        let tf_tensor = unsafe { tf::TFE_TensorHandleResolve(self.inner, status.inner) };
        if !status.is_ok() {
            return Err(status);
        }
        if self.data_type() != T::data_type() {
            let msg = format!(
                "The expected data type ({}) and underlying data type ({}) did not match.",
                T::data_type(),
                self.data_type()
            );

            status.set_lossy(crate::Code::InvalidArgument, &msg);
            return Err(status);
        }

        // Safely unwrap since data_type was checked beforehand.
        unsafe { Ok(Tensor::from_tf_tensor(tf_tensor).unwrap()) }
    }

    /// Create a new TensorHandle with the same contents as the current TensorHandle but placed
    /// in the memory of the device name 'device_name'.
    /// If source and destination are the same device, then this creates a new handle
    /// that shares the underlying buffer. Otherwise, it currently requires at least
    /// one of the source or destination devices to be CPU (i.e., for the source or
    /// destination tensor to be placed in host memory).
    /// If async execution is enabled, the copy may be enqueued and the call will
    /// return "non-ready" TensorHandle. Else, this function returns after the copy has
    /// been done.
    pub fn copy_to_device<'b>(
        &self,
        ctx: &'b Context,
        device_name: &str,
    ) -> Result<TensorHandle<'b>> {
        let status = Status::new();

        let device_name = CString::new(device_name)?;
        unsafe {
            let inner = tf::TFE_TensorHandleCopyToDevice(
                self.inner,
                ctx.inner,
                device_name.as_ptr(),
                status.inner,
            );

            if status.is_ok() {
                Ok(TensorHandle {
                    inner,
                    ctx: PhantomData,
                })
            } else {
                Err(status)
            }
        }
    }
}

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

    #[test]
    fn test_tensor_handle() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();

        assert_eq!(h.data_type(), DataType::Int32);
        assert_eq!(h.num_elements().unwrap(), 6);
        assert_eq!(h.num_dims().unwrap(), 2);
        assert_eq!(h.dim(0).unwrap(), 2);
        assert_eq!(h.dim(1).unwrap(), 3);
    }

    #[test]
    fn test_copy_sharing_tensor() {
        let opts = ContextOptions::new();
        let ctx = Context::new(opts).unwrap();

        let t = Tensor::new(&[2, 3])
            .with_values(&[0_i32, 1, 2, 3, 4, 5])
            .unwrap();
        let h = TensorHandle::new(&ctx, &t).unwrap();
        let h_copy = h.copy_sharing_tensor().unwrap();
        let t2 = h_copy.resolve::<i32>().unwrap();

        // t and t2 may share the same memory, but it's difficuly to check
        // since the `resolve` does not guarantee that.
        assert_eq!(&t[..], &t2[..]);
    }

    /// Following tests are disabled by default because it requires a GPU and some setup.
    ///
    /// To run this test, you need to pass the `-- --ignored` arguumnet to cargo test.
    /// ```sh
    /// cargo test --features "eager tensorflow_gpu" -- --ignored
    /// ```
    #[cfg(feature = "tensorflow_gpu")]
    mod gpu {
        use super::*;
        use crate::eager::ContextOptions;

        #[test]
        #[ignore]
        fn test_copy_to_device() {
            let values = [0_i32, 1, 2, 3];
            let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";

            let opts = ContextOptions::new();
            let ctx = Context::new(opts).unwrap();
            let devices = ctx.device_list().unwrap();
            assert!(
                devices.iter().any(|d| d.device_type == "GPU"),
                "Skip test_copy_to_device because no GPU device was found"
            );

            let t = Tensor::new(&[2, 2]).with_values(&values).unwrap();
            let h = TensorHandle::new(&ctx, &t).unwrap();
            let h_gpu = TensorHandle::copy_to_device(&h, &ctx, target_device).unwrap();
            assert_eq!(h_gpu.device_name().unwrap(), target_device);
            let t2 = h_gpu.resolve::<i32>().unwrap();

            assert_eq!(&t[..], &t2[..]);
        }

        #[test]
        #[ignore]
        fn test_copy_to_device_lifetime() {
            let values = [0_i32, 1, 2, 3];
            let target_device = "/job:localhost/replica:0/task:0/device:GPU:0";

            let opts = ContextOptions::new();
            let ctx = Context::new(opts).unwrap();
            let devices = ctx.device_list().unwrap();
            assert!(
                devices.iter().any(|d| d.device_type == "GPU"),
                "Skip test_copy_to_device because no GPU device was found"
            );

            let h_gpu = {
                // Create a temporal Context
                let opts = ContextOptions::new();
                let ctx2 = Context::new(opts).unwrap();
                let t = Tensor::new(&[2, 2]).with_values(&values).unwrap();

                // Create a TensorHandle managed by the context `ctx2`.
                let h = TensorHandle::new(&ctx2, &t).unwrap();

                // Copy to GPU. This creates a new handle managed by the context `ctx`.
                h.copy_to_device(&ctx, target_device).unwrap()
            };
            assert_eq!(h_gpu.device_name().unwrap(), target_device);
            let t2 = h_gpu.resolve::<i32>().unwrap();

            assert_eq!(&values[..], &t2[..]);
        }
    }
}
<|endoftext|>


<commit_before>use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonTruthy};
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct WithHelper;

impl HelperDef for WithHelper {
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let param = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"with\"")));

        let path = rc.get_path().clone();
        rc.promote_local_vars();

        let not_empty = param.value().is_truthy();
        let template = if not_empty {
            h.template()
        } else {
            h.inverse()
        };

        if not_empty {
            if let Some(inner_path) = param.path() {
                let new_path = format!("{}/{}", path, inner_path);
                rc.set_path(new_path);
            }
        }

        let rendered = match template {
            Some(t) => t.render(c, r, rc),
            None => Ok(()),
        };

        rc.set_path(path);
        rc.demote_local_vars();
        rendered
    }
}

pub static WITH_HELPER: WithHelper = WithHelper;

#[cfg(test)]
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    struct Address {
        city: String,
        country: String,
    }

    impl ToJson for Address {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

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

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

        let t0 = Template::compile("{{#with addr}}{{city}}{{/with}}".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#with notfound}}hello{{else}}world{{/with}}".to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#with addr/country}}{{this}}{{/with}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &person);
        assert_eq!(r0.ok().unwrap(), "Beijing".to_string());

        let r1 = handlebars.render("t1", &person);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t2", &person);
        assert_eq!(r2.ok().unwrap(), "China".to_string());
    }
}
<commit_msg>Fix case where with is used inside an each with nested data access<commit_after>use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonTruthy};
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct WithHelper;

impl HelperDef for WithHelper {
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let param = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"with\"")));

        let path = rc.get_path().clone();
        rc.promote_local_vars();
        if let Some(path_root) = param.path_root() {
            let local_path_root = format!("{}/{}", rc.get_path(), path_root);
            rc.set_local_path_root(local_path_root);
        }

        let not_empty = param.value().is_truthy();
        let template = if not_empty {
            h.template()
        } else {
            h.inverse()
        };

        if not_empty {
            if let Some(inner_path) = param.path() {
                let new_path = format!("{}/{}", path, inner_path);
                rc.set_path(new_path);
            }
        }

        let rendered = match template {
            Some(t) => t.render(c, r, rc),
            None => Ok(()),
        };

        rc.set_path(path);
        rc.demote_local_vars();
        rendered
    }
}

pub static WITH_HELPER: WithHelper = WithHelper;

#[cfg(test)]
#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;
    use serialize::json::{Json, ToJson};

    struct Address {
        city: String,
        country: String,
    }

    impl ToJson for Address {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("city".to_string(), self.city.to_json());
            m.insert("country".to_string(), self.country.to_json());
            Json::Object(m)
        }
    }

    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    impl ToJson for Person {
        fn to_json(&self) -> Json {
            let mut m = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("age".to_string(), self.age.to_json());
            m.insert("addr".to_string(), self.addr.to_json());
            m.insert("titles".to_string(), self.titles.to_json());
            Json::Object(m)
        }
    }

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

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

        let t0 = Template::compile("{{#with addr}}{{city}}{{/with}}".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#with notfound}}hello{{else}}world{{/with}}".to_string())
                     .ok()
                     .unwrap();
        let t2 = Template::compile("{{#with addr/country}}{{this}}{{/with}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &person);
        assert_eq!(r0.ok().unwrap(), "Beijing".to_string());

        let r1 = handlebars.render("t1", &person);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t2", &person);
        assert_eq!(r2.ok().unwrap(), "China".to_string());
    }

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

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

        let addr2 = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

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

        let people = vec![person, person2];

        let t0 = Template::compile("{{#each this}}{{#with addr}}{{city}}{{/with}}{{/each}}".to_string()).ok().unwrap();
        let t1 = Template::compile("{{#each this}}{{#with addr}}{{../age}}{{/with}}{{/each}}".to_string()).ok().unwrap();
        let t2 = Template::compile("{{#each this}}{{#with addr}}{{@../index}}{{/with}}{{/each}}".to_string()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);
        handlebars.register_template("t2", t2);

        let r0 = handlebars.render("t0", &people);
        assert_eq!(r0.ok().unwrap(), "BeijingBeijing".to_string());

        let r1 = handlebars.render("t1", &people);
        assert_eq!(r1.ok().unwrap(), "2727".to_string());

        let r2 = handlebars.render("t2", &people);
        assert_eq!(r2.ok().unwrap(), "01".to_string());
    }
}
<|endoftext|>


<commit_before>use crate::index::diff::Error;
use crate::{Change, CrateVersion};
use bstr::BStr;
use git_repository as git;
use std::collections::BTreeSet;
use std::ops::Range;

#[derive(Default)]
pub(crate) struct Delegate {
    changes: Vec<Change>,
    delete_version_ids: BTreeSet<u64>,
    err: Option<Error>,
}

impl Delegate {
    pub fn handle(
        &mut self,
        change: git::object::tree::diff::Change<'_, '_, '_>,
    ) -> Result<git::object::tree::diff::Action, Error> {
        use git::bstr::ByteSlice;
        use git::object::tree::diff::change::Event::*;
        use git::objs::tree::EntryMode::*;
        fn entry_data(
            entry: git::objs::tree::EntryMode,
            id: git::Id<'_>,
        ) -> Result<Option<git::Object<'_>>, Error> {
            matches!(entry, Blob | BlobExecutable)
                .then(|| id.object())
                .transpose()
                .map_err(Into::into)
        }
        if change.location.contains(&b'.') {
            return Ok(Default::default());
        }
        match change.event {
            Addition { entry_mode, id } => {
                if let Some(obj) = entry_data(entry_mode, id)? {
                    for line in (&obj.data).lines() {
                        let version = version_from_json_line(line, change.location)?;
                        self.changes.push(if version.yanked {
                            Change::Yanked(version)
                        } else {
                            Change::Added(version)
                        });
                    }
                }
            }
            Deletion { entry_mode, .. } => {
                if entry_mode.is_no_tree() {
                    self.changes.push(Change::Deleted {
                        name: change.location.to_string(),
                    });
                }
            }
            Modification { .. } => {
                if let Some(diff) = change.event.diff().transpose()? {
                    let location = change.location;

                    let input = diff.line_tokens();
                    let mut err = None;
                    git::diff::blob::diff(
                        diff.algo,
                        &input,
                        |before: Range<u32>, after: Range<u32>| {
                            if err.is_some() {
                                return;
                            }
                            let mut lines_before = input.before
                                [before.start as usize..before.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut lines_after = input.after
                                [after.start as usize..after.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            match (lines_before.peek().is_some(), lines_after.peek().is_some()) {
                                (true, false) => {
                                    for removed in lines_before {
                                        match version_from_json_line(removed, location) {
                                            Ok(version) => {
                                                self.delete_version_ids.insert(version.id());
                                            }
                                            Err(e) => {
                                                err = Some(e);
                                                break;
                                            }
                                        }
                                    }
                                }
                                (false, true) => {
                                    for inserted in lines_after {
                                        match version_from_json_line(inserted, location) {
                                            Ok(version) => {
                                                self.changes.push(if version.yanked {
                                                    Change::Yanked(version)
                                                } else {
                                                    Change::Added(version)
                                                });
                                            }
                                            Err(e) => {
                                                err = Some(e);
                                                break;
                                            }
                                        }
                                    }
                                }
                                (true, true) => {
                                    // These should always be yanked, but we check it anyway.
                                    for inserted in lines_after {
                                        match version_from_json_line(inserted, location) {
                                            Ok(version) => {
                                                self.changes.push(if version.yanked {
                                                    Change::Yanked(version)
                                                } else {
                                                    Change::Added(version)
                                                });
                                            }
                                            Err(e) => {
                                                err = Some(e);
                                                break;
                                            }
                                        }
                                    }
                                }
                                (false, false) => {}
                            }
                        },
                    );
                    if let Some(err) = err {
                        return Err(err.into());
                    }
                }
            }
        }
        Ok(Default::default())
    }

    pub fn into_result(mut self) -> Result<Vec<Change>, Error> {
        match self.err {
            Some(err) => Err(err),
            None => {
                if !self.delete_version_ids.is_empty() {
                    let deleted_version_ids = &self.delete_version_ids;
                    self.changes.retain(|change| match change {
                        Change::Added(v) | Change::Yanked(v) => {
                            !deleted_version_ids.contains(&v.id())
                        }
                        Change::Deleted { .. } => true,
                    })
                }
                Ok(self.changes)
            }
        }
    }
}

fn version_from_json_line(line: &[u8], file_name: &BStr) -> Result<CrateVersion, Error> {
    serde_json::from_slice(line).map_err(|err| Error::VersionDecode {
        source: err,
        file_name: file_name.into(),
        line: line.into(),
    })
}
<commit_msg>all tests are green<commit_after>use crate::index::diff::Error;
use crate::{Change, CrateVersion};
use bstr::BStr;
use git_repository as git;
use std::collections::BTreeSet;
use std::ops::Range;

#[derive(Default)]
pub(crate) struct Delegate {
    changes: Vec<Change>,
    delete_version_ids: BTreeSet<u64>,
    err: Option<Error>,
}

impl Delegate {
    pub fn handle(
        &mut self,
        change: git::object::tree::diff::Change<'_, '_, '_>,
    ) -> Result<git::object::tree::diff::Action, Error> {
        use git::bstr::ByteSlice;
        use git::object::tree::diff::change::Event::*;
        use git::objs::tree::EntryMode::*;
        fn entry_data(
            entry: git::objs::tree::EntryMode,
            id: git::Id<'_>,
        ) -> Result<Option<git::Object<'_>>, Error> {
            matches!(entry, Blob | BlobExecutable)
                .then(|| id.object())
                .transpose()
                .map_err(Into::into)
        }
        if change.location.contains(&b'.') {
            return Ok(Default::default());
        }
        match change.event {
            Addition { entry_mode, id } => {
                if let Some(obj) = entry_data(entry_mode, id)? {
                    for line in (&obj.data).lines() {
                        let version = version_from_json_line(line, change.location)?;
                        self.changes.push(if version.yanked {
                            Change::Yanked(version)
                        } else {
                            Change::Added(version)
                        });
                    }
                }
            }
            Deletion { entry_mode, .. } => {
                if entry_mode.is_no_tree() {
                    self.changes.push(Change::Deleted {
                        name: change.location.to_string(),
                    });
                }
            }
            Modification { .. } => {
                if let Some(diff) = change.event.diff().transpose()? {
                    let location = change.location;

                    let input = diff.line_tokens();
                    let mut err = None;
                    git::diff::blob::diff(
                        diff.algo,
                        &input,
                        |before: Range<u32>, after: Range<u32>| {
                            if err.is_some() {
                                return;
                            }
                            let mut lines_before = input.before
                                [before.start as usize..before.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            let mut lines_after = input.after
                                [after.start as usize..after.end as usize]
                                .iter()
                                .map(|&line| input.interner[line].as_bstr())
                                .peekable();
                            match (lines_before.peek().is_some(), lines_after.peek().is_some()) {
                                (true, false) => {
                                    for removed in lines_before {
                                        match version_from_json_line(removed, location) {
                                            Ok(version) => {
                                                self.delete_version_ids.insert(version.id());
                                            }
                                            Err(e) => {
                                                err = Some(e);
                                                break;
                                            }
                                        }
                                    }
                                }
                                (false, true) => {
                                    for inserted in lines_after {
                                        match version_from_json_line(inserted, location) {
                                            Ok(version) => {
                                                self.changes.push(if version.yanked {
                                                    Change::Yanked(version)
                                                } else {
                                                    Change::Added(version)
                                                });
                                            }
                                            Err(e) => {
                                                err = Some(e);
                                                break;
                                            }
                                        }
                                    }
                                }
                                (true, true) => {
                                    for (removed, inserted) in lines_before.zip(lines_after) {
                                        match version_from_json_line(inserted, location).and_then(
                                            |inserted| {
                                                version_from_json_line(removed, location)
                                                    .map(|removed| (removed, inserted))
                                            },
                                        ) {
                                            Ok((removed, inserted)) => {
                                                if removed.yanked != inserted.yanked {
                                                    self.changes.push(if inserted.yanked {
                                                        Change::Yanked(inserted)
                                                    } else {
                                                        Change::Added(inserted)
                                                    });
                                                }
                                            }
                                            Err(e) => {
                                                err = Some(e);
                                                break;
                                            }
                                        }
                                    }
                                }
                                (false, false) => {}
                            }
                        },
                    );
                    if let Some(err) = err {
                        return Err(err.into());
                    }
                }
            }
        }
        Ok(Default::default())
    }

    pub fn into_result(mut self) -> Result<Vec<Change>, Error> {
        match self.err {
            Some(err) => Err(err),
            None => {
                if !self.delete_version_ids.is_empty() {
                    let deleted_version_ids = &self.delete_version_ids;
                    self.changes.retain(|change| match change {
                        Change::Added(v) | Change::Yanked(v) => {
                            !deleted_version_ids.contains(&v.id())
                        }
                        Change::Deleted { .. } => true,
                    })
                }
                Ok(self.changes)
            }
        }
    }
}

fn version_from_json_line(line: &[u8], file_name: &BStr) -> Result<CrateVersion, Error> {
    serde_json::from_slice(line).map_err(|err| Error::VersionDecode {
        source: err,
        file_name: file_name.into(),
        line: line.into(),
    })
}
<|endoftext|>


<commit_before>use std::cmp::Ordering::{Equal, Less, Greater};

#[test]
fn test_clone() {
    let a = (1, "2");
    let b = a.clone();
    assert_eq!(a, b);
}

#[test]
fn test_tuple_cmp() {
    let (small, big) = ((1, 2, 3), (3, 2, 1));

    let nan = 0.0f64/0.0;

    // PartialEq
    assert_eq!(small, small);
    assert_eq!(big, big);
    assert!(small != big);
    assert!(big != small);

    // PartialOrd
    assert!(small < big);
    assert!(!(small < small));
    assert!(!(big < small));
    assert!(!(big < big));

    assert!(small <= small);
    assert!(big <= big);

    assert!(big > small);
    assert!(small >= small);
    assert!(big >= small);
    assert!(big >= big);

    assert!(!((1.0f64, 2.0f64) < (nan, 3.0)));
    assert!(!((1.0f64, 2.0f64) <= (nan, 3.0)));
    assert!(!((1.0f64, 2.0f64) > (nan, 3.0)));
    assert!(!((1.0f64, 2.0f64) >= (nan, 3.0)));
    assert!(((1.0f64, 2.0f64) < (2.0, nan)));
    assert!(!((2.0f64, 2.0f64) < (2.0, nan)));

    // Ord
    assert!(small.cmp(&small) == Equal);
    assert!(big.cmp(&big) == Equal);
    assert!(small.cmp(&big) == Less);
    assert!(big.cmp(&small) == Greater);
}

#[test]
fn test_show() {
    let s = format!("{:?}", (1,));
    assert_eq!(s, "(1,)");
    let s = format!("{:?}", (1, true));
    assert_eq!(s, "(1, true)");
    let s = format!("{:?}", (1, "hi", true));
    assert_eq!(s, "(1, \"hi\", true)");
}
<commit_msg>Refactor tuple comparison tests<commit_after>use std::cmp::Ordering::{Equal, Less, Greater};
use std::f64::NAN;

#[test]
fn test_clone() {
    let a = (1, "2");
    let b = a.clone();
    assert_eq!(a, b);
}

#[test]
fn test_partial_eq() {
    let (small, big) = ((1, 2, 3), (3, 2, 1));
    assert_eq!(small, small);
    assert_eq!(big, big);
    assert_ne!(small, big);
    assert_ne!(big, small);
}

#[test]
fn test_partial_ord() {
    let (small, big) = ((1, 2, 3), (3, 2, 1));

    assert!(small < big);
    assert!(!(small < small));
    assert!(!(big < small));
    assert!(!(big < big));

    assert!(small <= small);
    assert!(big <= big);

    assert!(big > small);
    assert!(small >= small);
    assert!(big >= small);
    assert!(big >= big);

    assert!(!((1.0f64, 2.0f64) < (NAN, 3.0)));
    assert!(!((1.0f64, 2.0f64) <= (NAN, 3.0)));
    assert!(!((1.0f64, 2.0f64) > (NAN, 3.0)));
    assert!(!((1.0f64, 2.0f64) >= (NAN, 3.0)));
    assert!(((1.0f64, 2.0f64) < (2.0, NAN)));
    assert!(!((2.0f64, 2.0f64) < (2.0, NAN)));
}

#[test]
fn test_ord() {
    let (small, big) = ((1, 2, 3), (3, 2, 1));
    assert_eq!(small.cmp(&small), Equal);
    assert_eq!(big.cmp(&big), Equal);
    assert_eq!(small.cmp(&big), Less);
    assert_eq!(big.cmp(&small), Greater);
}

#[test]
fn test_show() {
    let s = format!("{:?}", (1,));
    assert_eq!(s, "(1,)");
    let s = format!("{:?}", (1, true));
    assert_eq!(s, "(1, true)");
    let s = format!("{:?}", (1, "hi", true));
    assert_eq!(s, "(1, \"hi\", true)");
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Create_a_file

use std::io::{File, fs};
use std::libc::S_IRWXU;

fn main () {
  // Create a new file.
  let mut new_file = File::create(&Path::new("output.txt")).unwrap();
  // Write something trivial to it.
  new_file.write(bytes!("Nothing here..."));
  // Create a directory.
  fs::mkdir(&Path::new("docs"), S_IRWXU as u32)
}
<commit_msg>Fixed up create_file.rs<commit_after>// Implements http://rosettacode.org/wiki/Create_a_file

use std::io::{File, fs};
use std::libc::S_IRWXU;

fn main () {
  // Create a new file.
  let mut new_file = File::create(&Path::new("output.txt")).unwrap();
  // Write something trivial to it.
  write!(&mut new_file as &mut Writer, "Nothing here...");
  // Create a directory.
  fs::mkdir(&Path::new("docs"), S_IRWXU as u32)
}
<|endoftext|>


<commit_before>use std::fs::{self, DirEntry};
use std::io;
use std::path::Path;

#[test]
fn test_missing_tests() {
    explore_directory(Path::new("./tests")).unwrap();
}

/*
Test for missing files.

Since rs files are alphabetically before stderr/stdout, we can sort by the full name
and iter in that order. If we've seen the file stem for the first time and it's not
a rust file, it means the rust file has to be missing.
*/
fn explore_directory(dir: &Path) -> io::Result<()> {
    let mut current_file = String::new();
    let mut files: Vec<DirEntry> = fs::read_dir(dir)?.filter_map(Result::ok).collect();
    files.sort_by_key(|e| e.path());
    for entry in files.iter() {
        let path = entry.path();
        if path.is_dir() {
            explore_directory(&path)?;
        } else {
            let file_stem = path.file_stem().unwrap().to_str().unwrap().to_string();
            match path.extension() {
                Some(ext) => {
                    match ext.to_str().unwrap() {
                        "rs" => current_file = file_stem.clone(),
                        "stderr" | "stdout" => {
                            assert_eq!(
                                file_stem,
                                current_file,
                                "{}",
                                format!("Didn't see a test file for {:}", path.to_str().unwrap())
                            );
                        },
                        _ => continue,
                    };
                },
                None => {},
            }
        }
    }
    Ok(())
}
<commit_msg>Update to collect all the files then throw the error.<commit_after>use std::fs::{self, DirEntry};
use std::path::Path;

#[test]
fn test_missing_tests() {
    let missing_files = explore_directory(Path::new("./tests"));
    if missing_files.len() > 0 {
        assert!(
            false,
            format!(
                "Didn't see a test file for the following files:\n\n{}\n",
                missing_files
                    .iter()
                    .map(|s| format!("\t{}", s))
                    .collect::<Vec<_>>()
                    .join("\n")
            )
        );
    }
}

/*
Test for missing files.

Since rs files are alphabetically before stderr/stdout, we can sort by the full name
and iter in that order. If we've seen the file stem for the first time and it's not
a rust file, it means the rust file has to be missing.
*/
fn explore_directory(dir: &Path) -> Vec<String> {
    let mut missing_files: Vec<String> = Vec::new();
    let mut current_file = String::new();
    let mut files: Vec<DirEntry> = fs::read_dir(dir).unwrap().filter_map(Result::ok).collect();
    files.sort_by_key(|e| e.path());
    for entry in files.iter() {
        let path = entry.path();
        if path.is_dir() {
            missing_files.extend(explore_directory(&path));
        } else {
            let file_stem = path.file_stem().unwrap().to_str().unwrap().to_string();
            match path.extension() {
                Some(ext) => {
                    match ext.to_str().unwrap() {
                        "rs" => current_file = file_stem.clone(),
                        "stderr" | "stdout" => {
                            if file_stem != current_file {
                                missing_files.push(path.to_str().unwrap().to_string());
                            }
                        },
                        _ => continue,
                    };
                },
                None => {},
            }
        }
    }
    missing_files
}
<|endoftext|>


<commit_before>use crate::{
    auth, breakpoints,
    components::{activity_indicator, breadcrumbs, ddn_logo, font_awesome, restrict},
    ctx_help::CtxHelp,
    generated::css_classes::C,
    MergeAttrs, Model, Msg, Route, SessionExt,
    Visibility::*,
    CTX_HELP,
};
use iml_wire_types::GroupType;
use seed::{prelude::*, virtual_dom::Attrs, *};

fn menu_icon<T>(icon_name: &str) -> Node<T> {
    font_awesome(
        class![C.h_6, C.inline, C.lg__h_5, C.lg__mr_2, C.lg__w_5, C.mr_3, C.w_6, C.xl__h_6, C.xl__mr_3, C.xl__w_6],
        icon_name,
    )
}

fn nav_manage_dropdown(open: bool) -> Node<Msg> {
    if !open {
        return empty![];
    }

    let cls = class![
        C.my_2,
        C.px_4,
        C.py_2,
        C.bg_menu_active,
        C.block,
        C.cursor_pointer,
        C.hover__text_white,
    ];

    div![
        class![
            C.lg__absolute,
            C.lg__border,
            C.lg__w_full,
            C.lg__left_0,
            C.border_gray_600,
            C.mt_px,
            C.text_gray_500,
            C.rounded,
            C.bg_menu,
            C.p_4,
            C.z_20
        ],
        style! { "top" => "110%" },
        ul![
            li![a![
                &cls,
                Route::Servers.to_string(),
                attrs! {
                    At::Href => Route::Servers.to_href(),
                },
            ]],
            li![
                a![&cls, Route::PowerControl.to_string()],
                attrs! {
                    At::Href => Route::PowerControl.to_href(),
                },
            ],
            li![
                a![&cls, Route::Filesystems.to_string()],
                attrs! {
                    At::Href => Route::Filesystems.to_href(),
                },
            ],
            li![a![&cls, "HSM"]],
            li![a![&cls, "Storage"]],
            li![
                a![&cls, Route::Users.to_string()],
                attrs! {
                    At::Href => Route::Users.to_href(),
                },
            ],
            li![
                a![&cls, Route::Volumes.to_string()],
                attrs! {
                    At::Href => Route::Volumes.to_href(),
                },
            ],
            li![
                a![&cls, Route::Mgt.to_string()],
                attrs! {
                    At::Href => Route::Mgt.to_href(),
                },
            ]
        ]
    ]
}

fn main_menu_items(model: &Model) -> Node<Msg> {
    let menu_class = class![
        C.block,
        C.border_b_2,
        C.border_transparent,
        C.group,
        C.hover__bg_menu_active,
        C.lg__flex_auto,
        C.lg__flex_col,
        C.lg__flex,
        C.lg__h_16,
        C.lg__inline_block,
        C.lg__justify_center,
        C.lg__p_4,
        C.lg__py_0,
        C.p_6,
        C.text_gray_500,
        C.xl__p_6,
    ];

    div![
        class![C.lg__flex, C.lg__h_16],
        a![
            &menu_class,
            class![C.bg_menu_active => model.route == Route::Dashboard],
            attrs! {
                At::Href => Route::Dashboard.to_href()
            },
            span![
                menu_icon("tachometer-alt"),
                span![
                    class![C.group_hover__text_active, C.text_active => model.route == Route::Dashboard],
                    Route::Dashboard.to_string(),
                ],
            ]
        ],
        restrict::view(
            model.auth.get_session(),
            GroupType::FilesystemAdministrators,
            a![
                &menu_class,
                class![
                    C.lg__border_blue_400 => model.manage_menu_state.is_open(),
                    C.relative
                ],
                simple_ev(Ev::Click, Msg::ManageMenuState),
                span![
                    menu_icon("cog"),
                    span![
                        class![C.group_hover__text_active],
                        "Management",
                        font_awesome(
                            class![C.fill_current, C.h_3, C.w_3, C.ml_1, C.inline, C._mt_1],
                            "chevron-down"
                        ),
                    ],
                ],
                nav_manage_dropdown(model.manage_menu_state.is_open()),
            ]
        ),
        a![
            &menu_class,
            class![C.bg_menu_active => model.route == Route::Jobstats],
            attrs! {
                At::Href => Route::Jobstats.to_href(),
            },
            span![
                menu_icon("signal"),
                span![
                    class![C.group_hover__text_active, C.text_active => model.route == Route::Jobstats],
                    Route::Jobstats.to_string(),
                ],
            ]
        ],
        a![
            &menu_class,
            class![C.bg_menu_active => model.route == Route::Logs],
            attrs! {
                At::Href => Route::Logs.to_href()
            },
            span![
                menu_icon("book"),
                span![
                    class![C.group_hover__text_active, C.bg_menu_active => model.route == Route::Logs],
                    Route::Logs.to_string(),
                ]
            ]
        ],
        context_sensitive_help_link(model, &menu_class),
        a![
            &menu_class,
            class![C.bg_menu_active => model.route == Route::Activity],
            attrs! {
                At::Href => Route::Activity.to_href(),
            },
            span![
                activity_indicator(&model.activity_health),
                span![
                    class![C.group_hover__text_active, C.bg_menu_active => model.route == Route::Activity],
                    Route::Activity.to_string(),
                ]
            ]
        ],
    ]
}

fn context_sensitive_help_link(model: &Model, menu_class: &Attrs) -> Node<Msg> {
    let attrs = attrs! {
       At::Target => "_blank", // open the link in a new tab
       At::Href => format!(
           "{}{}",
           CTX_HELP,
           model.route.help_link().unwrap_or_else(|| "".into())
       )
    };
    a![
        menu_class,
        attrs,
        span![
            menu_icon("question-circle"),
            span![class![C.group_hover__text_active], "Help"]
        ]
    ]
}

fn toggle_nav_view() -> Node<Msg> {
    div![
        class![C.block, C.lg__hidden],
        button![
            class![
                C.flex,
                C.items_center,
                C.px_3,
                C.py_3,
                C.border,
                C.rounded,
                C.border_gray_400,
                C.text_gray_300,
                C.hover__text_white,
                C.hover__border_white,
            ],
            simple_ev(Ev::Click, Msg::ToggleMenu),
            svg![
                class![C.fill_current, C.h_3, C.w_3],
                attrs! {
                    At::ViewBox => "0 0 20 20"
                },
                title!["Menu"],
                path![attrs! { At::D => "M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" }],
            ],
        ],
    ]
}

/// The navbar logo
fn logo_nav_view<T>() -> Node<T> {
    div![
        class![
            C.flex_shrink_0,
            C.flex,
            C.items_center,
            C.lg__ml_0,
            C.lg__mr_4,
            C.lg__my_0,
            C.ml_6,
            C.my_2,
            C.text_red_600,
            C.xl__mr_12
        ],
        ddn_logo().merge_attrs(class![C.h_12, C.w_24, C.mr_3]),
        span![class![C.font_semibold, C.text_3xl, C.tracking_tight], "AI400"],
    ]
}

fn nav(model: &Model) -> Node<Msg> {
    nav![
        class![
            C.flex,
            C.bg_menu,
            C.items_center,
            C.px_5,
            C.xl__px_5,
            C.lg__px_4,
            C.lg__h_16,
            C.justify_between,
            C.flex_wrap
        ],
        logo_nav_view(),
        toggle_nav_view(),
        if model.menu_visibility == Visible || model.breakpoint_size >= breakpoints::Size::LG {
            div![
                class![
                    C.w_full,
                    C.block,
                    C.flex_grow,
                    C.lg__text_sm,
                    C.xl__text_base,
                    C.lg__flex,
                    C.lg__items_center,
                    C.lg__w_auto,
                    C.lg__h_16,
                ],
                main_menu_items(model),
                auth_view(&model.auth, model.logging_out),
            ]
        } else {
            empty![]
        }
    ]
}

/// Show the logged in user if available.
/// Also show the Login / Logout link
pub fn auth_view(auth: &auth::Model, logging_out: bool) -> Node<Msg> {
    let x = match auth.get_session() {
        Some(session) => session,
        None => return empty![],
    };

    let disabled = attrs! { At::Disabled => logging_out.as_at_value() };

    let cls = class![
        C.block,
        C.border_b_2,
        C.border_transparent,
        C.cursor_pointer
        C.hover__text_white,
        C.lg__flex_auto,
        C.lg__flex_col,
        C.lg__flex_grow_0,
        C.lg__flex,
        C.lg__h_16,
        C.lg__inline_block,
        C.lg__justify_center,
        C.lg__py_0,
        C.p_6,
        C.lg__p_4,
        C.xl__p_6,
        C.text_gray_300
    ];

    let mut auth_link = a![&cls, &disabled, if !x.has_user() { "Login" } else { "Logout" }];

    let auth_link = if !x.has_user() {
        auth_link.merge_attrs(attrs! {
            At::Href => Route::Login.to_href(),
        })
    } else {
        auth_link.add_listener(simple_ev(Ev::Click, Msg::Logout));

        auth_link
    };

    div![
        class![C.lg__flex, C.lg__h_16, C.lg__flex_grow, C.lg__justify_end],
        match x.user.as_ref() {
            Some(user) => {
                a![
                    &cls,
                    &disabled,
                    attrs! {
                        At::Href => Route::User(user.id.into()).to_href()
                    },
                    user.username
                ]
            }
            None => empty![],
        },
        auth_link
    ]
}

pub fn view(model: &Model) -> impl View<Msg> {
    vec![
        header![nav(model)],
        div![
            class![C.bg_menu_active, C.text_gray_300, C.text_center, C.py_2],
            breadcrumbs::view(&model.breadcrumbs).els()
        ],
    ]
}
<commit_msg>Remove some pages from header (#1625)<commit_after>use crate::{
    auth, breakpoints,
    components::{activity_indicator, breadcrumbs, ddn_logo, font_awesome, restrict},
    ctx_help::CtxHelp,
    generated::css_classes::C,
    MergeAttrs, Model, Msg, Route, SessionExt,
    Visibility::*,
    CTX_HELP,
};
use iml_wire_types::GroupType;
use seed::{prelude::*, virtual_dom::Attrs, *};

fn menu_icon<T>(icon_name: &str) -> Node<T> {
    font_awesome(
        class![C.h_6, C.inline, C.lg__h_5, C.lg__mr_2, C.lg__w_5, C.mr_3, C.w_6, C.xl__h_6, C.xl__mr_3, C.xl__w_6],
        icon_name,
    )
}

fn nav_manage_dropdown(open: bool) -> Node<Msg> {
    if !open {
        return empty![];
    }

    let cls = class![
        C.my_2,
        C.px_4,
        C.py_2,
        C.bg_menu_active,
        C.block,
        C.cursor_pointer,
        C.hover__text_white,
    ];

    div![
        class![
            C.lg__absolute,
            C.lg__border,
            C.lg__w_full,
            C.lg__left_0,
            C.border_gray_600,
            C.mt_px,
            C.text_gray_500,
            C.rounded,
            C.bg_menu,
            C.p_4,
            C.z_20
        ],
        style! { "top" => "110%" },
        ul![
            li![a![
                &cls,
                Route::Servers.to_string(),
                attrs! {
                    At::Href => Route::Servers.to_href(),
                },
            ]],
            li![
                a![&cls, Route::Filesystems.to_string()],
                attrs! {
                    At::Href => Route::Filesystems.to_href(),
                },
            ],
            li![
                a![&cls, Route::Volumes.to_string()],
                attrs! {
                    At::Href => Route::Volumes.to_href(),
                },
            ],
            li![
                a![&cls, Route::Mgt.to_string()],
                attrs! {
                    At::Href => Route::Mgt.to_href(),
                },
            ],
            li![
                a![&cls, Route::Users.to_string()],
                attrs! {
                    At::Href => Route::Users.to_href(),
                },
            ],
        ]
    ]
}

fn main_menu_items(model: &Model) -> Node<Msg> {
    let menu_class = class![
        C.block,
        C.border_b_2,
        C.border_transparent,
        C.group,
        C.hover__bg_menu_active,
        C.lg__flex_auto,
        C.lg__flex_col,
        C.lg__flex,
        C.lg__h_16,
        C.lg__inline_block,
        C.lg__justify_center,
        C.lg__p_4,
        C.lg__py_0,
        C.p_6,
        C.text_gray_500,
        C.xl__p_6,
    ];

    div![
        class![C.lg__flex, C.lg__h_16],
        a![
            &menu_class,
            class![C.bg_menu_active => model.route == Route::Dashboard],
            attrs! {
                At::Href => Route::Dashboard.to_href()
            },
            span![
                menu_icon("tachometer-alt"),
                span![
                    class![C.group_hover__text_active, C.text_active => model.route == Route::Dashboard],
                    Route::Dashboard.to_string(),
                ],
            ]
        ],
        restrict::view(
            model.auth.get_session(),
            GroupType::FilesystemAdministrators,
            a![
                &menu_class,
                class![
                    C.lg__border_blue_400 => model.manage_menu_state.is_open(),
                    C.relative
                ],
                simple_ev(Ev::Click, Msg::ManageMenuState),
                span![
                    menu_icon("cog"),
                    span![
                        class![C.group_hover__text_active],
                        "Management",
                        font_awesome(
                            class![C.fill_current, C.h_3, C.w_3, C.ml_1, C.inline, C._mt_1],
                            "chevron-down"
                        ),
                    ],
                ],
                nav_manage_dropdown(model.manage_menu_state.is_open()),
            ]
        ),
        a![
            &menu_class,
            class![C.bg_menu_active => model.route == Route::Logs],
            attrs! {
                At::Href => Route::Logs.to_href()
            },
            span![
                menu_icon("book"),
                span![
                    class![C.group_hover__text_active, C.bg_menu_active => model.route == Route::Logs],
                    Route::Logs.to_string(),
                ]
            ]
        ],
        context_sensitive_help_link(model, &menu_class),
        a![
            &menu_class,
            class![C.bg_menu_active => model.route == Route::Activity],
            attrs! {
                At::Href => Route::Activity.to_href(),
            },
            span![
                activity_indicator(&model.activity_health),
                span![
                    class![C.group_hover__text_active, C.bg_menu_active => model.route == Route::Activity],
                    Route::Activity.to_string(),
                ]
            ]
        ],
    ]
}

fn context_sensitive_help_link(model: &Model, menu_class: &Attrs) -> Node<Msg> {
    let attrs = attrs! {
       At::Target => "_blank", // open the link in a new tab
       At::Href => format!(
           "{}{}",
           CTX_HELP,
           model.route.help_link().unwrap_or_else(|| "".into())
       )
    };
    a![
        menu_class,
        attrs,
        span![
            menu_icon("question-circle"),
            span![class![C.group_hover__text_active], "Help"]
        ]
    ]
}

fn toggle_nav_view() -> Node<Msg> {
    div![
        class![C.block, C.lg__hidden],
        button![
            class![
                C.flex,
                C.items_center,
                C.px_3,
                C.py_3,
                C.border,
                C.rounded,
                C.border_gray_400,
                C.text_gray_300,
                C.hover__text_white,
                C.hover__border_white,
            ],
            simple_ev(Ev::Click, Msg::ToggleMenu),
            svg![
                class![C.fill_current, C.h_3, C.w_3],
                attrs! {
                    At::ViewBox => "0 0 20 20"
                },
                title!["Menu"],
                path![attrs! { At::D => "M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" }],
            ],
        ],
    ]
}

/// The navbar logo
fn logo_nav_view<T>() -> Node<T> {
    div![
        class![
            C.flex_shrink_0,
            C.flex,
            C.items_center,
            C.lg__ml_0,
            C.lg__mr_4,
            C.lg__my_0,
            C.ml_6,
            C.my_2,
            C.text_red_600,
            C.xl__mr_12
        ],
        ddn_logo().merge_attrs(class![C.h_12, C.w_24, C.mr_3]),
        span![class![C.font_semibold, C.text_3xl, C.tracking_tight], "AI400"],
    ]
}

fn nav(model: &Model) -> Node<Msg> {
    nav![
        class![
            C.flex,
            C.bg_menu,
            C.items_center,
            C.px_5,
            C.xl__px_5,
            C.lg__px_4,
            C.lg__h_16,
            C.justify_between,
            C.flex_wrap
        ],
        logo_nav_view(),
        toggle_nav_view(),
        if model.menu_visibility == Visible || model.breakpoint_size >= breakpoints::Size::LG {
            div![
                class![
                    C.w_full,
                    C.block,
                    C.flex_grow,
                    C.lg__text_sm,
                    C.xl__text_base,
                    C.lg__flex,
                    C.lg__items_center,
                    C.lg__w_auto,
                    C.lg__h_16,
                ],
                main_menu_items(model),
                auth_view(&model.auth, model.logging_out),
            ]
        } else {
            empty![]
        }
    ]
}

/// Show the logged in user if available.
/// Also show the Login / Logout link
pub fn auth_view(auth: &auth::Model, logging_out: bool) -> Node<Msg> {
    let x = match auth.get_session() {
        Some(session) => session,
        None => return empty![],
    };

    let disabled = attrs! { At::Disabled => logging_out.as_at_value() };

    let cls = class![
        C.block,
        C.border_b_2,
        C.border_transparent,
        C.cursor_pointer
        C.hover__text_white,
        C.lg__flex_auto,
        C.lg__flex_col,
        C.lg__flex_grow_0,
        C.lg__flex,
        C.lg__h_16,
        C.lg__inline_block,
        C.lg__justify_center,
        C.lg__py_0,
        C.p_6,
        C.lg__p_4,
        C.xl__p_6,
        C.text_gray_300
    ];

    let mut auth_link = a![&cls, &disabled, if !x.has_user() { "Login" } else { "Logout" }];

    let auth_link = if !x.has_user() {
        auth_link.merge_attrs(attrs! {
            At::Href => Route::Login.to_href(),
        })
    } else {
        auth_link.add_listener(simple_ev(Ev::Click, Msg::Logout));

        auth_link
    };

    div![
        class![C.lg__flex, C.lg__h_16, C.lg__flex_grow, C.lg__justify_end],
        match x.user.as_ref() {
            Some(user) => {
                a![
                    &cls,
                    &disabled,
                    attrs! {
                        At::Href => Route::User(user.id.into()).to_href()
                    },
                    user.username
                ]
            }
            None => empty![],
        },
        auth_link
    ]
}

pub fn view(model: &Model) -> impl View<Msg> {
    vec![
        header![nav(model)],
        div![
            class![C.bg_menu_active, C.text_gray_300, C.text_center, C.py_2],
            breadcrumbs::view(&model.breadcrumbs).els()
        ],
    ]
}
<|endoftext|>


<commit_before>//! Log all requests in a format similar to Heroku's router, but with additional
//! information that we care about like User-Agent

use super::prelude::*;
use crate::util::request_header;
use conduit::Request;
use std::fmt;
use std::time::Instant;

#[allow(missing_debug_implementations)] // We can't
#[derive(Default)]
pub struct LogRequests {
    handler: Option<Box<dyn Handler>>,
}

impl AroundMiddleware for LogRequests {
    fn with_handler(&mut self, handler: Box<dyn Handler>) {
        self.handler = Some(handler);
    }
}

impl Handler for LogRequests {
    fn call(&self, req: &mut dyn Request) -> Result<Response, Box<dyn Error + Send>> {
        let request_start = Instant::now();
        let res = self.handler.as_ref().unwrap().call(req);
        let (level, response_code) = match res {
            Ok(ref r) => ("info", r.status.0),
            Err(_) => ("error", 500),
        };
        let response_time = request_start.elapsed();
        let response_time =
            response_time.as_secs() * 1000 + u64::from(response_time.subsec_nanos()) / 1_000_000;

        let metadata_length = req
            .extensions()
            .find::<u64>()
            .map_or(String::new(), |l| format!(" metadata_length={}", l));

        print!(
            "at={level} method={method} path=\"{path}\" \
             request_id={request_id} fwd=\"{ip}\" service={time_ms}ms \
             status={status} user_agent=\"{user_agent}\"\
             {metadata_length}",
            level = level,
            method = req.method(),
            path = FullPath(req),
            ip = request_header(req, "X-Real-Ip"),
            time_ms = response_time,
            user_agent = request_header(req, "User-Agent"),
            request_id = request_header(req, "X-Request-Id"),
            status = response_code,
            metadata_length = metadata_length,
        );

        if let Err(ref e) = res {
            print!(" error=\"{}\"", e.to_string());
        }

        if response_time > 1000 {
            print!(" SLOW REQUEST");
        }

        println!();

        res
    }
}

struct FullPath<'a>(&'a dyn Request);

impl<'a> fmt::Display for FullPath<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0.path())?;
        if let Some(q_string) = self.0.query_string() {
            write!(f, "?{}", q_string)?;
        }
        Ok(())
    }
}
<commit_msg>Consolidate logging into a single `println!()`<commit_after>//! Log all requests in a format similar to Heroku's router, but with additional
//! information that we care about like User-Agent

use super::prelude::*;
use crate::util::request_header;
use conduit::Request;
use std::fmt;
use std::time::Instant;

#[allow(missing_debug_implementations)] // We can't
#[derive(Default)]
pub struct LogRequests {
    handler: Option<Box<dyn Handler>>,
}

impl AroundMiddleware for LogRequests {
    fn with_handler(&mut self, handler: Box<dyn Handler>) {
        self.handler = Some(handler);
    }
}

impl Handler for LogRequests {
    fn call(&self, req: &mut dyn Request) -> Result<Response, Box<dyn Error + Send>> {
        let request_start = Instant::now();
        let res = self.handler.as_ref().unwrap().call(req);
        let (level, response_code) = match res {
            Ok(ref r) => ("info", r.status.0),
            Err(_) => ("error", 500),
        };
        let response_time = request_start.elapsed();
        let response_time =
            response_time.as_secs() * 1000 + u64::from(response_time.subsec_nanos()) / 1_000_000;

        let metadata_length = req
            .extensions()
            .find::<u64>()
            .map_or(String::new(), |l| format!(" metadata_length={}", l));

        let slow_request = if response_time > 1000 {
            " SLOW REQUEST"
        } else {
            ""
        };

        let error = if let Err(ref e) = res {
            format!(" error=\"{}\"", e.to_string())
        } else {
            String::new()
        };

        println!(
            "at={level} method={method} path=\"{path}\" \
             request_id={request_id} fwd=\"{ip}\" service={time_ms}ms \
             status={status} user_agent=\"{user_agent}\"\
             {metadata_length}{error}{slow_request}",
            level = level,
            method = req.method(),
            path = FullPath(req),
            ip = request_header(req, "X-Real-Ip"),
            time_ms = response_time,
            user_agent = request_header(req, "User-Agent"),
            request_id = request_header(req, "X-Request-Id"),
            status = response_code,
            metadata_length = metadata_length,
            error = error,
            slow_request = slow_request,
        );

        res
    }
}

struct FullPath<'a>(&'a dyn Request);

impl<'a> fmt::Display for FullPath<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0.path())?;
        if let Some(q_string) = self.0.query_string() {
            write!(f, "?{}", q_string)?;
        }
        Ok(())
    }
}
<|endoftext|>


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

use xml::reader::XmlEvent;

use super::Child;
use amqp0::ParseError;
use amqp0::protocol::{ClassParser, ConstantParser, DomainParser, VoidParser};

#[derive(Debug)]
pub enum Parser<'a> {
    Void(VoidParser),
    Class(ClassParser<'a>),
    Constant(ConstantParser<'a>),
    Domain(DomainParser<'a>),
}

impl<'a> Parser<'a> {
    pub fn parse(self, event: &XmlEvent) -> Result<Self, ParseError> {
        Ok(match self {
            Parser::Void(p) => Parser::Void(try!(p.parse(event))),
            Parser::Constant(p) => Parser::Constant(try!(p.parse(event))),
            Parser::Domain(p) => Parser::Domain(try!(p.parse(event))),
            Parser::Class(p) => Parser::Class(try!(p.parse(event))),
        })
    }

    pub fn child(self) -> Result<Option<Child<'a>>, Self> {
        match self {
            Parser::Void(parser) => {
                match parser {
                    VoidParser::Finished => Ok(None),
                    parser => Err(Parser::Void(parser)),
                }
            }
            Parser::Constant(parser) => {
                match parser {
                    ConstantParser::Finished(c) => Ok(Some(c.into())),
                    parser => Err(Parser::Constant(parser)),
                }
            }
            Parser::Domain(parser) => {
                match parser {
                    DomainParser::Finished(d) => Ok(Some(d.into())),
                    parser => Err(Parser::Domain(parser)),
                }
            }
            Parser::Class(parser) => {
                match parser {
                    ClassParser::Finished(c) => Ok(Some(c.into())),
                    parser => Err(Parser::Class(parser)),
                }
            }
        }
    }

    pub fn is_finished(&self) -> bool {
        match *self {
            Parser::Void(VoidParser::Finished)
                | Parser::Constant(ConstantParser::Finished(_))
                | Parser::Domain(DomainParser::Finished(_))
                | Parser::Class(ClassParser::Finished(_))       => true,
            _ => false,
        }
    }
}

impl<'a> From<VoidParser> for Parser<'a> {
    fn from(parser: VoidParser) -> Self {
        Parser::Void(parser)
    }
}

impl<'a> From<ClassParser<'a>> for Parser<'a> {
    fn from(parser: ClassParser<'a>) -> Self {
        Parser::Class(parser)
    }
}

impl<'a> From<ConstantParser<'a>> for Parser<'a> {
    fn from(parser: ConstantParser<'a>) -> Self {
        Parser::Constant(parser)
    }
}

impl<'a> From<DomainParser<'a>> for Parser<'a> {
    fn from(parser: DomainParser<'a>) -> Self {
        Parser::Domain(parser)
    }
}
<commit_msg>[xmlparse] Removed unused is_finished() method from ChainedParser<commit_after>// Copyright 2016 Alexander Reece
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use xml::reader::XmlEvent;

use super::Child;
use amqp0::ParseError;
use amqp0::protocol::{ClassParser, ConstantParser, DomainParser, VoidParser};

#[derive(Debug)]
pub enum Parser<'a> {
    Void(VoidParser),
    Class(ClassParser<'a>),
    Constant(ConstantParser<'a>),
    Domain(DomainParser<'a>),
}

impl<'a> Parser<'a> {
    pub fn parse(self, event: &XmlEvent) -> Result<Self, ParseError> {
        Ok(match self {
            Parser::Void(p) => Parser::Void(try!(p.parse(event))),
            Parser::Constant(p) => Parser::Constant(try!(p.parse(event))),
            Parser::Domain(p) => Parser::Domain(try!(p.parse(event))),
            Parser::Class(p) => Parser::Class(try!(p.parse(event))),
        })
    }

    pub fn child(self) -> Result<Option<Child<'a>>, Self> {
        match self {
            Parser::Void(parser) => {
                match parser {
                    VoidParser::Finished => Ok(None),
                    parser => Err(Parser::Void(parser)),
                }
            }
            Parser::Constant(parser) => {
                match parser {
                    ConstantParser::Finished(c) => Ok(Some(c.into())),
                    parser => Err(Parser::Constant(parser)),
                }
            }
            Parser::Domain(parser) => {
                match parser {
                    DomainParser::Finished(d) => Ok(Some(d.into())),
                    parser => Err(Parser::Domain(parser)),
                }
            }
            Parser::Class(parser) => {
                match parser {
                    ClassParser::Finished(c) => Ok(Some(c.into())),
                    parser => Err(Parser::Class(parser)),
                }
            }
        }
    }
}

impl<'a> From<VoidParser> for Parser<'a> {
    fn from(parser: VoidParser) -> Self {
        Parser::Void(parser)
    }
}

impl<'a> From<ClassParser<'a>> for Parser<'a> {
    fn from(parser: ClassParser<'a>) -> Self {
        Parser::Class(parser)
    }
}

impl<'a> From<ConstantParser<'a>> for Parser<'a> {
    fn from(parser: ConstantParser<'a>) -> Self {
        Parser::Constant(parser)
    }
}

impl<'a> From<DomainParser<'a>> for Parser<'a> {
    fn from(parser: DomainParser<'a>) -> Self {
        Parser::Domain(parser)
    }
}
<|endoftext|>