<commit_before>use std::io::{Error, ErrorKind, Result};
use std::ffi::CString;
use message::Message;
use std::cmp::Ordering;
use std::ptr;
use std::boxed::Box;
use std::rc::Rc;
use std::ops::Deref;
use std::slice;
use std::time::Duration;
use ffi::*;

/// An LCM instance that handles publishing and subscribing,
/// as well as encoding and decoding messages.
pub struct Lcm {
    lcm: *mut lcm_t,
    subscriptions: Vec<Rc<LcmSubscription>>,
}

pub struct LcmSubscription {
    subscription: *mut lcm_subscription_t,
    handler: Box<FnMut(*const lcm_recv_buf_t)>,
}

impl Lcm {
    /// Creates a new `Lcm` instance.
    ///
    /// ```
    /// use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// ```
    pub fn new() -> Result<Lcm> {
        trace!("Creating LCM instance");
        let lcm = unsafe { lcm_create(ptr::null()) };
        match lcm.is_null() {
            true => Err(Error::new(ErrorKind::Other, "Failed to initialize LCM.")),
            false => {
                Ok(Lcm {
                    lcm: lcm,
                    subscriptions: Vec::new(),
                })
            }
        }
    }

    pub fn get_fileno(&self) -> ::std::os::raw::c_int {
        unsafe { lcm_get_fileno(self.lcm) }
    }

    /// Subscribes a callback to a particular topic.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("GREETINGS", |name: String| println!("Hello, {}!", name) );
    /// ```
    pub fn subscribe<M, F>(&mut self, channel: &str, mut callback: F) -> Rc<LcmSubscription>
        where M: Message,
              F: FnMut(M) + 'static
    {
        trace!("Subscribing handler to channel {}", channel);

        let channel = CString::new(channel).unwrap();

        let handler = Box::new(move |rbuf: *const lcm_recv_buf_t| {
            trace!("Running handler");
            let mut buf = unsafe {
                let ref rbuf = *rbuf;
                let data = rbuf.data as *mut u8;
                let len = rbuf.data_size as usize;
                slice::from_raw_parts(data, len)
            };
            trace!("Decoding buffer: {:?}", buf);
            match M::decode_with_hash(&mut buf) {
                Ok(msg) => callback(msg),
                Err(_) => error!("Failed to decode buffer: {:?}", buf),
            }
        });

        let mut subscription = Rc::new(LcmSubscription {
            subscription: ptr::null_mut(),
            handler: handler,
        });

        let user_data = (subscription.deref() as *const _) as *mut _;

        let c_subscription = unsafe {
            lcm_subscribe(self.lcm,
                          channel.as_ptr(),
                          Some(Lcm::handler_callback::<M>),
                          user_data)
        };

        Rc::get_mut(&mut subscription).unwrap().subscription = c_subscription;
        self.subscriptions.push(subscription.clone());

        subscription
    }

    /// Unsubscribes a message handler.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// # let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("GREETINGS", handler_function);
    /// // ...
    /// lcm.unsubscribe(handler);
    /// ```
    pub fn unsubscribe(&mut self, handler: Rc<LcmSubscription>) -> Result<()> {
        trace!("Unsubscribing handler {:?}", handler.subscription);
        let result = unsafe { lcm_unsubscribe(self.lcm, handler.subscription) };

        self.subscriptions.retain(|sub| { sub.subscription != handler.subscription });

        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM: Failed to unsubscribe")),
        }
    }

    /// Publishes a message on the specified channel.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.publish("GREETINGS", &"Charles".to_string()).unwrap();
    /// ```
    pub fn publish<M>(&mut self, channel: &str, message: &M) -> Result<()>
        where M: Message
    {
        let channel = CString::new(channel).unwrap();
        let buffer = message.encode_with_hash()?;
        let result = unsafe {
            lcm_publish(self.lcm,
                        channel.as_ptr(),
                        buffer.as_ptr() as *mut _,
                        buffer.len() as _)
        };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error")),
        }
    }

    /// Waits for and dispatches the next incoming message.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// loop {
    /// # break;
    ///     lcm.handle().unwrap();
    /// }
    /// ```
    pub fn handle(&mut self) -> Result<()> {
        let result = unsafe { lcm_handle(self.lcm) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error")),
        }
    }

    /// Waits for and dispatches the next incoming message, up to a time limit.
    ///
    /// ```
    /// # use std::time::Duration;
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// let wait_dur = Duration::from_millis(100);
    /// loop {
    /// # break;
    ///     lcm.handle_timeout(Duration::from_millis(1000)).unwrap();
    /// }
    /// ```
    pub fn handle_timeout(&mut self, timeout: Duration) -> Result<()> {
        let result = unsafe { lcm_handle_timeout(self.lcm, (timeout.as_secs() * 1000) as i32 + (timeout.subsec_nanos() / 1000_000) as i32) };
        match result.cmp(&0) {
            Ordering::Less => Err(Error::new(ErrorKind::Other, "LCM Error")),
            Ordering::Equal => Err(Error::new(ErrorKind::Other, "LCM Timeout")),
            Ordering::Greater => Ok(()),
        }
    }

    /// Adjusts the maximum number of received messages that can be queued up for a subscription.
    /// The default is `30`.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// # let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("POSITION", handler_function);
    /// lcm.subscription_set_queue_capacity(handler, 30);
    /// ```
    pub fn subscription_set_queue_capacity(&self, handler: Rc<LcmSubscription>, num_messages: usize) {
        let handler = handler.subscription;
        let num_messages = num_messages as _;
        unsafe { lcm_subscription_set_queue_capacity(handler, num_messages) };
    }

    extern "C" fn handler_callback<M>(rbuf: *const lcm_recv_buf_t,
                                      _: *const ::std::os::raw::c_char,
                                      user_data: *mut ::std::os::raw::c_void)
        where M: Message
    {
        trace!("Received data");
        let sub = user_data as *mut LcmSubscription;
        let sub = unsafe { &mut *sub };
        (sub.handler)(rbuf);
    }
}

impl Drop for Lcm {
    fn drop(&mut self) {
        trace!("Destroying Lcm instance");
        unsafe { lcm_destroy(self.lcm) };
    }
}

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

    #[test]
    fn initialized() {
        let _lcm = Lcm::new().unwrap();
    }

    #[test]
    fn test_subscribe() {
        let mut lcm = Lcm::new().unwrap();
        lcm.subscribe("channel", |_: String| {});
        assert_eq!(lcm.subscriptions.len(), 1);
    }

    #[test]
    fn test_unsubscribe() {
        let mut lcm = Lcm::new().unwrap();
        let sub = lcm.subscribe("channel", |_: String| {});
        lcm.unsubscribe(sub).unwrap();
        assert_eq!(lcm.subscriptions.len(), 0);
    }
}
<commit_msg>Make LCM derive Debug<commit_after>use std::io::{Error, ErrorKind, Result};
use std::ffi::CString;
use message::Message;
use std::cmp::Ordering;
use std::ptr;
use std::boxed::Box;
use std::rc::Rc;
use std::ops::Deref;
use std::slice;
use std::time::Duration;
use std::fmt;
use ffi::*;

/// An LCM instance that handles publishing and subscribing,
/// as well as encoding and decoding messages.
#[derive(Debug)]
pub struct Lcm {
    lcm: *mut lcm_t,
    subscriptions: Vec<Rc<LcmSubscription>>,
}

pub struct LcmSubscription {
    subscription: *mut lcm_subscription_t,
    handler: Box<FnMut(*const lcm_recv_buf_t)>,
}

impl Lcm {
    /// Creates a new `Lcm` instance.
    ///
    /// ```
    /// use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// ```
    pub fn new() -> Result<Lcm> {
        trace!("Creating LCM instance");
        let lcm = unsafe { lcm_create(ptr::null()) };
        match lcm.is_null() {
            true => Err(Error::new(ErrorKind::Other, "Failed to initialize LCM.")),
            false => {
                Ok(Lcm {
                    lcm: lcm,
                    subscriptions: Vec::new(),
                })
            }
        }
    }

    pub fn get_fileno(&self) -> ::std::os::raw::c_int {
        unsafe { lcm_get_fileno(self.lcm) }
    }

    /// Subscribes a callback to a particular topic.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("GREETINGS", |name: String| println!("Hello, {}!", name) );
    /// ```
    pub fn subscribe<M, F>(&mut self, channel: &str, mut callback: F) -> Rc<LcmSubscription>
        where M: Message,
              F: FnMut(M) + 'static
    {
        trace!("Subscribing handler to channel {}", channel);

        let channel = CString::new(channel).unwrap();

        let handler = Box::new(move |rbuf: *const lcm_recv_buf_t| {
            trace!("Running handler");
            let mut buf = unsafe {
                let ref rbuf = *rbuf;
                let data = rbuf.data as *mut u8;
                let len = rbuf.data_size as usize;
                slice::from_raw_parts(data, len)
            };
            trace!("Decoding buffer: {:?}", buf);
            match M::decode_with_hash(&mut buf) {
                Ok(msg) => callback(msg),
                Err(_) => error!("Failed to decode buffer: {:?}", buf),
            }
        });

        let mut subscription = Rc::new(LcmSubscription {
            subscription: ptr::null_mut(),
            handler: handler,
        });

        let user_data = (subscription.deref() as *const _) as *mut _;

        let c_subscription = unsafe {
            lcm_subscribe(self.lcm,
                          channel.as_ptr(),
                          Some(Lcm::handler_callback::<M>),
                          user_data)
        };

        Rc::get_mut(&mut subscription).unwrap().subscription = c_subscription;
        self.subscriptions.push(subscription.clone());

        subscription
    }

    /// Unsubscribes a message handler.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// # let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("GREETINGS", handler_function);
    /// // ...
    /// lcm.unsubscribe(handler);
    /// ```
    pub fn unsubscribe(&mut self, handler: Rc<LcmSubscription>) -> Result<()> {
        trace!("Unsubscribing handler {:?}", handler.subscription);
        let result = unsafe { lcm_unsubscribe(self.lcm, handler.subscription) };

        self.subscriptions.retain(|sub| { sub.subscription != handler.subscription });

        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM: Failed to unsubscribe")),
        }
    }

    /// Publishes a message on the specified channel.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.publish("GREETINGS", &"Charles".to_string()).unwrap();
    /// ```
    pub fn publish<M>(&mut self, channel: &str, message: &M) -> Result<()>
        where M: Message
    {
        let channel = CString::new(channel).unwrap();
        let buffer = message.encode_with_hash()?;
        let result = unsafe {
            lcm_publish(self.lcm,
                        channel.as_ptr(),
                        buffer.as_ptr() as *mut _,
                        buffer.len() as _)
        };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error")),
        }
    }

    /// Waits for and dispatches the next incoming message.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// loop {
    /// # break;
    ///     lcm.handle().unwrap();
    /// }
    /// ```
    pub fn handle(&mut self) -> Result<()> {
        let result = unsafe { lcm_handle(self.lcm) };
        match result {
            0 => Ok(()),
            _ => Err(Error::new(ErrorKind::Other, "LCM Error")),
        }
    }

    /// Waits for and dispatches the next incoming message, up to a time limit.
    ///
    /// ```
    /// # use std::time::Duration;
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// let mut lcm = Lcm::new().unwrap();
    /// lcm.subscribe("POSITION", handler_function);
    /// let wait_dur = Duration::from_millis(100);
    /// loop {
    /// # break;
    ///     lcm.handle_timeout(Duration::from_millis(1000)).unwrap();
    /// }
    /// ```
    pub fn handle_timeout(&mut self, timeout: Duration) -> Result<()> {
        let result = unsafe { lcm_handle_timeout(self.lcm, (timeout.as_secs() * 1000) as i32 + (timeout.subsec_nanos() / 1000_000) as i32) };
        match result.cmp(&0) {
            Ordering::Less => Err(Error::new(ErrorKind::Other, "LCM Error")),
            Ordering::Equal => Err(Error::new(ErrorKind::Other, "LCM Timeout")),
            Ordering::Greater => Ok(()),
        }
    }

    /// Adjusts the maximum number of received messages that can be queued up for a subscription.
    /// The default is `30`.
    ///
    /// ```
    /// # use lcm::Lcm;
    /// # let handler_function = |name: String| println!("Hello, {}!", name);
    /// # let mut lcm = Lcm::new().unwrap();
    /// let handler = lcm.subscribe("POSITION", handler_function);
    /// lcm.subscription_set_queue_capacity(handler, 30);
    /// ```
    pub fn subscription_set_queue_capacity(&self, handler: Rc<LcmSubscription>, num_messages: usize) {
        let handler = handler.subscription;
        let num_messages = num_messages as _;
        unsafe { lcm_subscription_set_queue_capacity(handler, num_messages) };
    }

    extern "C" fn handler_callback<M>(rbuf: *const lcm_recv_buf_t,
                                      _: *const ::std::os::raw::c_char,
                                      user_data: *mut ::std::os::raw::c_void)
        where M: Message
    {
        trace!("Received data");
        let sub = user_data as *mut LcmSubscription;
        let sub = unsafe { &mut *sub };
        (sub.handler)(rbuf);
    }
}

impl Drop for Lcm {
    fn drop(&mut self) {
        trace!("Destroying Lcm instance");
        unsafe { lcm_destroy(self.lcm) };
    }
}

impl fmt::Debug for LcmSubscription {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "LcmSubscription {{ subscription: {:?}, handler: {:?} }}", self.subscription, &*self.handler as *const _)
    }
}

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

    #[test]
    fn initialized() {
        let _lcm = Lcm::new().unwrap();
    }

    #[test]
    fn test_subscribe() {
        let mut lcm = Lcm::new().unwrap();
        lcm.subscribe("channel", |_: String| {});
        assert_eq!(lcm.subscriptions.len(), 1);
    }

    #[test]
    fn test_unsubscribe() {
        let mut lcm = Lcm::new().unwrap();
        let sub = lcm.subscribe("channel", |_: String| {});
        lcm.unsubscribe(sub).unwrap();
        assert_eq!(lcm.subscriptions.len(), 0);
    }
}
<|endoftext|>


<commit_before>//! Simple FBX 7.4 binary loader.

use parser::binary::{Parser, ParserSource, FbxFooter, Event, Attributes};
use loader::binary::simple::{Result, Error, GenericNode};
pub use self::connections::{Connections, Connection};
pub use self::definitions::{Definitions, ObjectType};
pub use self::fbx_header_extension::{FbxHeaderExtension, CreationTimeStamp, SceneInfo};
pub use self::global_settings::GlobalSettings;
pub use self::objects::{LoadObjects7400, ObjectProperties};
pub use self::properties70::{Properties70, PropertyMap, PropertyValue};
pub use self::takes::{Takes, Take};

/// Tries to load the node attributes for parsing a child node.
///
/// The type of `$parser` should be `P: Parser<R> where R: ParserSource`, and
/// the type of `$load_attr` should be `F: FnOnce(&str, &mut Attributes) -> Result<T, _>`.
///
/// This will returns from the parent function on errors.
macro_rules! try_get_node_attrs {
    ($parser:expr, $load_attr:expr) => {{
        use $crate::parser::binary::Event;
        match $parser.next_event()? {
            Event::StartNode(info) => $load_attr(info.name, info.attributes)?,
            Event::EndNode => break,
            ev => panic!("Unexpected node event: {:?}", ev),
        }
    }}
}

/// Unwraps `$node_op` or returns `Error::MissingNode` error.
macro_rules! ensure_node_exists {
    ($node_opt:expr, $parent:expr, $child:expr) => {
        $node_opt.ok_or_else(|| {
            $crate::loader::binary::simple::Error::missing_node($parent, $child)
        })?
    };
}

macro_rules! child_attr_loader {
    (@load $enum_name:ident; $_name:ident; $_attrs:ident; $variant:ident($content:ty);
        => $load:block $(=> $_rest_load:block)*) => {
        $load
    };
    (@load $enum_name:ident; $name:ident; $attrs:ident; $variant:ident($content:ty);) => {
        <$content>::from_attributes(&mut $attrs)
            ?
            .ok_or_else(|| Error::InvalidAttribute($name.to_owned()))
            .map($enum_name::$variant)
    };
    (@load $enum_name:ident; $name:ident; $attrs:ident; $variant:ident;) => {
        Ok($enum_name::$variant)
    };
    ($enum_name:ident {
        $($node_name:expr => $variant:ident$(($content:ty))* $(=> $load:block)*),*,
    }) => {
        #[derive(Debug)]
        enum $enum_name {
            $($variant$(($content))*),*,
        }
        impl $enum_name {
            pub fn load<R: ParserSource>(name: &str, mut attrs: Attributes<R>)
                -> $crate::loader::binary::simple::Result<Self> {
                use parser::binary::utils::AttributeValues;

                match name {
                    $($node_name => child_attr_loader!{
                        @load $enum_name; name; attrs; $variant$(($content))*; $(=> $load)*
                    }),*,
                    _ => Err(Error::UnexpectedNode(name.to_owned())),
                }
            }
        }
    };
}

pub mod connections;
pub mod definitions;
pub mod fbx_header_extension;
pub mod global_settings;
pub mod objects;
pub mod properties70;
pub mod takes;

/// FBX 7.4 or later.
#[derive(Debug, Clone, PartialEq)]
pub struct Fbx7400<O: LoadObjects7400> {
    /// FBX version.
    pub version: u32,
    /// `FBXHeaderExtension`.
    pub fbx_header_extension: FbxHeaderExtension,
    /// `FileId`.
    pub file_id: FileId,
    /// `CreationTime`.
    pub creation_time: CreationTime,
    /// `Creator`.
    pub creator: Creator,
    /// `References`.
    pub references: References,
    /// `GlobalSettings`.
    pub global_settings: GlobalSettings,
    /// `Documents`.
    pub documents: Documents,
    /// `Definitions`.
    pub definitions: Definitions,
    /// `Objects`.
    pub objects: O::Objects,
    /// `Connections`.
    pub connections: Connections,
    /// `Takes`.
    pub takes: Option<Takes>,
    /// FBX footer.
    pub footer: Option<FbxFooter>,
}

impl<O: LoadObjects7400> Fbx7400<O> {
    /// Loads FBX 7400 (or later) structure from the given parser.
    pub fn load_from_parser<R: ParserSource, P: Parser<R>>(
        version: u32,
        mut parser: P,
        objs_loader: O
    ) -> Result<Self> {
        info!("FBX version: {}, loading in FBX 7400 mode", version);

        let mut objs_loader = Some(objs_loader);
        let footer;
        let mut fbx_header_extension = None;
        let mut file_id = None;
        let mut creation_time = None;
        let mut creator = None;
        let mut global_settings = None;
        let mut documents = None;
        let mut references = None;
        let mut definitions = None;
        let mut objects_and_before = None;
        let mut connections = None;
        let mut takes = None;
        loop {
            let node_type = match parser.next_event()? {
                Event::StartFbx(_) |
                Event::EndNode => unreachable!(),
                Event::EndFbx(f) => {
                    footer = f.ok();
                    break;
                },
                Event::StartNode(info) => NodeType::load(info.name, info.attributes)?,
            };
            debug!("node_type: {:?}", node_type);
            match node_type {
                NodeType::FbxHeaderExtension => {
                    fbx_header_extension = Some(FbxHeaderExtension::load(parser.subtree_parser())?);
                },
                NodeType::FileId(attrs) => {
                    file_id = Some(FileId::load(parser.subtree_parser(), attrs)?);
                },
                NodeType::CreationTime(attrs) => {
                    creation_time = Some(CreationTime::load(parser.subtree_parser(), attrs)?);
                },
                NodeType::Creator(attrs) => {
                    creator = Some(Creator::load(parser.subtree_parser(), attrs)?);
                },
                NodeType::GlobalSettings => {
                    global_settings = Some(GlobalSettings::load(parser.subtree_parser())?);
                },
                NodeType::Documents => {
                    documents = Some(Documents::load(parser.subtree_parser())?);
                },
                NodeType::References => {
                    references = Some(References::load(parser.subtree_parser())?);
                },
                NodeType::Definitions => {
                    definitions = Some(Definitions::load(parser.subtree_parser())?);
                },
                NodeType::Objects => {
                    if let Some(objs_loader) = objs_loader.take() {
                        let nodes_before_objects = NodesBeforeObjects {
                            version: version,
                            fbx_header_extension: ensure_node_exists!(fbx_header_extension.take(),
                                                                      "(root)",
                                                                      "FBXHeaderExtension"),
                            file_id: ensure_node_exists!(file_id.take(), "(root)", "FileId"),
                            creation_time: ensure_node_exists!(creation_time.take(),
                                                               "(root)",
                                                               "CreationTime"),
                            creator: ensure_node_exists!(creator.take(), "(root)", "Creator"),
                            global_settings: ensure_node_exists!(global_settings.take(),
                                                                 "(root)",
                                                                 "GlobalSettings"),
                            documents: ensure_node_exists!(documents.take(), "(root)", "Documents"),
                            references: ensure_node_exists!(references.take(),
                                                            "(root)",
                                                            "References"),
                            definitions: ensure_node_exists!(definitions.take(),
                                                             "(root)",
                                                             "Definitions"),
                        };
                        let objects = load_objects(parser.subtree_parser(),
                                                   objs_loader,
                                                   &nodes_before_objects)?;
                        objects_and_before = Some((objects, nodes_before_objects));
                    } else {
                        warn!("Multiple `Objects` node found, ignoring.");
                    }
                },
                NodeType::Connections => {
                    connections = Some(Connections::load(parser.subtree_parser())?);
                },
                NodeType::Takes => {
                    takes = Some(Takes::load(parser.subtree_parser())?);
                },
            }
        }

        let (objects, nodes_before_objects) =
            ensure_node_exists!(objects_and_before, "(root)", "Objects");

        Ok(Fbx7400 {
            version: version,
            fbx_header_extension: nodes_before_objects.fbx_header_extension,
            file_id: nodes_before_objects.file_id,
            creation_time: nodes_before_objects.creation_time,
            creator: nodes_before_objects.creator,
            global_settings: nodes_before_objects.global_settings,
            documents: nodes_before_objects.documents,
            references: nodes_before_objects.references,
            definitions: nodes_before_objects.definitions,
            objects: objects,
            connections: ensure_node_exists!(connections, "(root)", "Connections"),
            takes: takes,
            footer: footer,
        })
    }
}

/// Toplevel nodes before `Objects`.
///
/// These nodes would be referred by objects loader.
#[derive(Debug, Clone, PartialEq)]
pub struct NodesBeforeObjects {
    /// FBX version.
    pub version: u32,
    /// `FBXHeaderExtension`.
    pub fbx_header_extension: FbxHeaderExtension,
    /// `FileId`.
    pub file_id: FileId,
    /// `CreationTime`.
    pub creation_time: CreationTime,
    /// `Creator`.
    pub creator: Creator,
    /// `References`.
    pub references: References,
    /// `GlobalSettings`.
    pub global_settings: GlobalSettings,
    /// `Documents`.
    pub documents: Documents,
    /// `Definitions`.
    pub definitions: Definitions,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum NodeType {
    FbxHeaderExtension,
    FileId(Vec<u8>),
    CreationTime(String),
    Creator(String),
    GlobalSettings,
    Documents,
    References,
    Definitions,
    Objects,
    Connections,
    Takes,
}

impl NodeType {
    /// Creates `NodeType` from the given node name.
    pub fn load<R: ParserSource>(name: &str, mut attrs: Attributes<R>) -> Result<Self> {
        use parser::binary::utils::AttributeValues;

        match name {
            "FBXHeaderExtension" => Ok(NodeType::FbxHeaderExtension),
            "FileId" => {
                <Vec<u8>>::from_attributes(&mut attrs)
                    ?
                    .ok_or_else(|| Error::InvalidAttribute(name.to_owned()))
                    .map(NodeType::FileId)
            },
            "CreationTime" => {
                <String>::from_attributes(&mut attrs)
                    ?
                    .ok_or_else(|| Error::InvalidAttribute(name.to_owned()))
                    .map(NodeType::CreationTime)
            },
            "Creator" => {
                <String>::from_attributes(&mut attrs)
                    ?
                    .ok_or_else(|| Error::InvalidAttribute(name.to_owned()))
                    .map(NodeType::Creator)
            },
            "GlobalSettings" => Ok(NodeType::GlobalSettings),
            "Documents" => Ok(NodeType::Documents),
            "References" => Ok(NodeType::References),
            "Definitions" => Ok(NodeType::Definitions),
            "Objects" => Ok(NodeType::Objects),
            "Connections" => Ok(NodeType::Connections),
            "Takes" => Ok(NodeType::Takes),
            _ => Err(Error::UnexpectedNode(name.to_owned())),
        }
    }
}

/// `FileId`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub Vec<u8>);

impl FileId {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P, attrs: Vec<u8>) -> Result<Self> {
        parser.skip_current_node()?;
        Ok(FileId(attrs))
    }
}

/// `CreationTime`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CreationTime(pub String);

impl CreationTime {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P, attrs: String) -> Result<Self> {
        parser.skip_current_node()?;
        Ok(CreationTime(attrs))
    }
}

/// `Creator`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Creator(pub String);

impl Creator {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P, attrs: String) -> Result<Self> {
        parser.skip_current_node()?;
        Ok(Creator(attrs))
    }
}

/// `Documents`.
#[derive(Debug, Clone, PartialEq)]
pub struct Documents {
    /// Child nodes.
    pub nodes: Vec<GenericNode>,
}

impl Documents {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P) -> Result<Self> {
        let nodes = GenericNode::load_from_parser(&mut parser)?.0;
        Ok(Documents { nodes: nodes })
    }
}

/// `References`.
#[derive(Debug, Clone, PartialEq)]
pub struct References {
    /// Child nodes.
    pub nodes: Vec<GenericNode>,
}

impl References {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P) -> Result<Self> {
        let nodes = GenericNode::load_from_parser(&mut parser)?.0;
        Ok(References { nodes: nodes })
    }
}

/// Returns `Option<(name: &'a str, class: &'a str)>`
pub fn separate_name_class(name_class: &str) -> Option<(&str, &str)> {
    name_class.find("\u{0}\u{1}")
        .map(|sep_pos| (&name_class[0..sep_pos], &name_class[sep_pos + 2..]))
}

/// Loads node contents from the parser.
fn load_objects<R: ParserSource, P: Parser<R>, O: LoadObjects7400>(
    mut parser: P,
    mut objs_loader: O,
    nodes_before_objects: &NodesBeforeObjects
) -> Result<O::Objects> {
    loop {
        let props = try_get_node_attrs!(parser, ObjectProperties::load);
        let mut sub_parser = parser.subtree_parser();
        objs_loader.load(props, &mut sub_parser, nodes_before_objects)?;
        sub_parser.skip_current_node()?;
    }
    objs_loader.build()
}
<commit_msg>Fix `load_object()` to skip unread nodes correctly<commit_after>//! Simple FBX 7.4 binary loader.

use parser::binary::{Parser, ParserSource, FbxFooter, Event, Attributes};
use loader::binary::simple::{Result, Error, GenericNode};
pub use self::connections::{Connections, Connection};
pub use self::definitions::{Definitions, ObjectType};
pub use self::fbx_header_extension::{FbxHeaderExtension, CreationTimeStamp, SceneInfo};
pub use self::global_settings::GlobalSettings;
pub use self::objects::{LoadObjects7400, ObjectProperties};
pub use self::properties70::{Properties70, PropertyMap, PropertyValue};
pub use self::takes::{Takes, Take};

/// Tries to load the node attributes for parsing a child node.
///
/// The type of `$parser` should be `P: Parser<R> where R: ParserSource`, and
/// the type of `$load_attr` should be `F: FnOnce(&str, &mut Attributes) -> Result<T, _>`.
///
/// This will returns from the parent function on errors.
macro_rules! try_get_node_attrs {
    ($parser:expr, $load_attr:expr) => {{
        use $crate::parser::binary::Event;
        match $parser.next_event()? {
            Event::StartNode(info) => $load_attr(info.name, info.attributes)?,
            Event::EndNode => break,
            ev => panic!("Unexpected node event: {:?}", ev),
        }
    }}
}

/// Unwraps `$node_op` or returns `Error::MissingNode` error.
macro_rules! ensure_node_exists {
    ($node_opt:expr, $parent:expr, $child:expr) => {
        $node_opt.ok_or_else(|| {
            $crate::loader::binary::simple::Error::missing_node($parent, $child)
        })?
    };
}

macro_rules! child_attr_loader {
    (@load $enum_name:ident; $_name:ident; $_attrs:ident; $variant:ident($content:ty);
        => $load:block $(=> $_rest_load:block)*) => {
        $load
    };
    (@load $enum_name:ident; $name:ident; $attrs:ident; $variant:ident($content:ty);) => {
        <$content>::from_attributes(&mut $attrs)
            ?
            .ok_or_else(|| Error::InvalidAttribute($name.to_owned()))
            .map($enum_name::$variant)
    };
    (@load $enum_name:ident; $name:ident; $attrs:ident; $variant:ident;) => {
        Ok($enum_name::$variant)
    };
    ($enum_name:ident {
        $($node_name:expr => $variant:ident$(($content:ty))* $(=> $load:block)*),*,
    }) => {
        #[derive(Debug)]
        enum $enum_name {
            $($variant$(($content))*),*,
        }
        impl $enum_name {
            pub fn load<R: ParserSource>(name: &str, mut attrs: Attributes<R>)
                -> $crate::loader::binary::simple::Result<Self> {
                use parser::binary::utils::AttributeValues;

                match name {
                    $($node_name => child_attr_loader!{
                        @load $enum_name; name; attrs; $variant$(($content))*; $(=> $load)*
                    }),*,
                    _ => Err(Error::UnexpectedNode(name.to_owned())),
                }
            }
        }
    };
}

pub mod connections;
pub mod definitions;
pub mod fbx_header_extension;
pub mod global_settings;
pub mod objects;
pub mod properties70;
pub mod takes;

/// FBX 7.4 or later.
#[derive(Debug, Clone, PartialEq)]
pub struct Fbx7400<O: LoadObjects7400> {
    /// FBX version.
    pub version: u32,
    /// `FBXHeaderExtension`.
    pub fbx_header_extension: FbxHeaderExtension,
    /// `FileId`.
    pub file_id: FileId,
    /// `CreationTime`.
    pub creation_time: CreationTime,
    /// `Creator`.
    pub creator: Creator,
    /// `References`.
    pub references: References,
    /// `GlobalSettings`.
    pub global_settings: GlobalSettings,
    /// `Documents`.
    pub documents: Documents,
    /// `Definitions`.
    pub definitions: Definitions,
    /// `Objects`.
    pub objects: O::Objects,
    /// `Connections`.
    pub connections: Connections,
    /// `Takes`.
    pub takes: Option<Takes>,
    /// FBX footer.
    pub footer: Option<FbxFooter>,
}

impl<O: LoadObjects7400> Fbx7400<O> {
    /// Loads FBX 7400 (or later) structure from the given parser.
    pub fn load_from_parser<R: ParserSource, P: Parser<R>>(
        version: u32,
        mut parser: P,
        objs_loader: O
    ) -> Result<Self> {
        info!("FBX version: {}, loading in FBX 7400 mode", version);

        let mut objs_loader = Some(objs_loader);
        let footer;
        let mut fbx_header_extension = None;
        let mut file_id = None;
        let mut creation_time = None;
        let mut creator = None;
        let mut global_settings = None;
        let mut documents = None;
        let mut references = None;
        let mut definitions = None;
        let mut objects_and_before = None;
        let mut connections = None;
        let mut takes = None;
        loop {
            let node_type = match parser.next_event()? {
                Event::StartFbx(_) |
                Event::EndNode => unreachable!(),
                Event::EndFbx(f) => {
                    footer = f.ok();
                    break;
                },
                Event::StartNode(info) => NodeType::load(info.name, info.attributes)?,
            };
            debug!("node_type: {:?}", node_type);
            match node_type {
                NodeType::FbxHeaderExtension => {
                    fbx_header_extension = Some(FbxHeaderExtension::load(parser.subtree_parser())?);
                },
                NodeType::FileId(attrs) => {
                    file_id = Some(FileId::load(parser.subtree_parser(), attrs)?);
                },
                NodeType::CreationTime(attrs) => {
                    creation_time = Some(CreationTime::load(parser.subtree_parser(), attrs)?);
                },
                NodeType::Creator(attrs) => {
                    creator = Some(Creator::load(parser.subtree_parser(), attrs)?);
                },
                NodeType::GlobalSettings => {
                    global_settings = Some(GlobalSettings::load(parser.subtree_parser())?);
                },
                NodeType::Documents => {
                    documents = Some(Documents::load(parser.subtree_parser())?);
                },
                NodeType::References => {
                    references = Some(References::load(parser.subtree_parser())?);
                },
                NodeType::Definitions => {
                    definitions = Some(Definitions::load(parser.subtree_parser())?);
                },
                NodeType::Objects => {
                    if let Some(objs_loader) = objs_loader.take() {
                        let nodes_before_objects = NodesBeforeObjects {
                            version: version,
                            fbx_header_extension: ensure_node_exists!(fbx_header_extension.take(),
                                                                      "(root)",
                                                                      "FBXHeaderExtension"),
                            file_id: ensure_node_exists!(file_id.take(), "(root)", "FileId"),
                            creation_time: ensure_node_exists!(creation_time.take(),
                                                               "(root)",
                                                               "CreationTime"),
                            creator: ensure_node_exists!(creator.take(), "(root)", "Creator"),
                            global_settings: ensure_node_exists!(global_settings.take(),
                                                                 "(root)",
                                                                 "GlobalSettings"),
                            documents: ensure_node_exists!(documents.take(), "(root)", "Documents"),
                            references: ensure_node_exists!(references.take(),
                                                            "(root)",
                                                            "References"),
                            definitions: ensure_node_exists!(definitions.take(),
                                                             "(root)",
                                                             "Definitions"),
                        };
                        let objects = load_objects(parser.subtree_parser(),
                                                   objs_loader,
                                                   &nodes_before_objects)?;
                        objects_and_before = Some((objects, nodes_before_objects));
                    } else {
                        warn!("Multiple `Objects` node found, ignoring.");
                    }
                },
                NodeType::Connections => {
                    connections = Some(Connections::load(parser.subtree_parser())?);
                },
                NodeType::Takes => {
                    takes = Some(Takes::load(parser.subtree_parser())?);
                },
            }
        }

        let (objects, nodes_before_objects) =
            ensure_node_exists!(objects_and_before, "(root)", "Objects");

        Ok(Fbx7400 {
            version: version,
            fbx_header_extension: nodes_before_objects.fbx_header_extension,
            file_id: nodes_before_objects.file_id,
            creation_time: nodes_before_objects.creation_time,
            creator: nodes_before_objects.creator,
            global_settings: nodes_before_objects.global_settings,
            documents: nodes_before_objects.documents,
            references: nodes_before_objects.references,
            definitions: nodes_before_objects.definitions,
            objects: objects,
            connections: ensure_node_exists!(connections, "(root)", "Connections"),
            takes: takes,
            footer: footer,
        })
    }
}

/// Toplevel nodes before `Objects`.
///
/// These nodes would be referred by objects loader.
#[derive(Debug, Clone, PartialEq)]
pub struct NodesBeforeObjects {
    /// FBX version.
    pub version: u32,
    /// `FBXHeaderExtension`.
    pub fbx_header_extension: FbxHeaderExtension,
    /// `FileId`.
    pub file_id: FileId,
    /// `CreationTime`.
    pub creation_time: CreationTime,
    /// `Creator`.
    pub creator: Creator,
    /// `References`.
    pub references: References,
    /// `GlobalSettings`.
    pub global_settings: GlobalSettings,
    /// `Documents`.
    pub documents: Documents,
    /// `Definitions`.
    pub definitions: Definitions,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum NodeType {
    FbxHeaderExtension,
    FileId(Vec<u8>),
    CreationTime(String),
    Creator(String),
    GlobalSettings,
    Documents,
    References,
    Definitions,
    Objects,
    Connections,
    Takes,
}

impl NodeType {
    /// Creates `NodeType` from the given node name.
    pub fn load<R: ParserSource>(name: &str, mut attrs: Attributes<R>) -> Result<Self> {
        use parser::binary::utils::AttributeValues;

        match name {
            "FBXHeaderExtension" => Ok(NodeType::FbxHeaderExtension),
            "FileId" => {
                <Vec<u8>>::from_attributes(&mut attrs)
                    ?
                    .ok_or_else(|| Error::InvalidAttribute(name.to_owned()))
                    .map(NodeType::FileId)
            },
            "CreationTime" => {
                <String>::from_attributes(&mut attrs)
                    ?
                    .ok_or_else(|| Error::InvalidAttribute(name.to_owned()))
                    .map(NodeType::CreationTime)
            },
            "Creator" => {
                <String>::from_attributes(&mut attrs)
                    ?
                    .ok_or_else(|| Error::InvalidAttribute(name.to_owned()))
                    .map(NodeType::Creator)
            },
            "GlobalSettings" => Ok(NodeType::GlobalSettings),
            "Documents" => Ok(NodeType::Documents),
            "References" => Ok(NodeType::References),
            "Definitions" => Ok(NodeType::Definitions),
            "Objects" => Ok(NodeType::Objects),
            "Connections" => Ok(NodeType::Connections),
            "Takes" => Ok(NodeType::Takes),
            _ => Err(Error::UnexpectedNode(name.to_owned())),
        }
    }
}

/// `FileId`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub Vec<u8>);

impl FileId {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P, attrs: Vec<u8>) -> Result<Self> {
        parser.skip_current_node()?;
        Ok(FileId(attrs))
    }
}

/// `CreationTime`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CreationTime(pub String);

impl CreationTime {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P, attrs: String) -> Result<Self> {
        parser.skip_current_node()?;
        Ok(CreationTime(attrs))
    }
}

/// `Creator`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Creator(pub String);

impl Creator {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P, attrs: String) -> Result<Self> {
        parser.skip_current_node()?;
        Ok(Creator(attrs))
    }
}

/// `Documents`.
#[derive(Debug, Clone, PartialEq)]
pub struct Documents {
    /// Child nodes.
    pub nodes: Vec<GenericNode>,
}

impl Documents {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P) -> Result<Self> {
        let nodes = GenericNode::load_from_parser(&mut parser)?.0;
        Ok(Documents { nodes: nodes })
    }
}

/// `References`.
#[derive(Debug, Clone, PartialEq)]
pub struct References {
    /// Child nodes.
    pub nodes: Vec<GenericNode>,
}

impl References {
    /// Loads node contents from the parser.
    pub fn load<R: ParserSource, P: Parser<R>>(mut parser: P) -> Result<Self> {
        let nodes = GenericNode::load_from_parser(&mut parser)?.0;
        Ok(References { nodes: nodes })
    }
}

/// Returns `Option<(name: &'a str, class: &'a str)>`
pub fn separate_name_class(name_class: &str) -> Option<(&str, &str)> {
    name_class.find("\u{0}\u{1}")
        .map(|sep_pos| (&name_class[0..sep_pos], &name_class[sep_pos + 2..]))
}

/// Loads node contents from the parser.
fn load_objects<R: ParserSource, P: Parser<R>, O: LoadObjects7400>(
    mut parser: P,
    mut objs_loader: O,
    nodes_before_objects: &NodesBeforeObjects
) -> Result<O::Objects> {
    loop {
        let props = try_get_node_attrs!(parser, ObjectProperties::load);
        let mut sub_parser = parser.subtree_parser();
        objs_loader.load(props, &mut sub_parser, nodes_before_objects)?;
        sub_parser.skip_to_end()?;
    }
    objs_loader.build()
}
<|endoftext|>


<commit_before>use std::io as std_io;
use bytes::Buf;
use futures::{Async, Poll};

use AsyncRead;

/// Writes bytes asynchronously.
///
/// The trait inherits from `std::io::Write` and indicates that an I/O object is
/// **nonblocking**. All non-blocking I/O objects must return an error when
/// bytes cannot be written instead of blocking the current thread.
///
/// Specifically, this means that the `poll_write` function will return one of
/// the following:
///
/// * `Ok(Async::Ready(n))` means that `n` bytes of data was immediately
///   written.
///
/// * `Ok(Async::NotReady)` means that no data was written from the buffer
///   provided. The I/O object is not currently writable but may become writable
///   in the future. Most importantly, **the current future's task is scheduled
///   to get unparked when the object is writable**. This means that like
///   `Future::poll` you'll receive a notification when the I/O object is
///   writable again.
///
/// * `Err(e)` for other errors are standard I/O errors coming from the
///   underlying object.
///
/// This trait importantly means that the `write` method only works in the
/// context of a future's task. The object may panic if used outside of a task.
///
/// Note that this trait also represents that the  `Write::flush` method works
/// very similarly to the `write` method, notably that `Ok(())` means that the
/// writer has successfully been flushed, a "would block" error means that the
/// current task is ready to receive a notification when flushing can make more
/// progress, and otherwise normal errors can happen as well.
pub trait AsyncWrite: std_io::Write {
    /// Attempt to write bytes from `buf` into the object.
    ///
    /// On success, returns `Ok(Async::Ready(num_bytes_written))`.
    ///
    /// If the object is not ready for writing, the method returns
    /// `Ok(Async::NotReady)` and arranges for the current task (via
    /// `cx.waker()`) to receive a notification when the object becomes
    /// readable or is closed.
    fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, std_io::Error> {
        match self.write(buf) {
            Ok(t) => Ok(Async::Ready(t)),
            Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => {
                return Ok(Async::NotReady)
            }
            Err(e) => return Err(e.into()),
        }
    }

    /// Attempt to flush the object, ensuring that any buffered data reach
    /// their destination.
    ///
    /// On success, returns `Ok(Async::Ready(()))`.
    ///
    /// If flushing cannot immediately complete, this method returns
    /// `Ok(Async::NotReady)` and arranges for the current task (via
    /// `cx.waker()`) to receive a notification when the object can make
    /// progress towards flushing.
    fn poll_flush(&mut self) -> Poll<(), std_io::Error> {
        match self.flush() {
            Ok(t) => Ok(Async::Ready(t)),
            Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => {
                return Ok(Async::NotReady)
            }
            Err(e) => return Err(e.into()),
        }
    }

    /// Initiates or attempts to shut down this writer, returning success when
    /// the I/O connection has completely shut down.
    ///
    /// This method is intended to be used for asynchronous shutdown of I/O
    /// connections. For example this is suitable for implementing shutdown of a
    /// TLS connection or calling `TcpStream::shutdown` on a proxied connection.
    /// Protocols sometimes need to flush out final pieces of data or otherwise
    /// perform a graceful shutdown handshake, reading/writing more data as
    /// appropriate. This method is the hook for such protocols to implement the
    /// graceful shutdown logic.
    ///
    /// This `shutdown` method is required by implementers of the
    /// `AsyncWrite` trait. Wrappers typically just want to proxy this call
    /// through to the wrapped type, and base types will typically implement
    /// shutdown logic here or just return `Ok(().into())`. Note that if you're
    /// wrapping an underlying `AsyncWrite` a call to `shutdown` implies that
    /// transitively the entire stream has been shut down. After your wrapper's
    /// shutdown logic has been executed you should shut down the underlying
    /// stream.
    ///
    /// Invocation of a `shutdown` implies an invocation of `flush`. Once this
    /// method returns `Ready` it implies that a flush successfully happened
    /// before the shutdown happened. That is, callers don't need to call
    /// `flush` before calling `shutdown`. They can rely that by calling
    /// `shutdown` any pending buffered data will be written out.
    ///
    /// # Return value
    ///
    /// This function returns a `Poll<(), io::Error>` classified as such:
    ///
    /// * `Ok(Async::Ready(()))` - indicates that the connection was
    ///   successfully shut down and is now safe to deallocate/drop/close
    ///   resources associated with it. This method means that the current task
    ///   will no longer receive any notifications due to this method and the
    ///   I/O object itself is likely no longer usable.
    ///
    /// * `Ok(Async::NotReady)` - indicates that shutdown is initiated but could
    ///   not complete just yet. This may mean that more I/O needs to happen to
    ///   continue this shutdown operation. The current task is scheduled to
    ///   receive a notification when it's otherwise ready to continue the
    ///   shutdown operation. When woken up this method should be called again.
    ///
    /// * `Err(e)` - indicates a fatal error has happened with shutdown,
    ///   indicating that the shutdown operation did not complete successfully.
    ///   This typically means that the I/O object is no longer usable.
    ///
    /// # Errors
    ///
    /// This function can return normal I/O errors through `Err`, described
    /// above. Additionally this method may also render the underlying
    /// `Write::write` method no longer usable (e.g. will return errors in the
    /// future). It's recommended that once `shutdown` is called the
    /// `write` method is no longer called.
    ///
    /// # Panics
    ///
    /// This function will panic if not called within the context of a future's
    /// task.
    fn shutdown(&mut self) -> Poll<(), std_io::Error>;

    /// Write a `Buf` into this value, returning how many bytes were written.
    ///
    /// Note that this method will advance the `buf` provided automatically by
    /// the number of bytes written.
    fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, std_io::Error>
        where Self: Sized,
    {
        if !buf.has_remaining() {
            return Ok(Async::Ready(0));
        }

        let n = try_ready!(self.poll_write(buf.bytes()));
        buf.advance(n);
        Ok(Async::Ready(n))
    }
}

impl<T: ?Sized + AsyncWrite> AsyncWrite for Box<T> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        (**self).shutdown()
    }
}
impl<'a, T: ?Sized + AsyncWrite> AsyncWrite for &'a mut T {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        (**self).shutdown()
    }
}

impl AsyncRead for std_io::Repeat {
    unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
        false
    }
}

impl AsyncWrite for std_io::Sink {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}

// TODO: Implement `prepare_uninitialized_buffer` for `io::Take`.
// This is blocked on rust-lang/rust#27269
impl<T: AsyncRead> AsyncRead for std_io::Take<T> {
}

// TODO: Implement `prepare_uninitialized_buffer` when upstream exposes inner
// parts
impl<T, U> AsyncRead for std_io::Chain<T, U>
    where T: AsyncRead,
          U: AsyncRead,
{
}

impl<T: AsyncWrite> AsyncWrite for std_io::BufWriter<T> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        try_ready!(self.poll_flush());
        self.get_mut().shutdown()
    }
}

impl<T: AsyncRead> AsyncRead for std_io::BufReader<T> {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        self.get_ref().prepare_uninitialized_buffer(buf)
    }
}

impl<T: AsRef<[u8]>> AsyncRead for std_io::Cursor<T> {
}

impl<'a> AsyncWrite for std_io::Cursor<&'a mut [u8]> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}

impl AsyncWrite for std_io::Cursor<Vec<u8>> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}

impl AsyncWrite for std_io::Cursor<Box<[u8]>> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}
<commit_msg>io: implement prepare_uninitialized_buffer for Take and Chain (#678)<commit_after>use std::io as std_io;
use bytes::Buf;
use futures::{Async, Poll};

use AsyncRead;

/// Writes bytes asynchronously.
///
/// The trait inherits from `std::io::Write` and indicates that an I/O object is
/// **nonblocking**. All non-blocking I/O objects must return an error when
/// bytes cannot be written instead of blocking the current thread.
///
/// Specifically, this means that the `poll_write` function will return one of
/// the following:
///
/// * `Ok(Async::Ready(n))` means that `n` bytes of data was immediately
///   written.
///
/// * `Ok(Async::NotReady)` means that no data was written from the buffer
///   provided. The I/O object is not currently writable but may become writable
///   in the future. Most importantly, **the current future's task is scheduled
///   to get unparked when the object is writable**. This means that like
///   `Future::poll` you'll receive a notification when the I/O object is
///   writable again.
///
/// * `Err(e)` for other errors are standard I/O errors coming from the
///   underlying object.
///
/// This trait importantly means that the `write` method only works in the
/// context of a future's task. The object may panic if used outside of a task.
///
/// Note that this trait also represents that the  `Write::flush` method works
/// very similarly to the `write` method, notably that `Ok(())` means that the
/// writer has successfully been flushed, a "would block" error means that the
/// current task is ready to receive a notification when flushing can make more
/// progress, and otherwise normal errors can happen as well.
pub trait AsyncWrite: std_io::Write {
    /// Attempt to write bytes from `buf` into the object.
    ///
    /// On success, returns `Ok(Async::Ready(num_bytes_written))`.
    ///
    /// If the object is not ready for writing, the method returns
    /// `Ok(Async::NotReady)` and arranges for the current task (via
    /// `cx.waker()`) to receive a notification when the object becomes
    /// readable or is closed.
    fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, std_io::Error> {
        match self.write(buf) {
            Ok(t) => Ok(Async::Ready(t)),
            Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => {
                return Ok(Async::NotReady)
            }
            Err(e) => return Err(e.into()),
        }
    }

    /// Attempt to flush the object, ensuring that any buffered data reach
    /// their destination.
    ///
    /// On success, returns `Ok(Async::Ready(()))`.
    ///
    /// If flushing cannot immediately complete, this method returns
    /// `Ok(Async::NotReady)` and arranges for the current task (via
    /// `cx.waker()`) to receive a notification when the object can make
    /// progress towards flushing.
    fn poll_flush(&mut self) -> Poll<(), std_io::Error> {
        match self.flush() {
            Ok(t) => Ok(Async::Ready(t)),
            Err(ref e) if e.kind() == std_io::ErrorKind::WouldBlock => {
                return Ok(Async::NotReady)
            }
            Err(e) => return Err(e.into()),
        }
    }

    /// Initiates or attempts to shut down this writer, returning success when
    /// the I/O connection has completely shut down.
    ///
    /// This method is intended to be used for asynchronous shutdown of I/O
    /// connections. For example this is suitable for implementing shutdown of a
    /// TLS connection or calling `TcpStream::shutdown` on a proxied connection.
    /// Protocols sometimes need to flush out final pieces of data or otherwise
    /// perform a graceful shutdown handshake, reading/writing more data as
    /// appropriate. This method is the hook for such protocols to implement the
    /// graceful shutdown logic.
    ///
    /// This `shutdown` method is required by implementers of the
    /// `AsyncWrite` trait. Wrappers typically just want to proxy this call
    /// through to the wrapped type, and base types will typically implement
    /// shutdown logic here or just return `Ok(().into())`. Note that if you're
    /// wrapping an underlying `AsyncWrite` a call to `shutdown` implies that
    /// transitively the entire stream has been shut down. After your wrapper's
    /// shutdown logic has been executed you should shut down the underlying
    /// stream.
    ///
    /// Invocation of a `shutdown` implies an invocation of `flush`. Once this
    /// method returns `Ready` it implies that a flush successfully happened
    /// before the shutdown happened. That is, callers don't need to call
    /// `flush` before calling `shutdown`. They can rely that by calling
    /// `shutdown` any pending buffered data will be written out.
    ///
    /// # Return value
    ///
    /// This function returns a `Poll<(), io::Error>` classified as such:
    ///
    /// * `Ok(Async::Ready(()))` - indicates that the connection was
    ///   successfully shut down and is now safe to deallocate/drop/close
    ///   resources associated with it. This method means that the current task
    ///   will no longer receive any notifications due to this method and the
    ///   I/O object itself is likely no longer usable.
    ///
    /// * `Ok(Async::NotReady)` - indicates that shutdown is initiated but could
    ///   not complete just yet. This may mean that more I/O needs to happen to
    ///   continue this shutdown operation. The current task is scheduled to
    ///   receive a notification when it's otherwise ready to continue the
    ///   shutdown operation. When woken up this method should be called again.
    ///
    /// * `Err(e)` - indicates a fatal error has happened with shutdown,
    ///   indicating that the shutdown operation did not complete successfully.
    ///   This typically means that the I/O object is no longer usable.
    ///
    /// # Errors
    ///
    /// This function can return normal I/O errors through `Err`, described
    /// above. Additionally this method may also render the underlying
    /// `Write::write` method no longer usable (e.g. will return errors in the
    /// future). It's recommended that once `shutdown` is called the
    /// `write` method is no longer called.
    ///
    /// # Panics
    ///
    /// This function will panic if not called within the context of a future's
    /// task.
    fn shutdown(&mut self) -> Poll<(), std_io::Error>;

    /// Write a `Buf` into this value, returning how many bytes were written.
    ///
    /// Note that this method will advance the `buf` provided automatically by
    /// the number of bytes written.
    fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, std_io::Error>
        where Self: Sized,
    {
        if !buf.has_remaining() {
            return Ok(Async::Ready(0));
        }

        let n = try_ready!(self.poll_write(buf.bytes()));
        buf.advance(n);
        Ok(Async::Ready(n))
    }
}

impl<T: ?Sized + AsyncWrite> AsyncWrite for Box<T> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        (**self).shutdown()
    }
}
impl<'a, T: ?Sized + AsyncWrite> AsyncWrite for &'a mut T {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        (**self).shutdown()
    }
}

impl AsyncRead for std_io::Repeat {
    unsafe fn prepare_uninitialized_buffer(&self, _: &mut [u8]) -> bool {
        false
    }
}

impl AsyncWrite for std_io::Sink {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}

impl<T: AsyncRead> AsyncRead for std_io::Take<T> {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        self.get_ref().prepare_uninitialized_buffer(buf)
    }
}

impl<T, U> AsyncRead for std_io::Chain<T, U>
    where T: AsyncRead,
          U: AsyncRead,
{
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        let (t, u) = self.get_ref();
        // We don't need to execute the second initializer if the first one
        // already zeroed the buffer out.
        t.prepare_uninitialized_buffer(buf) || u.prepare_uninitialized_buffer(buf)
    }
}

impl<T: AsyncWrite> AsyncWrite for std_io::BufWriter<T> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        try_ready!(self.poll_flush());
        self.get_mut().shutdown()
    }
}

impl<T: AsyncRead> AsyncRead for std_io::BufReader<T> {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        self.get_ref().prepare_uninitialized_buffer(buf)
    }
}

impl<T: AsRef<[u8]>> AsyncRead for std_io::Cursor<T> {
}

impl<'a> AsyncWrite for std_io::Cursor<&'a mut [u8]> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}

impl AsyncWrite for std_io::Cursor<Vec<u8>> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}

impl AsyncWrite for std_io::Cursor<Box<[u8]>> {
    fn shutdown(&mut self) -> Poll<(), std_io::Error> {
        Ok(().into())
    }
}
<|endoftext|>


<commit_before>use crate::asm::label_parsers::label_ref;
use crate::asm::register_parsers::register;
use crate::asm::Token;

use nom::digit;
use nom::types::CompleteStr;

named!(pub operand<CompleteStr, Token>,
    alt!(
        real_operand |
        integer_operand |
        label_ref |
        register |
        string
    )
);

named!(integer_operand<CompleteStr, Token>,
    ws!(
        do_parse!(
            tag!("#") >>
            sign: opt!(tag!("-")) >>
            integer: digit >>
            (
                {
                    let mut tmp = String::from("");
                    if sign.is_some() {
                        tmp.push_str("-");
                    }
                    tmp.push_str(&integer.to_string());
                    Token::Integer{value: tmp.parse::<i32>().unwrap()}
                }
            )
        )
    )
);

named!(real_operand<CompleteStr, Token>,
    ws!(
        do_parse!(
            tag!("#") >>
            sign: opt!(tag!("-")) >>
            left:  digit >>
            tag!(".") >>
            right: digit >>
            (
                {
                    let mut tmp = String::from("");
                    if sign.is_some() {
                        tmp.push_str("-");
                    }
                    tmp.push_str(&left.to_string());
                    tmp.push_str(".");
                    tmp.push_str(&right.to_string());
                    Token::Real{value: tmp.parse::<f64>().unwrap()}
                }
            )
        )
    )
);

named!(string<CompleteStr, Token>,
    do_parse!(
        tag!("'") >>
        content: take_until!("'") >>
        tag!("'") >>
        (
            Token::DoString{ value: content.to_string() }
        )
    )
);

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

    #[test]
    fn test_parse_integer() {
        let result = integer_operand(CompleteStr("#42"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Integer { value: 42 });

        let result = integer_operand(CompleteStr("42"));
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_real() {
        let result = real_operand(CompleteStr("#4.2"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Real { value: 4.2 });

        let result = real_operand(CompleteStr("4.2"));
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_operand() {
        let result = operand(CompleteStr("#3.145"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Real { value: 3.145 });

        let result = operand(CompleteStr("#4"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Integer { value: 4 });
    }

    #[test]
    fn test_parse_label() {
        let result = operand(CompleteStr("@test"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(
            value,
            Token::LabelRef {
                name: "test".to_string()
            }
        );

        let result = operand(CompleteStr("test"));
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_string() {
        let result = string(CompleteStr("'hello do'"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(
            value,
            Token::DoString {
                value: "hello do".to_string()
            }
        );

        let result = string(CompleteStr("'invalid"));
        assert!(result.is_err());
    }
}
<commit_msg>added TODO and cleanup<commit_after>use crate::asm::label_parsers::label_ref;
use crate::asm::register_parsers::register;
use crate::asm::Token;

use nom::digit;
use nom::types::CompleteStr;

// TODO: if asm can understand variables then the compiler gets a bit
// simpler.
named!(pub operand<CompleteStr, Token>,
    alt!(
        real_operand |
        integer_operand |
        label_ref |
        register |
        string
    )
);

named!(integer_operand<CompleteStr, Token>,
    ws!(
        do_parse!(
            tag!("#") >>
            sign: opt!(tag!("-")) >>
            integer: digit >>
            (
                {
                    let mut tmp = String::from("");
                    if sign.is_some() {
                        tmp.push_str("-");
                    }
                    tmp.push_str(&integer.to_string());
                    Token::Integer{value: tmp.parse::<i32>().unwrap()}
                }
            )
        )
    )
);

named!(real_operand<CompleteStr, Token>,
    ws!(
        do_parse!(
            tag!("#") >>
            sign: opt!(tag!("-")) >>
            left: digit >>
            tag!(".") >>
            right: digit >>
            (
                {
                    let mut tmp = String::from("");
                    if sign.is_some() {
                        tmp.push_str("-");
                    }
                    tmp.push_str(&left.to_string());
                    tmp.push_str(".");
                    tmp.push_str(&right.to_string());
                    Token::Real{value: tmp.parse::<f64>().unwrap()}
                }
            )
        )
    )
);

named!(string<CompleteStr, Token>,
    do_parse!(
        tag!("'") >>
        content: take_until!("'") >>
        tag!("'") >>
        (
            Token::DoString{ value: content.to_string() }
        )
    )
);

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

    #[test]
    fn test_parse_integer() {
        let result = integer_operand(CompleteStr("#42"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Integer { value: 42 });

        let result = integer_operand(CompleteStr("42"));
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_real() {
        let result = real_operand(CompleteStr("#4.2"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Real { value: 4.2 });

        let result = real_operand(CompleteStr("4.2"));
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_operand() {
        let result = operand(CompleteStr("#3.145"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Real { value: 3.145 });

        let result = operand(CompleteStr("#4"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(value, Token::Integer { value: 4 });
    }

    #[test]
    fn test_parse_label() {
        let result = operand(CompleteStr("@test"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(
            value,
            Token::LabelRef {
                name: "test".to_string()
            }
        );

        let result = operand(CompleteStr("test"));
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_string() {
        let result = string(CompleteStr("'hello do'"));
        assert!(result.is_ok());

        let (rest, value) = result.unwrap();
        assert_eq!(rest, CompleteStr(""));
        assert_eq!(
            value,
            Token::DoString {
                value: "hello do".to_string()
            }
        );

        let result = string(CompleteStr("'invalid"));
        assert!(result.is_err());
    }
}
<|endoftext|>


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

#[macro_use]
extern crate clap;
#[macro_use]
extern crate derive_error;
extern crate resvg;
extern crate fern;

use std::str::FromStr;
use std::fs;
use std::fmt;
use std::path;
use std::io::{
    self,
    Write,
};

use clap::{
    App,
    Arg,
    ArgMatches,
};

#[cfg(feature = "cairo-backend")]
use resvg::cairo;

use resvg::{
    log,
    svgdom,
    Backend,
    Document,
    FitTo,
    Options,
};

use svgdom::{
    ChainedErrorExt,
    WriteBuffer,
};

struct Args {
    in_svg: path::PathBuf,
    out_png: path::PathBuf,
    dump: Option<path::PathBuf>,
    pretend: bool,
    backend: Backend,
}

#[derive(Debug, Error)]
enum Error {
    Resvg(resvg::Error),
    Io(io::Error),

    #[cfg(feature = "cairo-backend")]
    Cairo(cairo::IoError),
}

fn main() {
    #[cfg(all(not(feature = "cairo-backend"), not(feature = "qt-backend")))]
    {
        eprintln!("Error: rendersvg has been build without any backend.");
        return;
    }

    if let Err(e) = process() {
        match e {
            Error::Resvg(ref e) => eprintln!("{}.", e.full_chain()),
            Error::Io(ref e) => eprintln!("Error: {}.", e),

            #[cfg(feature = "cairo-backend")]
            Error::Cairo(ref e) => eprintln!("Error: {}.", e),
        }

        std::process::exit(1);
    }
}

fn process() -> Result<(), Error> {
    fern::Dispatch::new()
        .format(log_format)
        .level(log::LevelFilter::Warn)
        .chain(std::io::stderr())
        .apply().unwrap();

    let (args, opt) = parse_args();

    // load file
    let doc = resvg::parse_doc_from_file(args.in_svg, &opt)?;

    if let Some(ref dump_path) = args.dump {
        dump_svg(&doc, dump_path)?;
    }

    if args.pretend {
        return Ok(());
    }

    match args.backend {
        #[cfg(feature = "cairo-backend")]
        Backend::Cairo => {
            let img = resvg::render_cairo::render_to_image(&doc, &opt)?;
            let mut buffer = fs::File::create(args.out_png)?;
            img.write_to_png(&mut buffer)?;
        }
        #[cfg(feature = "qt-backend")]
        Backend::Qt => {
            let img = resvg::render_qt::render_to_image(&doc, &opt)?;
            img.save(args.out_png.to_str().unwrap());
        }
    }

    Ok(())
}

fn parse_args() -> (Args, Options) {
    let app = prepare_app();
    let args = match app.get_matches_safe() {
        Ok(a) => a,
        Err(mut e) => {
            // change case before printing an error
            if e.message.starts_with("error:") {
                e.message = e.message.replace("error:", "Error:");
            }
            e.exit();
        }
    };

    (fill_args(&args), fill_options(&args))
}

fn prepare_app<'a, 'b>() -> App<'a, 'b> {
    App::new("rendersvg")
        .version(env!("CARGO_PKG_VERSION"))
        .arg(Arg::with_name("in-svg")
            .help("Input file")
            .required(true)
            .index(1)
            .validator(is_svg))
        .arg(Arg::with_name("out-png")
            .help("Output file")
            .required(true)
            .index(2)
            .validator(is_png))
        .arg(Arg::with_name("dpi")
            .long("dpi")
            .help("Sets the resolution [72..4000]")
            .value_name("DPI")
            .default_value("96")
            .validator(is_dpi))
        .arg(Arg::with_name("width")
            .short("w")
            .long("width")
            .value_name("LENGTH")
            .help("Sets the width in pixels")
            .conflicts_with_all(&["height", "zoom"])
            .validator(is_length))
        .arg(Arg::with_name("height")
            .short("h")
            .long("height")
            .value_name("LENGTH")
            .help("Sets the height in pixels")
            .conflicts_with_all(&["width", "zoom"])
            .validator(is_length))
        .arg(Arg::with_name("zoom")
            .short("z")
            .long("zoom")
            .value_name("ZOOM")
            .help("Zooms the image by a factor")
            .conflicts_with_all(&["width", "height"])
            .validator(is_zoom))
        .arg(Arg::with_name("background")
            .long("background")
            .value_name("COLOR")
            .help("Sets the background color")
            .validator(is_color))
        .arg(Arg::with_name("backend")
            .long("backend")
            .help("Sets the rendering backend.\nHas no effect if built with only one backend")
            .takes_value(true)
            .default_value(default_backend())
            .possible_values(&backends()))
        .arg(Arg::with_name("dump-svg")
            .long("dump-svg")
            .help("Saves a preprocessed SVG to the selected file")
            .value_name("PATH"))
        .arg(Arg::with_name("pretend")
            .long("pretend")
            .help("Do all the steps except rendering"))
}

fn backends() -> Vec<&'static str> {
    let mut list = Vec::new();

    #[cfg(feature = "cairo-backend")]
    {
        list.push("cairo");
    }

    #[cfg(feature = "qt-backend")]
    {
        list.push("qt");
    }

    list
}

#[allow(unreachable_code)]
fn default_backend() -> &'static str {
    #[cfg(feature = "cairo-backend")]
    {
        return "cairo"
    }

    #[cfg(feature = "qt-backend")]
    {
        return "qt"
    }

    unreachable!();
}

fn is_svg(val: String) -> Result<(), String> {
    let val = val.to_lowercase();
    if val.ends_with(".svg") || val.ends_with(".svgz") {
        Ok(())
    } else {
        Err(String::from("The input file format must be SVG(Z)."))
    }
}

fn is_png(val: String) -> Result<(), String> {
    if val.ends_with(".png") || val.ends_with(".PNG") {
        Ok(())
    } else {
        Err(String::from("The output file format must be PNG."))
    }
}

fn is_dpi(val: String) -> Result<(), String> {
    let n = match val.parse::<u32>() {
        Ok(v) => v,
        Err(e) => return Err(format!("{}", e)),
    };

    if n >= 72 && n <= 4000 {
        Ok(())
    } else {
        Err(String::from("Invalid DPI value."))
    }
}

fn is_length(val: String) -> Result<(), String> {
    let n = match val.parse::<u32>() {
        Ok(v) => v,
        Err(e) => return Err(format!("{}", e)),
    };

    if n > 0 {
        Ok(())
    } else {
        Err(String::from("Invalid length value."))
    }
}

fn is_zoom(val: String) -> Result<(), String> {
    let n = match val.parse::<f32>() {
        Ok(v) => v,
        Err(e) => return Err(format!("{}", e)),
    };

    if n > 0.0 {
        Ok(())
    } else {
        Err(String::from("Invalid zoom value."))
    }
}

fn is_color(val: String) -> Result<(), String> {
    match svgdom::Color::from_str(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err("Invalid color.".into()),
    }
}

fn fill_args(args: &ArgMatches) -> Args {
    let in_svg  = args.value_of("in-svg").unwrap().into();
    let out_png = args.value_of("out-png").unwrap().into();

    let dump = if args.is_present("dump-svg") {
        Some(args.value_of("dump-svg").unwrap().into())
    } else {
        None
    };

    let backend = match args.value_of("backend").unwrap() {
        #[cfg(feature = "cairo-backend")]
        "cairo" => Backend::Cairo,
        #[cfg(feature = "qt-backend")]
        "qt" => Backend::Qt,
        _ => unreachable!(),
    };

    Args {
        in_svg,
        out_png,
        dump,
        pretend: args.is_present("dump-svg"),
        backend,
    }
}

fn fill_options(args: &ArgMatches) -> Options {
    let mut fit_to = FitTo::Original;
    if args.is_present("width") {
        fit_to = FitTo::Width(value_t!(args.value_of("width"), u32).unwrap());
    } else if args.is_present("height") {
        fit_to = FitTo::Height(value_t!(args.value_of("height"), u32).unwrap());
    } else if args.is_present("zoom") {
        fit_to = FitTo::Zoom(value_t!(args.value_of("zoom"), f32).unwrap());
    }

    let mut background = None;
    if args.is_present("background") {
        let s = args.value_of("background").unwrap();
        background = Some(svgdom::Color::from_str(s).unwrap());
    }

    Options {
        path: Some(args.value_of("in-svg").unwrap().into()),
        dpi: value_t!(args.value_of("dpi"), u16).unwrap() as f64,
        fit_to,
        background,
    }
}

fn dump_svg(doc: &Document, path: &path::Path) -> Result<(), io::Error> {
    let mut f = fs::File::create(path)?;

    let opt = svgdom::WriteOptions {
        indent: svgdom::Indent::Spaces(2),
        attributes_indent: svgdom::Indent::Spaces(3),
        write_hidden_attributes: true,
        attributes_order: svgdom::AttributesOrder::Specification,
        .. svgdom::WriteOptions::default()
    };

    let svgdoc = doc.to_svgdom();

    let mut out = Vec::new();
    svgdoc.write_buf_opt(&opt, &mut out);
    f.write_all(&out)?;

    Ok(())
}

fn log_format(out: fern::FormatCallback, message: &fmt::Arguments, record: &log::Record) {
    let lvl = match record.level() {
        log::Level::Error => "Error",
        log::Level::Warn => "Warning",
        log::Level::Info => "Info",
        log::Level::Debug => "Debug",
        log::Level::Trace => "Trace",
    };

    out.finish(format_args!(
        "{} (in {}:{}): {}",
        lvl,
        record.target(),
        record.line().unwrap_or(0),
        message
    ))
}
<commit_msg>Fixed CLI args parsing.<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 http://mozilla.org/MPL/2.0/.

#[macro_use]
extern crate clap;
#[macro_use]
extern crate derive_error;
extern crate resvg;
extern crate fern;

use std::str::FromStr;
use std::fs;
use std::fmt;
use std::path;
use std::io::{
    self,
    Write,
};

use clap::{
    App,
    Arg,
    ArgMatches,
};

#[cfg(feature = "cairo-backend")]
use resvg::cairo;

use resvg::{
    log,
    svgdom,
    Backend,
    Document,
    FitTo,
    Options,
};

use svgdom::{
    ChainedErrorExt,
    WriteBuffer,
};

struct Args {
    in_svg: path::PathBuf,
    out_png: path::PathBuf,
    dump: Option<path::PathBuf>,
    pretend: bool,
    backend: Backend,
}

#[derive(Debug, Error)]
enum Error {
    Resvg(resvg::Error),
    Io(io::Error),

    #[cfg(feature = "cairo-backend")]
    Cairo(cairo::IoError),
}

fn main() {
    #[cfg(all(not(feature = "cairo-backend"), not(feature = "qt-backend")))]
    {
        eprintln!("Error: rendersvg has been build without any backend.");
        return;
    }

    if let Err(e) = process() {
        match e {
            Error::Resvg(ref e) => eprintln!("{}.", e.full_chain()),
            Error::Io(ref e) => eprintln!("Error: {}.", e),

            #[cfg(feature = "cairo-backend")]
            Error::Cairo(ref e) => eprintln!("Error: {}.", e),
        }

        std::process::exit(1);
    }
}

fn process() -> Result<(), Error> {
    fern::Dispatch::new()
        .format(log_format)
        .level(log::LevelFilter::Warn)
        .chain(std::io::stderr())
        .apply().unwrap();

    let (args, opt) = parse_args();

    // load file
    let doc = resvg::parse_doc_from_file(args.in_svg, &opt)?;

    if let Some(ref dump_path) = args.dump {
        dump_svg(&doc, dump_path)?;
    }

    if args.pretend {
        return Ok(());
    }

    match args.backend {
        #[cfg(feature = "cairo-backend")]
        Backend::Cairo => {
            let img = resvg::render_cairo::render_to_image(&doc, &opt)?;
            let mut buffer = fs::File::create(args.out_png)?;
            img.write_to_png(&mut buffer)?;
        }
        #[cfg(feature = "qt-backend")]
        Backend::Qt => {
            let img = resvg::render_qt::render_to_image(&doc, &opt)?;
            img.save(args.out_png.to_str().unwrap());
        }
    }

    Ok(())
}

fn parse_args() -> (Args, Options) {
    let app = prepare_app();
    let args = match app.get_matches_safe() {
        Ok(a) => a,
        Err(mut e) => {
            // change case before printing an error
            if e.message.starts_with("error:") {
                e.message = e.message.replace("error:", "Error:");
            }
            e.exit();
        }
    };

    (fill_args(&args), fill_options(&args))
}

fn prepare_app<'a, 'b>() -> App<'a, 'b> {
    App::new("rendersvg")
        .version(env!("CARGO_PKG_VERSION"))
        .arg(Arg::with_name("in-svg")
            .help("Input file")
            .required(true)
            .index(1)
            .validator(is_svg))
        .arg(Arg::with_name("out-png")
            .help("Output file")
            .required(true)
            .index(2)
            .validator(is_png))
        .arg(Arg::with_name("dpi")
            .long("dpi")
            .help("Sets the resolution [72..4000]")
            .value_name("DPI")
            .default_value("96")
            .validator(is_dpi))
        .arg(Arg::with_name("width")
            .short("w")
            .long("width")
            .value_name("LENGTH")
            .help("Sets the width in pixels")
            .conflicts_with_all(&["height", "zoom"])
            .validator(is_length))
        .arg(Arg::with_name("height")
            .short("h")
            .long("height")
            .value_name("LENGTH")
            .help("Sets the height in pixels")
            .conflicts_with_all(&["width", "zoom"])
            .validator(is_length))
        .arg(Arg::with_name("zoom")
            .short("z")
            .long("zoom")
            .value_name("ZOOM")
            .help("Zooms the image by a factor")
            .conflicts_with_all(&["width", "height"])
            .validator(is_zoom))
        .arg(Arg::with_name("background")
            .long("background")
            .value_name("COLOR")
            .help("Sets the background color")
            .validator(is_color))
        .arg(Arg::with_name("backend")
            .long("backend")
            .help("Sets the rendering backend.\nHas no effect if built with only one backend")
            .takes_value(true)
            .default_value(default_backend())
            .possible_values(&backends()))
        .arg(Arg::with_name("dump-svg")
            .long("dump-svg")
            .help("Saves a preprocessed SVG to the selected file")
            .value_name("PATH"))
        .arg(Arg::with_name("pretend")
            .long("pretend")
            .help("Do all the steps except rendering"))
}

fn backends() -> Vec<&'static str> {
    let mut list = Vec::new();

    #[cfg(feature = "cairo-backend")]
    {
        list.push("cairo");
    }

    #[cfg(feature = "qt-backend")]
    {
        list.push("qt");
    }

    list
}

#[allow(unreachable_code)]
fn default_backend() -> &'static str {
    #[cfg(feature = "cairo-backend")]
    {
        return "cairo"
    }

    #[cfg(feature = "qt-backend")]
    {
        return "qt"
    }

    unreachable!();
}

fn is_svg(val: String) -> Result<(), String> {
    let val = val.to_lowercase();
    if val.ends_with(".svg") || val.ends_with(".svgz") {
        Ok(())
    } else {
        Err(String::from("The input file format must be SVG(Z)."))
    }
}

fn is_png(val: String) -> Result<(), String> {
    if val.ends_with(".png") || val.ends_with(".PNG") {
        Ok(())
    } else {
        Err(String::from("The output file format must be PNG."))
    }
}

fn is_dpi(val: String) -> Result<(), String> {
    let n = match val.parse::<u32>() {
        Ok(v) => v,
        Err(e) => return Err(format!("{}", e)),
    };

    if n >= 72 && n <= 4000 {
        Ok(())
    } else {
        Err(String::from("Invalid DPI value."))
    }
}

fn is_length(val: String) -> Result<(), String> {
    let n = match val.parse::<u32>() {
        Ok(v) => v,
        Err(e) => return Err(format!("{}", e)),
    };

    if n > 0 {
        Ok(())
    } else {
        Err(String::from("Invalid length value."))
    }
}

fn is_zoom(val: String) -> Result<(), String> {
    let n = match val.parse::<f32>() {
        Ok(v) => v,
        Err(e) => return Err(format!("{}", e)),
    };

    if n > 0.0 {
        Ok(())
    } else {
        Err(String::from("Invalid zoom value."))
    }
}

fn is_color(val: String) -> Result<(), String> {
    match svgdom::Color::from_str(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err("Invalid color.".into()),
    }
}

fn fill_args(args: &ArgMatches) -> Args {
    let in_svg  = args.value_of("in-svg").unwrap().into();
    let out_png = args.value_of("out-png").unwrap().into();

    let dump = if args.is_present("dump-svg") {
        Some(args.value_of("dump-svg").unwrap().into())
    } else {
        None
    };

    let backend = match args.value_of("backend").unwrap() {
        #[cfg(feature = "cairo-backend")]
        "cairo" => Backend::Cairo,
        #[cfg(feature = "qt-backend")]
        "qt" => Backend::Qt,
        _ => unreachable!(),
    };

    Args {
        in_svg,
        out_png,
        dump,
        pretend: args.is_present("pretend"),
        backend,
    }
}

fn fill_options(args: &ArgMatches) -> Options {
    let mut fit_to = FitTo::Original;
    if args.is_present("width") {
        fit_to = FitTo::Width(value_t!(args.value_of("width"), u32).unwrap());
    } else if args.is_present("height") {
        fit_to = FitTo::Height(value_t!(args.value_of("height"), u32).unwrap());
    } else if args.is_present("zoom") {
        fit_to = FitTo::Zoom(value_t!(args.value_of("zoom"), f32).unwrap());
    }

    let mut background = None;
    if args.is_present("background") {
        let s = args.value_of("background").unwrap();
        background = Some(svgdom::Color::from_str(s).unwrap());
    }

    Options {
        path: Some(args.value_of("in-svg").unwrap().into()),
        dpi: value_t!(args.value_of("dpi"), u16).unwrap() as f64,
        fit_to,
        background,
    }
}

fn dump_svg(doc: &Document, path: &path::Path) -> Result<(), io::Error> {
    let mut f = fs::File::create(path)?;

    let opt = svgdom::WriteOptions {
        indent: svgdom::Indent::Spaces(2),
        attributes_indent: svgdom::Indent::Spaces(3),
        write_hidden_attributes: true,
        attributes_order: svgdom::AttributesOrder::Specification,
        .. svgdom::WriteOptions::default()
    };

    let svgdoc = doc.to_svgdom();

    let mut out = Vec::new();
    svgdoc.write_buf_opt(&opt, &mut out);
    f.write_all(&out)?;

    Ok(())
}

fn log_format(out: fern::FormatCallback, message: &fmt::Arguments, record: &log::Record) {
    let lvl = match record.level() {
        log::Level::Error => "Error",
        log::Level::Warn => "Warning",
        log::Level::Info => "Info",
        log::Level::Debug => "Debug",
        log::Level::Trace => "Trace",
    };

    out.finish(format_args!(
        "{} (in {}:{}): {}",
        lvl,
        record.target(),
        record.line().unwrap_or(0),
        message
    ))
}
<|endoftext|>


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

use syntax::{ast, ast_util};
use syntax::ast::Public;
use syntax::attr::AttrMetaMethods;
use rustc::lint::{Context, LintPass, LintArray};

declare_lint!(PRIVATIZE, Deny,
              "Allows to enforce private fields for struct definitions");

/// Lint for keeping DOM fields private
///
/// This lint (disable with `-A privatize`/`#[allow(privatize)]`) ensures all types marked with `#[privatize]`
/// have no private fields
pub struct PrivatizePass;

impl LintPass for PrivatizePass {
    fn get_lints(&self) -> LintArray {
        lint_array!(PRIVATIZE)
    }

    fn check_struct_def(&mut self,
                        cx: &Context,
                        def: &ast::StructDef,
                        _i: ast::Ident,
                        _gen: &ast::Generics,
                        id: ast::NodeId) {
        if cx.tcx.has_attr(ast_util::local_def(id), "privatize") {
            for field in def.fields.iter() {
                match field.node {
                    ast::StructField_ { kind: ast::NamedField(ident, visibility), .. } if visibility == Public => {
                        cx.span_lint(PRIVATIZE, field.span,
                                     &format!("Field {} is public where only private fields are allowed",
                                              ident.name));
                    }
                    _ => {}
                }
            }
        }
    }
}
<commit_msg>Fix minor typo in lint docstring.<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 http://mozilla.org/MPL/2.0/. */

use syntax::{ast, ast_util};
use syntax::ast::Public;
use syntax::attr::AttrMetaMethods;
use rustc::lint::{Context, LintPass, LintArray};

declare_lint!(PRIVATIZE, Deny,
              "Allows to enforce private fields for struct definitions");

/// Lint for keeping DOM fields private
///
/// This lint (disable with `-A privatize`/`#[allow(privatize)]`) ensures all types marked with `#[privatize]`
/// have no public fields
pub struct PrivatizePass;

impl LintPass for PrivatizePass {
    fn get_lints(&self) -> LintArray {
        lint_array!(PRIVATIZE)
    }

    fn check_struct_def(&mut self,
                        cx: &Context,
                        def: &ast::StructDef,
                        _i: ast::Ident,
                        _gen: &ast::Generics,
                        id: ast::NodeId) {
        if cx.tcx.has_attr(ast_util::local_def(id), "privatize") {
            for field in def.fields.iter() {
                match field.node {
                    ast::StructField_ { kind: ast::NamedField(ident, visibility), .. } if visibility == Public => {
                        cx.span_lint(PRIVATIZE, field.span,
                                     &format!("Field {} is public where only private fields are allowed",
                                              ident.name));
                    }
                    _ => {}
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Implementation of a simple uTP client and server.
#![feature(collections,convert,os)]

extern crate utp;

macro_rules! iotry {
    ($e:expr) => (match $e { Ok(v) => v, Err(e) => panic!("{}", e), })
}

fn usage() {
    println!("Usage: utp [-s|-c] <address> <port>");
}

fn main() {
    use utp::UtpStream;
    use std::io::{stdin, stdout, stderr, Read, Write};

    enum Mode {
        Server,
        Client
    }

    let args = std::os::args();
    let mut args = args.iter().map(|arg| &arg[..]);

    // Skip program name
    args.next();

    let mode = match args.next() {
        Some("-s") => Mode::Server,
        Some("-c") => Mode::Client,
        _ => { usage(); return; }
    };

    let addr = match (args.next(), args.next()) {
        (None, None) => String::from_str("127.0.0.1:8080"),
        (Some(ip), Some(port)) => format!("{}:{}", ip, port),
        _ => { usage(); return; }
    };
    let addr: &str = addr.as_ref();

    match mode {
        Mode::Server => {
            let mut stream = iotry!(UtpStream::bind(addr));
            let mut writer = stdout();
            let _ = writeln!(&mut stderr(), "Serving on {}", addr);

            let mut payload = vec!();
            iotry!(stream.read_to_end(&mut payload));
            iotry!(writer.write(&payload[..]));
        }
        Mode::Client => {
            let mut stream = iotry!(UtpStream::connect(addr));
            let mut reader = stdin();

            let mut payload = vec!();
            iotry!(reader.read_to_end(&mut payload));
            iotry!(stream.write(&payload[..]));
            iotry!(stream.close());
            drop(stream);
        }
    }
}
<commit_msg>Use reasonable buffers in utpcat example.<commit_after>//! Implementation of a simple uTP client and server.
#![feature(collections,convert,os)]

extern crate utp;

macro_rules! iotry {
    ($e:expr) => (match $e { Ok(v) => v, Err(e) => panic!("{}", e), })
}

fn usage() {
    println!("Usage: utp [-s|-c] <address> <port>");
}

fn main() {
    use utp::UtpStream;
    use std::io::{stdin, stdout, stderr, Read, Write};

    enum Mode {
        Server,
        Client
    }

    let args = std::os::args();
    let mut args = args.iter().map(|arg| &arg[..]);

    // Skip program name
    args.next();

    let mode = match args.next() {
        Some("-s") => Mode::Server,
        Some("-c") => Mode::Client,
        _ => { usage(); return; }
    };

    let addr = match (args.next(), args.next()) {
        (None, None) => String::from_str("127.0.0.1:8080"),
        (Some(ip), Some(port)) => format!("{}:{}", ip, port),
        _ => { usage(); return; }
    };
    let addr: &str = addr.as_ref();

    match mode {
        Mode::Server => {
            let mut stream = iotry!(UtpStream::bind(addr));
            let mut writer = stdout();
            let _ = writeln!(&mut stderr(), "Serving on {}", addr);

            let mut payload = vec![0; 1024 * 1024];
            loop {
                match stream.read(&mut payload) {
                    Ok(0) => break,
                    Ok(read) => iotry!(writer.write(&payload[..read])),
                    Err(e) => panic!("{}", e)
                };
            }
        }
        Mode::Client => {
            let mut stream = iotry!(UtpStream::connect(addr));
            let mut reader = stdin();

            let mut payload = vec![0; 1024 * 1024];
            loop {
                match reader.read(&mut payload) {
                    Ok(0) => break,
                    Ok(read) => iotry!(stream.write(&payload[..read])),
                    Err(e) => {
                        iotry!(stream.close());
                        panic!("{:?}", e);
                    }
                };
            }
            iotry!(stream.close());
        }
    }
}
<|endoftext|>