<commit_before>use std::old_io::Buffer;

use dom::{Document, Element, Text, Node, RcNode_new, RcElement, RcElement_new};
use error::BuildError;

use xml::common::XmlVersion;
use xml::reader::EventReader;
use xml::reader::events::XmlEvent;

pub fn build<B: Buffer>(reader: &mut EventReader<B>) -> Result<Document, BuildError> {
    let mut root: Option<RcElement> = None;
    let mut curr: Option<RcElement> = None;
    let mut doc_version: Option<XmlVersion> = None;
    let mut doc_encoding: Option<String> = None;

    for event in reader.events() {
        match event {
            // StartDocument
            XmlEvent::StartDocument { version, encoding, standalone: _ } => {
                doc_version = Some(version);
                doc_encoding = Some(encoding);
            }
            // EndDocument
            XmlEvent::EndDocument => {
                // TODO
            }
            // StartElement
            XmlEvent::StartElement { name, attributes, namespace } => {
                // Check if a root exists.
                match root {
                    // If it does not, it must be created.
                    // Also sets the current element to the root.
                    None => {
                        let elem = Element::new_root(name, attributes, namespace);
                        let rcelem = RcElement_new(elem);
                        root = Some(rcelem.clone());
                        curr = Some(rcelem.clone());
                    }
                    // If it does, the new element will be appended,
                    // to the current element.
                    Some (_) => {
                        match curr {
                            // this should never happen
                            None => return Err(BuildError::BuildError),
                            Some(parent) => {
                                // create the element with the parent as a weak reference
                                let elem = Element::new(parent.clone().downgrade(), name, attributes, namespace);
                                let rcelem = RcElement_new(elem);
                                // create a node from the element
                                let node = Node::Element(rcelem.clone());
                                let rcnode = RcNode_new(node);
                                // add the element to the parent
                                parent.borrow_mut().add_child(rcnode);
                                // move into the new element
                                curr = Some(rcelem.clone());
                            }
                        }
                    }
                }
            }
            // EndElement
            XmlEvent::EndElement { name: _ } => {
                match curr {
                    // this should never happen
                    None => return Err(BuildError::BuildError),
                    Some(elem) => {
                        // move out of the element by setting the current
                        // element to the parent of the element we're
                        // exiting
                        match elem.borrow().parent {
                            // current has no parent, so it's probably root
                            None => {
                                // check if root is valid
                                match root {
                                    None => return Err(BuildError::BuildError),
                                    Some(ref tmp_root) => {
                                        curr = Some(tmp_root.clone());
                                    }
                                }
                            }
                            Some(ref parent) => {
                                // TODO unwrap
                                curr = Some(parent.clone().upgrade().unwrap());
                            }
                        }
                    }
                }
            },
            // Cdata or Characters
            XmlEvent::CData(content) | XmlEvent::Characters(content) => {
                match curr {
                    // this should never happen
                    None => return Err(BuildError::BuildError),
                    Some(ref parent) => {
                        // create the text node with the parent as a weak reference
                        let text = Text::new(parent.clone().downgrade(), content);
                        let node = Node::Text(text);
                        let rcnode = RcNode_new(node);
                        // add the text node to the parent
                        parent.borrow_mut().add_child(rcnode.clone());
                    }
                }
            },
            // Error
            XmlEvent::Error(err) => {
                return Err(BuildError::ParserError(err));
            }
            // Ignore other events.
            _ => {}
        }
    }

    Ok(Document {
        version: doc_version,
        encoding: doc_encoding,
        root: root.unwrap().clone(),
    })
}
<commit_msg>EndDocument can be ignored.<commit_after>use std::old_io::Buffer;

use dom::{Document, Element, Text, Node, RcNode_new, RcElement, RcElement_new};
use error::BuildError;

use xml::common::XmlVersion;
use xml::reader::EventReader;
use xml::reader::events::XmlEvent;

pub fn build<B: Buffer>(reader: &mut EventReader<B>) -> Result<Document, BuildError> {
    let mut root: Option<RcElement> = None;
    let mut curr: Option<RcElement> = None;
    let mut doc_version: Option<XmlVersion> = None;
    let mut doc_encoding: Option<String> = None;

    for event in reader.events() {
        match event {
            // StartDocument
            XmlEvent::StartDocument { version, encoding, standalone: _ } => {
                doc_version = Some(version);
                doc_encoding = Some(encoding);
            }
            // StartElement
            XmlEvent::StartElement { name, attributes, namespace } => {
                // Check if a root exists.
                match root {
                    // If it does not, it must be created.
                    // Also sets the current element to the root.
                    None => {
                        let elem = Element::new_root(name, attributes, namespace);
                        let rcelem = RcElement_new(elem);
                        root = Some(rcelem.clone());
                        curr = Some(rcelem.clone());
                    }
                    // If it does, the new element will be appended,
                    // to the current element.
                    Some (_) => {
                        match curr {
                            // this should never happen
                            None => return Err(BuildError::BuildError),
                            Some(parent) => {
                                // create the element with the parent as a weak reference
                                let elem = Element::new(parent.clone().downgrade(), name, attributes, namespace);
                                let rcelem = RcElement_new(elem);
                                // create a node from the element
                                let node = Node::Element(rcelem.clone());
                                let rcnode = RcNode_new(node);
                                // add the element to the parent
                                parent.borrow_mut().add_child(rcnode);
                                // move into the new element
                                curr = Some(rcelem.clone());
                            }
                        }
                    }
                }
            }
            // EndElement
            XmlEvent::EndElement { name: _ } => {
                match curr {
                    // this should never happen
                    None => return Err(BuildError::BuildError),
                    Some(elem) => {
                        // move out of the element by setting the current
                        // element to the parent of the element we're
                        // exiting
                        match elem.borrow().parent {
                            // current has no parent, so it's probably root
                            None => {
                                // check if root is valid
                                match root {
                                    None => return Err(BuildError::BuildError),
                                    Some(ref tmp_root) => {
                                        curr = Some(tmp_root.clone());
                                    }
                                }
                            }
                            Some(ref parent) => {
                                // TODO unwrap
                                curr = Some(parent.clone().upgrade().unwrap());
                            }
                        }
                    }
                }
            },
            // Cdata or Characters
            XmlEvent::CData(content) | XmlEvent::Characters(content) => {
                match curr {
                    // this should never happen
                    None => return Err(BuildError::BuildError),
                    Some(ref parent) => {
                        // create the text node with the parent as a weak reference
                        let text = Text::new(parent.clone().downgrade(), content);
                        let node = Node::Text(text);
                        let rcnode = RcNode_new(node);
                        // add the text node to the parent
                        parent.borrow_mut().add_child(rcnode.clone());
                    }
                }
            },
            // Error
            XmlEvent::Error(err) => {
                return Err(BuildError::ParserError(err));
            }
            // Ignore other events.
            _ => {}
        }
    }

    Ok(Document {
        version: doc_version,
        encoding: doc_encoding,
        root: root.unwrap().clone(),
    })
}
<|endoftext|>


<commit_before>use slack_api as slack;
use tokio_tungstenite as tungstenite;

use futures_util::{SinkExt, StreamExt};

use tokio::sync::mpsc;

use atomic_counter::RelaxedCounter;

use std::sync::Arc;
use std::env;
use std::path::Path;
use std::result::Result;
use std::thread;

use chrono::prelude::{Utc, DateTime};

use karmator_rust::database;
use karmator_rust::message;
use karmator_rust::cache;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Shared integer counter for message ids
    let msg_id = Arc::new(RelaxedCounter::new(0));

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    let filename = env::var("SQLITE_FILE").map_err(|_| "SQLITE_FILE env var must be set")?;
    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // System Cache manager
    let cache = cache::Cache::new(&token, client.clone());

    // Work to establish the WS connection
    let response = slack::rtm::connect(&client, &token).await.map_err(|e| format!("Control - {:?}", e))?;
    println!("Control - Got an ok reply");

    let ws_url = response.url.ok_or(format!("Control - \tNo Ws url"))?;
    println!("Control - \tGot a WS url: {:?}", ws_url);

    let (ws_stream, _) = tungstenite::connect_async(ws_url).await.map_err(|e| format!("Control - \t\t{:?}", e))?;
    println!("Control - \t\tWS connection established");

    // Setup the mpsc channel
    let (tx, mut rx) = mpsc::channel(32);

    // Split the stream
    let (mut ws_write, mut ws_read) = ws_stream.split();

    // Setup the mpsc channel for sqlite and spawn the sqlite worker thread
    // TODO: define a data format for asking the db to query something on your behalf, for now
    // pass in the raw query string?
    let (sql_tx, sql_rx) = mpsc::channel(32);

    let sql_worker = thread::spawn(move || {
        println!("Sql Worker - Launching");
        // TODO: check result
        let _ = database::process_queries(Path::new(&filename), sql_rx);
        println!("Sql Worker - Exiting");
    });

    // Spawn the inbound ws stream processor
    let inbound = tokio::spawn(async move {
        // TODO: we may have some ordering funnyness if we spawn a processor per
        // inbound message, ponder doing one processor per channel so its consistent
        // order per channel, if want to be intelligent about it, have a timeout, if a
        // channel has been quiet long enough, go ahead and shut down the channel processor.
        //
        // TODO: this could explode if the outstream or database get backed up it will just
        // spawn more and more inbound message, not good.
        loop {
            if let Some(Ok(ws_msg)) = ws_read.next().await {
                let msg_id = msg_id.clone();
                let tx2 = tx.clone();
                let sql_tx2 = sql_tx.clone();
                let cache = cache.clone();

                tokio::spawn(async move {
                    // TODO: check result
                    let _ = message::process_inbound_message(
                        msg_id,
                        ws_msg,
                        tx2,
                        sql_tx2,
                        start_time,
                        cache,
                    ).await;
                });

            }
        }
    });

    // Spawn outbound ws processor
    // TODO: need to make sure to wait on sending till we recieve the hello event
    // could be a oneshot or some sort of flag which it waits till inbound has
    // gotten the hello
    let outbound = tokio::spawn(async move {
        loop {
            while let Some(message) = rx.recv().await {
                println!("Outbound - {:?}", message);

                // TODO: present some way to do plain vs fancy message, and if
                // fancy do a webapi post, otherwise dump into the WS
                //
                // TODO: look into tracking the sent message with a confirmation
                // that it was sent (via msg id) and if it fails, resend with
                // backoff
                //
                // TODO: find a way to handle the ping/pong cycle and monitor
                // TODO: check result
                let _ = ws_write.send(message).await;
            }
        }
    });

    // Wait till either exits (error) then begin recovery
    match tokio::try_join!(inbound, outbound) {
        Ok((_first, _second)) => println!("Control - \t\t\tBoth exited fine"),
        Err(err) => println!("Control - \t\t\tSomething failed: {:?}", err),
    }

    // The sql_tx got moved into the inbound tokio async, so when that dies....
    let res = sql_worker.join();
    println!("Control - \t\t\tSql worker: {:?}", res);

    Ok(())
}
<commit_msg>Switched from two spawned workers to one select loop, should be able to recover a bit better<commit_after>use slack_api as slack;
use tokio_tungstenite as tungstenite;

use futures_util::{SinkExt, StreamExt};

use tokio::sync::mpsc;

use atomic_counter::RelaxedCounter;

use std::sync::Arc;
use std::env;
use std::path::Path;
use std::result::Result;
use std::thread;

use chrono::prelude::{Utc, DateTime};

use karmator_rust::database;
use karmator_rust::message;
use karmator_rust::cache;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let filename = env::var("SQLITE_FILE").map_err(|_| "SQLITE_FILE env var must be set")?;
    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // Shared integer counter for message ids
    let msg_id = Arc::new(RelaxedCounter::new(0));

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    // System Cache manager
    let cache = cache::Cache::new(&token, client.clone());

    // Message Tx/Rx from the message loop to the downstream handlers
    let (tx, mut rx) = mpsc::channel(32);

    // Sql request/reply channels for the downstream handlers to talk to the
    // sqlite worker thread
    let (sql_tx, sql_rx) = mpsc::channel(32);

    // Launch the sqlite worker thread
    let sql_worker = thread::Builder::new().name("sqlite_worker".into()).spawn(move || {
        println!("Sql Worker - Launching");

        let res = database::process_queries(Path::new(&filename), sql_rx);
        println!("Sql Worker - {:?}", res);

        // TODO: Worker died, let's make sure everything else goes down as well.
        println!("Sql Worker - Exiting");
    })?;

    // TODO: figure out how to re-establish an connection upon connection loss or a 'good bye' from
    // slack
    //
    // I think the steps should be:
    // 1. loop start
    // 2. establish an WS connection
    // 3. select!(get/send)
    // 4. do some processing in 'get' for system control messages (another layer for message
    //    control)
    // 5. if system control get/connection lost, exit, causes the select! to end, and the loop
    //    again

    // Work to establish the WS connection
    let response = slack::rtm::connect(&client, &token).await.map_err(|e| format!("Control - {:?}", e))?;
    println!("Control - Got an ok reply");

    let ws_url = response.url.ok_or(format!("Control - \tNo Ws url"))?;
    println!("Control - \tGot a WS url: {:?}", ws_url);

    let (ws_stream, _) = tungstenite::connect_async(ws_url).await.map_err(|e| format!("Control - \t\t{:?}", e))?;
    println!("Control - \t\tWS connection established");

    // Split the stream
    let (mut ws_write, mut ws_read) = ws_stream.split();

    loop {
        // TODO: have each branch return a Ok or Err (or w/e) to indicate all ok or shit is bad
        // then if shit is bad go to recovery loop, and if its irrevociable, exit loop and
        // rejoin/terminate the sql worker if its not already terminated
        //
        // TODO: nested loop, one for the connection setup/teardown, then one for the inner select
        // loop

        tokio::select! {
            Some(Ok(ws_msg)) = ws_read.next() => {
                // TODO: we may have some ordering funnyness if we spawn a processor per
                // inbound message, ponder doing one processor per channel so its consistent
                // order per channel, if want to be intelligent about it, have a timeout, if a
                // channel has been quiet long enough, go ahead and shut down the channel processor.
                //
                // TODO: this could explode if the outstream or database get backed up it will just
                // spawn more and more inbound message, not good.
                let msg_id = msg_id.clone();
                let tx2 = tx.clone();
                let sql_tx2 = sql_tx.clone();
                let cache = cache.clone();

                tokio::spawn(async move {
                    // TODO: check result
                    let _ = message::process_inbound_message(
                        msg_id,
                        ws_msg,
                        tx2,
                        sql_tx2,
                        start_time,
                        cache,
                    ).await;
                });
            },

            Some(message) = rx.recv() => {
                // TODO: need to make sure to wait on sending till we recieve the hello event
                // could be a oneshot or some sort of flag which it waits till inbound has
                // gotten the hello
                println!("Outbound - {:?}", message);

                // TODO: present some way to do plain vs fancy message, and if
                // fancy do a webapi post, otherwise dump into the WS
                //
                // TODO: look into tracking the sent message with a confirmation
                // that it was sent (via msg id) and if it fails, resend with
                // backoff
                //
                // TODO: find a way to handle the ping/pong cycle and monitor
                // TODO: check result
                let _ = ws_write.send(message).await;
            },
        }
    }

    // The sql_tx got moved into the inbound tokio async, so when that dies....
    // Its now not moved into a inbound tokio async, should be able to resume
    let res = sql_worker.join();
    println!("Control - \t\t\tSql worker: {:?}", res);

    Ok(())
}
<|endoftext|>


<commit_before>use std::ptr::null_mut;

use gdk_pixbuf_sys::GdkPixbuf;
use gdk_pixbuf_sys::{
    GdkPixbufFormat, GdkPixbufModule, GdkPixbufModulePattern, GdkPixbufModulePreparedFunc,
    GdkPixbufModuleSizeFunc, GdkPixbufModuleUpdatedFunc, GDK_PIXBUF_FORMAT_SCALABLE,
    GDK_PIXBUF_FORMAT_THREADSAFE,
};

use glib::translate::IntoGlib;
use glib::translate::ToGlibPtr;
use glib_sys::{gboolean, GError};
use gobject_sys::GObject;
use libc::{c_char, c_int, c_uint};

use gio::prelude::MemoryInputStreamExt;
use gio::MemoryInputStream;
use glib::Bytes;
use librsvg::Loader;

use cstr::cstr;
use librsvg::rsvg_convert_only::LegacySize;

struct SvgContext {
    size_func: GdkPixbufModuleSizeFunc,
    prep_func: GdkPixbufModulePreparedFunc,
    update_func: GdkPixbufModuleUpdatedFunc,
    user_data: glib_sys::gpointer,
    stream: MemoryInputStream,
}

#[no_mangle]
unsafe extern "C" fn begin_load(
    size_func: GdkPixbufModuleSizeFunc,
    prep_func: GdkPixbufModulePreparedFunc,
    update_func: GdkPixbufModuleUpdatedFunc,
    user_data: glib_sys::gpointer,
    error: *mut *mut GError,
) -> glib_sys::gpointer {
    if error != null_mut() {
        *error = null_mut();
    }

    let stream = MemoryInputStream::new();
    let ctx = Box::new(SvgContext {
        size_func,
        prep_func,
        update_func,
        user_data,
        stream,
    });

    Box::into_raw(ctx) as glib_sys::gpointer
}

#[no_mangle]
unsafe extern "C" fn load_increment(
    user_data: glib_sys::gpointer,
    buffer: *const u8,
    size: c_uint,
    error: *mut *mut GError,
) -> gboolean {
    if error != null_mut() {
        *error = null_mut();
    }

    let ctx = user_data as *mut SvgContext;

    let data = std::slice::from_raw_parts(buffer, size as usize);
    (&*ctx).stream.add_bytes(&Bytes::from(data));
    true.into_glib()
}

#[no_mangle]
unsafe extern "C" fn stop_load(user_data: glib_sys::gpointer, error: *mut *mut GError) -> gboolean {
    let ctx = Box::from_raw(user_data as *mut SvgContext);
    if error != null_mut() {
        *error = null_mut();
    }

    fn _inner_stop_load(ctx: &Box<SvgContext>) -> Result<gdk_pixbuf::Pixbuf, String> {
        let handle = Loader::new()
            .read_stream::<_, gio::File, gio::Cancellable>(&ctx.stream, None, None)
            .map_err(|e| e.to_string())?;

        let renderer = librsvg::CairoRenderer::new(&handle);
        let (w, h) = renderer.legacy_document_size().map_err(|e| e.to_string())?;
        let mut w = w.ceil() as c_int;
        let mut h = h.ceil() as c_int;

        if let Some(size_func) = ctx.size_func {
            let mut tmp_w: c_int = w;
            let mut tmp_h: c_int = h;
            unsafe {
                size_func(
                    &mut tmp_w as *mut c_int,
                    &mut tmp_h as *mut c_int,
                    ctx.user_data,
                )
            };
            if tmp_w != 0 && tmp_h != 0 {
                w = tmp_w;
                h = tmp_h;
            }
        }

        let pb = librsvg::c_api::pixbuf_utils::render_to_pixbuf_at_size(
            &renderer, w as f64, h as f64, w as f64, h as f64,
        )
        .map_err(|e| e.to_string())?;

        Ok(pb)
    }

    let pixbuf = match _inner_stop_load(&ctx) {
        Ok(r) => r,
        Err(e) => {
            if error != null_mut() {
                let gerr = glib::Error::new(gdk_pixbuf::PixbufError::Failed, &e);
                *error = gerr.to_glib_full() as *mut GError;
            }
            return false.into_glib();
        }
    };

    let w = pixbuf.width();
    let h = pixbuf.height();
    let pixbuf: *mut GdkPixbuf = pixbuf.to_glib_full();

    if let Some(prep_func) = ctx.prep_func {
        prep_func(pixbuf, null_mut(), ctx.user_data);
    }
    if let Some(update_func) = ctx.update_func {
        update_func(pixbuf, 0, 0, w, h, ctx.user_data);
    }

    // The module loader increases a ref so we drop the pixbuf here
    gobject_sys::g_object_unref(pixbuf as *mut GObject);

    true.into_glib()
}

#[no_mangle]
extern "C" fn fill_vtable(module: &mut GdkPixbufModule) {
    module.begin_load = Some(begin_load);
    module.stop_load = Some(stop_load);
    module.load_increment = Some(load_increment);
}

const SIGNATURE: [GdkPixbufModulePattern; 3] = [
    GdkPixbufModulePattern {
        prefix: cstr!(" <svg").as_ptr() as *mut c_char,
        mask: cstr!("*    ").as_ptr() as *mut c_char,
        relevance: 100,
    },
    GdkPixbufModulePattern {
        prefix: cstr!(" <!DOCTYPE svg").as_ptr() as *mut c_char,
        mask: cstr!("*             ").as_ptr() as *mut c_char,
        relevance: 100,
    },
    GdkPixbufModulePattern {
        prefix: std::ptr::null_mut(),
        mask: std::ptr::null_mut(),
        relevance: 0,
    },
];

const MIME_TYPES: [*const c_char; 7] = [
    cstr!("image/svg+xml").as_ptr(),
    cstr!("image/svg").as_ptr(),
    cstr!("image/svg-xml").as_ptr(),
    cstr!("image/vnd.adobe.svg+xml").as_ptr(),
    cstr!("text/xml-svg").as_ptr(),
    cstr!("image/svg+xml-compressed").as_ptr(),
    std::ptr::null(),
];

const EXTENSIONS: [*const c_char; 4] = [
    cstr!("svg").as_ptr(),
    cstr!("svgz").as_ptr(),
    cstr!("svg.gz").as_ptr(),
    std::ptr::null(),
];

#[no_mangle]
extern "C" fn fill_info(info: &mut GdkPixbufFormat) {
    info.name = cstr!("svg").as_ptr() as *mut c_char;
    info.signature = SIGNATURE.as_ptr() as *mut GdkPixbufModulePattern;
    info.description = cstr!("Scalable Vector Graphics").as_ptr() as *mut c_char; //TODO: Gettext this
    info.mime_types = MIME_TYPES.as_ptr() as *mut *mut c_char;
    info.extensions = EXTENSIONS.as_ptr() as *mut *mut c_char;
    info.flags = GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
    info.license = cstr!("LGPL").as_ptr() as *mut c_char;
}

#[cfg(test)]
mod tests {
    use gdk_pixbuf_sys::{
        GdkPixbufFormat, GDK_PIXBUF_FORMAT_SCALABLE, GDK_PIXBUF_FORMAT_THREADSAFE,
    };
    use glib::translate::IntoGlib;

    use crate::{EXTENSIONS, MIME_TYPES};
    use libc::c_char;
    use std::ptr::{null, null_mut};

    fn pb_format_new() -> GdkPixbufFormat {
        let mut info = super::GdkPixbufFormat {
            name: null_mut(),
            signature: null_mut(),
            description: null_mut(),
            mime_types: null_mut(),
            extensions: null_mut(),
            flags: 0,
            license: null_mut(),
            disabled: false.into_glib(),
            domain: null_mut(),
        };

        super::fill_info(&mut info);
        info
    }

    #[test]
    fn fill_info() {
        let info = pb_format_new();

        assert_ne!(info.name, null_mut());
        assert_ne!(info.signature, null_mut());
        assert_ne!(info.description, null_mut());
        assert_ne!(info.mime_types, null_mut());
        assert_ne!(info.extensions, null_mut());
        assert_eq!(
            info.flags,
            GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE
        );
        assert_ne!(info.license, null_mut());
    }

    fn check_null_terminated_arr_cstrings(arr: &[*const c_char]) {
        let n_strings = arr
            .iter()
            .filter(|e| e != &&null())
            .map(|e| {
                if e != &null() {
                    // We use strlen in all of them to ensure some safety
                    // We could use CStr instead but it'd be a bit more cumbersome
                    assert!(unsafe { libc::strlen(*e as *const c_char) } > 0)
                }
            })
            .count(); // Count all non_null items

        // Ensure last item is null and is the only null item
        assert_eq!(n_strings, arr.len() - 1);
        assert!(arr.last().unwrap() == &null());
    }

    #[test]
    fn extensions_bounds() {
        check_null_terminated_arr_cstrings(&EXTENSIONS);
    }

    #[test]
    fn mime_bounds() {
        check_null_terminated_arr_cstrings(&MIME_TYPES)
    }

    #[test]
    fn signature() {
        let info = pb_format_new();

        for i in 0..2 {
            let ptr = unsafe { info.signature.offset(i) };
            if i == 2 {
                assert_eq!(unsafe { (*ptr).prefix }, null_mut());
                continue;
            } else {
                assert_ne!(unsafe { (*ptr).prefix }, null_mut());
                if unsafe { (*ptr).mask } != null_mut() {
                    // Mask can be null
                    assert_eq!(
                        unsafe { libc::strlen((*ptr).prefix as *mut c_char) },
                        unsafe { libc::strlen((*ptr).mask as *mut c_char) }
                    );
                }
                // Relevance must be 0 to 100
                assert!(unsafe { (*ptr).relevance } >= 0);
                assert!(unsafe { (*ptr).relevance } <= 100);
            }
        }
    }

    const SVG_DATA: &'static str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>
                                    <svg
                                    width="100px"
                                    height="150px"
                                    viewBox="0 0 26.458333 26.458333"
                                    version="1.1"
                                    id="svg5"
                                    xmlns="http://www.w3.org/2000/svg"
                                    xmlns:svg="http://www.w3.org/2000/svg">
                                    <rect
                                        style="fill:#aa1144;stroke-width:0.0344347"
                                        width="26.458332"
                                        height="39.6875"
                                        x="4.691162e-07"
                                        y="-6.6145835"
                                        id="rect2" />
                                    </svg>
    "#;

    #[test]
    fn minimal_svg() {
        unsafe extern "C" fn prep_cb(
            pb: *mut gdk_pixbuf_sys::GdkPixbuf,
            pba: *mut gdk_pixbuf_sys::GdkPixbufAnimation,
            user_data: *mut libc::c_void,
        ) {
            assert_eq!(user_data, null_mut());
            assert_eq!(pba, null_mut());

            let w = gdk_pixbuf_sys::gdk_pixbuf_get_width(pb);
            let h = gdk_pixbuf_sys::gdk_pixbuf_get_height(pb);
            let stride = gdk_pixbuf_sys::gdk_pixbuf_get_rowstride(pb);
            assert_eq!(w, 100);
            assert_eq!(h, 150);

            let pixels = gdk_pixbuf_sys::gdk_pixbuf_get_pixels(pb);

            // Upper left pixel, black with full opacity #000000ff
            assert_eq!(*pixels, 0xaa);
            assert_eq!(*pixels.offset(1), 0x11);
            assert_eq!(*pixels.offset(2), 0x44);
            assert_eq!(*pixels.offset(3), 0xff);

            // Bottom left pixel
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize), 0xaa);
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 1), 0x11);
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 2), 0x44);
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 3), 0xff);

            // Bottom right pixel
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4),
                0xaa
            );
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 1),
                0x11
            );
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 2),
                0x44
            );
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 3),
                0xff
            );
        }

        let ctx = unsafe { crate::begin_load(None, Some(prep_cb), None, null_mut(), null_mut()) };
        assert_ne!(ctx, null_mut());

        let inc = unsafe {
            crate::load_increment(ctx, SVG_DATA.as_ptr(), SVG_DATA.len() as u32, null_mut())
        };
        assert_ne!(inc, 0);

        unsafe { crate::stop_load(ctx, null_mut()) };
    }
}
<commit_msg>pixbuf-loader: tidy up imports<commit_after>use std::ptr::null_mut;

use gdk_pixbuf_sys::GdkPixbuf;
use gdk_pixbuf_sys::{
    GdkPixbufFormat, GdkPixbufModule, GdkPixbufModulePattern, GdkPixbufModulePreparedFunc,
    GdkPixbufModuleSizeFunc, GdkPixbufModuleUpdatedFunc, GDK_PIXBUF_FORMAT_SCALABLE,
    GDK_PIXBUF_FORMAT_THREADSAFE,
};

use libc::{c_char, c_int, c_uint};

use glib::translate::{IntoGlib, ToGlibPtr};
use glib::Bytes;
use glib_sys::{gboolean, GError};

use gio::prelude::MemoryInputStreamExt;
use gio::MemoryInputStream;
use gobject_sys::GObject;

use librsvg::rsvg_convert_only::LegacySize;
use librsvg::Loader;

use cstr::cstr;

struct SvgContext {
    size_func: GdkPixbufModuleSizeFunc,
    prep_func: GdkPixbufModulePreparedFunc,
    update_func: GdkPixbufModuleUpdatedFunc,
    user_data: glib_sys::gpointer,
    stream: MemoryInputStream,
}

#[no_mangle]
unsafe extern "C" fn begin_load(
    size_func: GdkPixbufModuleSizeFunc,
    prep_func: GdkPixbufModulePreparedFunc,
    update_func: GdkPixbufModuleUpdatedFunc,
    user_data: glib_sys::gpointer,
    error: *mut *mut GError,
) -> glib_sys::gpointer {
    if error != null_mut() {
        *error = null_mut();
    }

    let stream = MemoryInputStream::new();
    let ctx = Box::new(SvgContext {
        size_func,
        prep_func,
        update_func,
        user_data,
        stream,
    });

    Box::into_raw(ctx) as glib_sys::gpointer
}

#[no_mangle]
unsafe extern "C" fn load_increment(
    user_data: glib_sys::gpointer,
    buffer: *const u8,
    size: c_uint,
    error: *mut *mut GError,
) -> gboolean {
    if error != null_mut() {
        *error = null_mut();
    }

    let ctx = user_data as *mut SvgContext;

    let data = std::slice::from_raw_parts(buffer, size as usize);
    (&*ctx).stream.add_bytes(&Bytes::from(data));
    true.into_glib()
}

#[no_mangle]
unsafe extern "C" fn stop_load(user_data: glib_sys::gpointer, error: *mut *mut GError) -> gboolean {
    let ctx = Box::from_raw(user_data as *mut SvgContext);
    if error != null_mut() {
        *error = null_mut();
    }

    fn _inner_stop_load(ctx: &Box<SvgContext>) -> Result<gdk_pixbuf::Pixbuf, String> {
        let handle = Loader::new()
            .read_stream::<_, gio::File, gio::Cancellable>(&ctx.stream, None, None)
            .map_err(|e| e.to_string())?;

        let renderer = librsvg::CairoRenderer::new(&handle);
        let (w, h) = renderer.legacy_document_size().map_err(|e| e.to_string())?;
        let mut w = w.ceil() as c_int;
        let mut h = h.ceil() as c_int;

        if let Some(size_func) = ctx.size_func {
            let mut tmp_w: c_int = w;
            let mut tmp_h: c_int = h;
            unsafe {
                size_func(
                    &mut tmp_w as *mut c_int,
                    &mut tmp_h as *mut c_int,
                    ctx.user_data,
                )
            };
            if tmp_w != 0 && tmp_h != 0 {
                w = tmp_w;
                h = tmp_h;
            }
        }

        let pb = librsvg::c_api::pixbuf_utils::render_to_pixbuf_at_size(
            &renderer, w as f64, h as f64, w as f64, h as f64,
        )
        .map_err(|e| e.to_string())?;

        Ok(pb)
    }

    let pixbuf = match _inner_stop_load(&ctx) {
        Ok(r) => r,
        Err(e) => {
            if error != null_mut() {
                let gerr = glib::Error::new(gdk_pixbuf::PixbufError::Failed, &e);
                *error = gerr.to_glib_full() as *mut GError;
            }
            return false.into_glib();
        }
    };

    let w = pixbuf.width();
    let h = pixbuf.height();
    let pixbuf: *mut GdkPixbuf = pixbuf.to_glib_full();

    if let Some(prep_func) = ctx.prep_func {
        prep_func(pixbuf, null_mut(), ctx.user_data);
    }
    if let Some(update_func) = ctx.update_func {
        update_func(pixbuf, 0, 0, w, h, ctx.user_data);
    }

    // The module loader increases a ref so we drop the pixbuf here
    gobject_sys::g_object_unref(pixbuf as *mut GObject);

    true.into_glib()
}

#[no_mangle]
extern "C" fn fill_vtable(module: &mut GdkPixbufModule) {
    module.begin_load = Some(begin_load);
    module.stop_load = Some(stop_load);
    module.load_increment = Some(load_increment);
}

const SIGNATURE: [GdkPixbufModulePattern; 3] = [
    GdkPixbufModulePattern {
        prefix: cstr!(" <svg").as_ptr() as *mut c_char,
        mask: cstr!("*    ").as_ptr() as *mut c_char,
        relevance: 100,
    },
    GdkPixbufModulePattern {
        prefix: cstr!(" <!DOCTYPE svg").as_ptr() as *mut c_char,
        mask: cstr!("*             ").as_ptr() as *mut c_char,
        relevance: 100,
    },
    GdkPixbufModulePattern {
        prefix: std::ptr::null_mut(),
        mask: std::ptr::null_mut(),
        relevance: 0,
    },
];

const MIME_TYPES: [*const c_char; 7] = [
    cstr!("image/svg+xml").as_ptr(),
    cstr!("image/svg").as_ptr(),
    cstr!("image/svg-xml").as_ptr(),
    cstr!("image/vnd.adobe.svg+xml").as_ptr(),
    cstr!("text/xml-svg").as_ptr(),
    cstr!("image/svg+xml-compressed").as_ptr(),
    std::ptr::null(),
];

const EXTENSIONS: [*const c_char; 4] = [
    cstr!("svg").as_ptr(),
    cstr!("svgz").as_ptr(),
    cstr!("svg.gz").as_ptr(),
    std::ptr::null(),
];

#[no_mangle]
extern "C" fn fill_info(info: &mut GdkPixbufFormat) {
    info.name = cstr!("svg").as_ptr() as *mut c_char;
    info.signature = SIGNATURE.as_ptr() as *mut GdkPixbufModulePattern;
    info.description = cstr!("Scalable Vector Graphics").as_ptr() as *mut c_char; //TODO: Gettext this
    info.mime_types = MIME_TYPES.as_ptr() as *mut *mut c_char;
    info.extensions = EXTENSIONS.as_ptr() as *mut *mut c_char;
    info.flags = GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
    info.license = cstr!("LGPL").as_ptr() as *mut c_char;
}

#[cfg(test)]
mod tests {
    use gdk_pixbuf_sys::{
        GdkPixbufFormat, GDK_PIXBUF_FORMAT_SCALABLE, GDK_PIXBUF_FORMAT_THREADSAFE,
    };
    use glib::translate::IntoGlib;

    use crate::{EXTENSIONS, MIME_TYPES};
    use libc::c_char;
    use std::ptr::{null, null_mut};

    fn pb_format_new() -> GdkPixbufFormat {
        let mut info = super::GdkPixbufFormat {
            name: null_mut(),
            signature: null_mut(),
            description: null_mut(),
            mime_types: null_mut(),
            extensions: null_mut(),
            flags: 0,
            license: null_mut(),
            disabled: false.into_glib(),
            domain: null_mut(),
        };

        super::fill_info(&mut info);
        info
    }

    #[test]
    fn fill_info() {
        let info = pb_format_new();

        assert_ne!(info.name, null_mut());
        assert_ne!(info.signature, null_mut());
        assert_ne!(info.description, null_mut());
        assert_ne!(info.mime_types, null_mut());
        assert_ne!(info.extensions, null_mut());
        assert_eq!(
            info.flags,
            GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE
        );
        assert_ne!(info.license, null_mut());
    }

    fn check_null_terminated_arr_cstrings(arr: &[*const c_char]) {
        let n_strings = arr
            .iter()
            .filter(|e| e != &&null())
            .map(|e| {
                if e != &null() {
                    // We use strlen in all of them to ensure some safety
                    // We could use CStr instead but it'd be a bit more cumbersome
                    assert!(unsafe { libc::strlen(*e as *const c_char) } > 0)
                }
            })
            .count(); // Count all non_null items

        // Ensure last item is null and is the only null item
        assert_eq!(n_strings, arr.len() - 1);
        assert!(arr.last().unwrap() == &null());
    }

    #[test]
    fn extensions_bounds() {
        check_null_terminated_arr_cstrings(&EXTENSIONS);
    }

    #[test]
    fn mime_bounds() {
        check_null_terminated_arr_cstrings(&MIME_TYPES)
    }

    #[test]
    fn signature() {
        let info = pb_format_new();

        for i in 0..2 {
            let ptr = unsafe { info.signature.offset(i) };
            if i == 2 {
                assert_eq!(unsafe { (*ptr).prefix }, null_mut());
                continue;
            } else {
                assert_ne!(unsafe { (*ptr).prefix }, null_mut());
                if unsafe { (*ptr).mask } != null_mut() {
                    // Mask can be null
                    assert_eq!(
                        unsafe { libc::strlen((*ptr).prefix as *mut c_char) },
                        unsafe { libc::strlen((*ptr).mask as *mut c_char) }
                    );
                }
                // Relevance must be 0 to 100
                assert!(unsafe { (*ptr).relevance } >= 0);
                assert!(unsafe { (*ptr).relevance } <= 100);
            }
        }
    }

    const SVG_DATA: &'static str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>
                                    <svg
                                    width="100px"
                                    height="150px"
                                    viewBox="0 0 26.458333 26.458333"
                                    version="1.1"
                                    id="svg5"
                                    xmlns="http://www.w3.org/2000/svg"
                                    xmlns:svg="http://www.w3.org/2000/svg">
                                    <rect
                                        style="fill:#aa1144;stroke-width:0.0344347"
                                        width="26.458332"
                                        height="39.6875"
                                        x="4.691162e-07"
                                        y="-6.6145835"
                                        id="rect2" />
                                    </svg>
    "#;

    #[test]
    fn minimal_svg() {
        unsafe extern "C" fn prep_cb(
            pb: *mut gdk_pixbuf_sys::GdkPixbuf,
            pba: *mut gdk_pixbuf_sys::GdkPixbufAnimation,
            user_data: *mut libc::c_void,
        ) {
            assert_eq!(user_data, null_mut());
            assert_eq!(pba, null_mut());

            let w = gdk_pixbuf_sys::gdk_pixbuf_get_width(pb);
            let h = gdk_pixbuf_sys::gdk_pixbuf_get_height(pb);
            let stride = gdk_pixbuf_sys::gdk_pixbuf_get_rowstride(pb);
            assert_eq!(w, 100);
            assert_eq!(h, 150);

            let pixels = gdk_pixbuf_sys::gdk_pixbuf_get_pixels(pb);

            // Upper left pixel, black with full opacity #000000ff
            assert_eq!(*pixels, 0xaa);
            assert_eq!(*pixels.offset(1), 0x11);
            assert_eq!(*pixels.offset(2), 0x44);
            assert_eq!(*pixels.offset(3), 0xff);

            // Bottom left pixel
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize), 0xaa);
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 1), 0x11);
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 2), 0x44);
            assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 3), 0xff);

            // Bottom right pixel
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4),
                0xaa
            );
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 1),
                0x11
            );
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 2),
                0x44
            );
            assert_eq!(
                *pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 3),
                0xff
            );
        }

        let ctx = unsafe { crate::begin_load(None, Some(prep_cb), None, null_mut(), null_mut()) };
        assert_ne!(ctx, null_mut());

        let inc = unsafe {
            crate::load_increment(ctx, SVG_DATA.as_ptr(), SVG_DATA.len() as u32, null_mut())
        };
        assert_ne!(inc, 0);

        unsafe { crate::stop_load(ctx, null_mut()) };
    }
}
<|endoftext|>


<commit_before>use node::*;
use typing::Type;

use std::collections::{HashMap, hash_map, HashSet};

pub enum Closure {
    Unit,
    Bool(bool),
    Int(i32),
    Float(f64),
    Var(String),
    AppCls(Box<Closure>, Vec<Closure>),
    AppDir(Box<Closure>, Vec<Closure>),
}

fn g(env: &HashMap<String, Type>, known: &HashSet<String>, e: NodeKind) -> Closure {
    Closure::Unit
}

pub fn f(e: NodeKind) -> Closure {
    g(&HashMap::new(), &HashSet::new(), e)
}
<commit_msg>add a few NodeKind supports on Closure<commit_after>use node::*;
use typing::Type;

use std::collections::{HashMap, hash_map, HashSet};

pub enum Closure {
    Unit,
    Bool(bool),
    Int(i32),
    Float(f64),
    Var(String),
    AppCls(Box<Closure>, Vec<Closure>),
    AppDir(Box<Closure>, Vec<Closure>),
}

fn g(node: NodeKind, env: &HashMap<String, Type>, known: &HashSet<String>) -> Closure {
    match node {
        NodeKind::Unit => Closure::Unit,
        NodeKind::Int(i) => Closure::Int(i),
        NodeKind::Float(f) => Closure::Float(f),
        _ => panic!(),
    }
}

pub fn f(e: NodeKind) -> Closure {
    g(e, &HashMap::new(), &HashSet::new())
}
<|endoftext|>


<commit_before>
use imp_prelude::*;
use libnum;

/// A slice extension trait for concatenating arrays.
pub trait ArrayStackingExt {
    type Output;

    /// Stack the given arrays along the specified axis.
    ///
    /// *Panics* if axis is out of bounds
    /// *Panics* if the slice is empty.
    fn stack(&self, axis: Axis) -> Self::Output;
}

impl<'a, A, D> ArrayStackingExt for [ArrayView<'a, A, D>]
    where A: Clone + libnum::Zero,
          D: Dimension + RemoveAxis
{
    type Output = OwnedArray<A, D>;

    fn stack(&self, axis: Axis) -> <Self as ArrayStackingExt>::Output {
        assert!(self.len() > 0);
        let mut res_dim = self[0].dim().clone();
        let stacked_dim = self.iter()
                              .fold(0, |acc, a| acc + a.dim().index(axis));
        *res_dim.index_mut(axis) = stacked_dim;
        let mut res = OwnedArray::zeros(res_dim);

        let mut array_iter = self.iter();
        let mut in_iter = array_iter.next().unwrap().axis_iter(axis);
        let mut cum = *self[0].dim().index(axis);
        for (ind, mut out) in res.axis_iter_mut(axis).enumerate() {
            if ind == cum {
                let cur_array = array_iter.next().unwrap();
                cum += *cur_array.dim().index(axis);
                in_iter = cur_array.axis_iter(axis);
            }
            out.assign(&in_iter.next().unwrap());
        }
        res
    }
}
<commit_msg>simplify stacking impl using split_at<commit_after>
use imp_prelude::*;
use libnum;

/// A slice extension trait for concatenating arrays.
pub trait ArrayStackingExt {
    type Output;

    /// Stack the given arrays along the specified axis.
    ///
    /// *Panics* if axis is out of bounds
    /// *Panics* if the slice is empty.
    fn stack(&self, axis: Axis) -> Self::Output;
}

impl<'a, A, D> ArrayStackingExt for [ArrayView<'a, A, D>]
    where A: Clone + libnum::Zero,
          D: Dimension + RemoveAxis
{
    type Output = OwnedArray<A, D>;

    fn stack(&self, axis: Axis) -> <Self as ArrayStackingExt>::Output {
        assert!(self.len() > 0);
        let mut res_dim = self[0].dim().clone();
        let stacked_dim = self.iter()
                              .fold(0, |acc, a| acc + a.dim().index(axis));
        *res_dim.index_mut(axis) = stacked_dim;
        let mut res = OwnedArray::zeros(res_dim);

        {
            let mut assign_view = res.view_mut();
            for array in self {
                let len = *array.dim().index(axis);
                let (mut front, rest) = assign_view.split_at(axis, len);
                front.assign(array);
                assign_view = rest;
            }
        }
        res
    }
}
<|endoftext|>


<commit_before>
use imp_prelude::*;
use error::{ShapeError, ErrorKind, from_kind};

/// Stack arrays along the given axis.
///
/// *Panics* if axis is out of bounds.
pub fn stack<'a, A, D>(arrays: &[ArrayView<'a, A, D>], axis: Axis)
    -> Result<OwnedArray<A, D>, ShapeError>
    where A: Copy,
          D: Dimension + RemoveAxis
{
    if arrays.len() == 0 {
        return Err(from_kind(ErrorKind::Unsupported));
    }
    let mut res_dim = arrays[0].dim().clone();
    let common_dim = res_dim.remove_axis(axis);
    if arrays.iter().any(|a| a.dim().remove_axis(axis) != common_dim) {
        return Err(from_kind(ErrorKind::IncompatibleShape));
    }

    let stacked_dim = arrays.iter()
                            .fold(0, |acc, a| acc + a.dim().index(axis));
    *res_dim.index_mut(axis) = stacked_dim;

    // we can safely use uninitialized values here because they are Copy
    // and we will only ever write to them
    let size = res_dim.size();
    let mut v = Vec::with_capacity(size);
    unsafe {
        v.set_len(size);
    }
    let mut res = try!(OwnedArray::from_vec_dim(res_dim, v));

    {
        let mut assign_view = res.view_mut();
        for array in arrays {
            let len = *array.dim().index(axis);
            let (mut front, rest) = assign_view.split_at(axis, len);
            front.assign(array);
            assign_view = rest;
        }
    }
    Ok(res)
}

#[macro_export]
macro_rules! stack {
    ($axis:expr, $( $a:expr ),+ ) => {
        ndarray::stack(&[ $($a.view() ),* ], $axis).unwrap()
    }
}
<commit_msg>stacking returns OutOfBounds error instead of panicking<commit_after>
use imp_prelude::*;
use error::{ShapeError, ErrorKind, from_kind};

/// Stack arrays along the given axis.
pub fn stack<'a, A, D>(arrays: &[ArrayView<'a, A, D>], axis: Axis)
    -> Result<OwnedArray<A, D>, ShapeError>
    where A: Copy,
          D: Dimension + RemoveAxis
{
    let mut res_dim = arrays[0].dim().clone();
    if axis.axis() >= res_dim.slice().len() {
        return Err(from_kind(ErrorKind::OutOfBounds));
    }
    if arrays.len() == 0 {
        return Err(from_kind(ErrorKind::Unsupported));
    }
    let common_dim = res_dim.remove_axis(axis);
    if arrays.iter().any(|a| a.dim().remove_axis(axis) != common_dim) {
        return Err(from_kind(ErrorKind::IncompatibleShape));
    }

    let stacked_dim = arrays.iter()
                            .fold(0, |acc, a| acc + a.dim().index(axis));
    *res_dim.index_mut(axis) = stacked_dim;

    // we can safely use uninitialized values here because they are Copy
    // and we will only ever write to them
    let size = res_dim.size();
    let mut v = Vec::with_capacity(size);
    unsafe {
        v.set_len(size);
    }
    let mut res = try!(OwnedArray::from_vec_dim(res_dim, v));

    {
        let mut assign_view = res.view_mut();
        for array in arrays {
            let len = *array.dim().index(axis);
            let (mut front, rest) = assign_view.split_at(axis, len);
            front.assign(array);
            assign_view = rest;
        }
    }
    Ok(res)
}

#[macro_export]
macro_rules! stack {
    ($axis:expr, $( $a:expr ),+ ) => {
        ndarray::stack(&[ $($a.view() ),* ], $axis).unwrap()
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, VecDeque};

use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
use crate::grammar::Rule;
use crate::json::path::*;
use crate::json::value::ScopedJson;
use crate::util::extend;

pub type Object = HashMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

#[derive(Debug)]
enum ResolvedPath<'a> {
    // FIXME: change to borrowed when possible
    // full path
    AbsolutePath(Vec<String>),
    // relative path and path root
    RelativePath(Vec<String>),
    // relative path against block param value
    BlockParamValue(Vec<String>, &'a Json),
    // relative path against derived value,
    LocalValue(Vec<String>, &'a Json),
}

fn parse_json_visitor<'a, 'reg>(
    relative_path: &[PathSeg],
    block_contexts: &'a VecDeque<BlockContext<'reg>>,
    always_for_absolute_path: bool,
) -> ResolvedPath<'a> {
    let mut path_context_depth: i64 = 0;
    let mut with_block_param = None;
    let mut from_root = false;

    // peek relative_path for block param, @root and  "../../"
    for path_seg in relative_path {
        match path_seg {
            PathSeg::Named(the_path) => {
                if let Some((holder, base_path)) = get_in_block_params(&block_contexts, the_path) {
                    with_block_param = Some((holder, base_path));
                }
                break;
            }
            PathSeg::Ruled(the_rule) => match the_rule {
                Rule::path_root => {
                    from_root = true;
                    break;
                }
                Rule::path_up => path_context_depth += 1,
                _ => break,
            },
        }
    }

    let mut path_stack = Vec::with_capacity(relative_path.len() + 5);
    match with_block_param {
        Some((BlockParamHolder::Value(ref value), _)) => {
            merge_json_path(&mut path_stack, &relative_path[1..]);
            ResolvedPath::BlockParamValue(path_stack, value)
        }
        Some((BlockParamHolder::Path(ref paths), base_path)) => {
            extend(&mut path_stack, base_path);
            if !paths.is_empty() {
                extend(&mut path_stack, paths);
            }
            merge_json_path(&mut path_stack, &relative_path[1..]);

            ResolvedPath::AbsolutePath(path_stack)
        }
        None => {
            if path_context_depth > 0 {
                if let Some(ref context_base_path) = block_contexts
                    .get(path_context_depth as usize)
                    .map(|blk| blk.base_path())
                {
                    extend(&mut path_stack, context_base_path);
                } else {
                    // TODO: is this correct behaviour?
                    if let Some(ref base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        extend(&mut path_stack, base_path);
                    }
                }
                merge_json_path(&mut path_stack, relative_path);
                ResolvedPath::AbsolutePath(path_stack)
            } else if from_root {
                merge_json_path(&mut path_stack, relative_path);
                ResolvedPath::AbsolutePath(path_stack)
            } else if always_for_absolute_path {
                if let Some(base_value) = block_contexts.front().and_then(|blk| blk.base_value()) {
                    merge_json_path(&mut path_stack, relative_path);
                    ResolvedPath::LocalValue(path_stack, base_value)
                } else {
                    if let Some(base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        extend(&mut path_stack, base_path);
                    }
                    merge_json_path(&mut path_stack, relative_path);
                    ResolvedPath::AbsolutePath(path_stack)
                }
            } else {
                merge_json_path(&mut path_stack, relative_path);
                ResolvedPath::RelativePath(path_stack)
            }
        }
    }
}

fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> {
    let result = match d {
        Some(&Json::Array(ref l)) => p.parse::<usize>().map(|idx_u| l.get(idx_u))?,
        Some(&Json::Object(ref m)) => m.get(p),
        Some(_) => None,
        None => None,
    };
    Ok(result)
}

fn get_in_block_params<'a, 'reg>(
    block_contexts: &'a VecDeque<BlockContext<'reg>>,
    p: &str,
) -> Option<(&'a BlockParamHolder, &'a Vec<String>)> {
    for bc in block_contexts {
        let v = bc.get_block_param(p);
        if v.is_some() {
            return v.map(|v| (v, bc.base_path()));
        }
    }

    None
}

pub(crate) fn merge_json(base: &Json, addition: &HashMap<&str, &Json>) -> Json {
    let mut base_map = match base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert(k.to_string(), (*v).clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with relative path and block scopes
    pub(crate) fn navigate<'reg, 'rc>(
        &'rc self,
        relative_path: &[PathSeg],
        block_contexts: &VecDeque<BlockContext<'reg>>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // always use absolute at the moment until we get base_value lifetime issue fixed
        let resolved_visitor = parse_json_visitor(&relative_path, block_contexts, true);

        // debug logging
        debug!("Accessing context value: {:?}", resolved_visitor);

        match resolved_visitor {
            ResolvedPath::AbsolutePath(paths) => {
                let mut ptr = Some(self.data());
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }

                Ok(ptr
                    .map(|v| ScopedJson::Context(v, paths))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::RelativePath(_paths) => {
                // relative path is disabled for now
                unreachable!()
                // let mut ptr = block_contexts.front().and_then(|blk| blk.base_value());
                // for p in paths.iter() {
                //     ptr = get_data(ptr, p)?;
                // }

                // Ok(ptr
                //     .map(|v| ScopedJson::Context(v, paths))
                //     .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::BlockParamValue(paths, value)
            | ResolvedPath::LocalValue(paths, value) => {
                let mut ptr = Some(value);
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }
                Ok(ptr
                    .map(|v| ScopedJson::Derived(v.clone()))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
        }
    }

    /// Return the Json data wrapped in context
    pub fn data(&self) -> &Json {
        &self.data
    }

    /// Return the mutable reference to Json data wrapped in context
    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

#[cfg(test)]
mod test {
    use crate::block::{BlockContext, BlockParams};
    use crate::context::{self, Context};
    use crate::error::RenderError;
    use crate::json::path::Path;
    use crate::json::value::{self, ScopedJson};
    use serde_json::value::Map;
    use std::collections::{HashMap, VecDeque};

    fn navigate_from_root<'reg, 'rc>(
        ctx: &'rc Context,
        path: &str,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        let relative_path = Path::parse(path).unwrap();
        ctx.navigate(relative_path.segs().unwrap(), &VecDeque::new())
    }

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this").unwrap().render(),
            v.to_string()
        );
    }

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

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

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "./addr/country").unwrap().render(),
            "China".to_string()
        );
        assert_eq!(
            navigate_from_root(&ctx, "addr.[country]").unwrap().render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            navigate_from_root(&ctx2, "this").unwrap().render(),
            "true".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "titles.[0]").unwrap().render(),
            "programmer".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "age").unwrap().render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            navigate_from_root(&ctx1, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx2, "age").unwrap().render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let mut hash = HashMap::new();
        let v = value::to_json("h1");
        hash.insert("tag", &v);

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a1, "age").unwrap().render(),
            "4".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a1, "tag").unwrap().render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a2, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a2, "tag").unwrap().render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this_name").unwrap().render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        let mut block = BlockContext::new();
        *block.base_path_mut() = ["a".to_owned(), "b".to_owned()].to_vec();

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("@root/b").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
    }

    #[test]
    fn test_block_params() {
        let m = json!([{
            "a": [1, 2]
        }, {
            "b": [2, 3]
        }]);

        let ctx = Context::wraps(&m).unwrap();
        let mut block_params = BlockParams::new();
        block_params
            .add_path("z", ["0".to_owned(), "a".to_owned()].to_vec())
            .unwrap();
        block_params.add_value("t", json!("good")).unwrap();

        let mut block = BlockContext::new();
        block.set_block_params(block_params);

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("z.[1]").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
        assert_eq!(
            ctx.navigate(&Path::parse("t").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "good".to_string()
        );
    }
}
<commit_msg>(fix) issue when up level block has base value<commit_after>use std::collections::{HashMap, VecDeque};

use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
use crate::grammar::Rule;
use crate::json::path::*;
use crate::json::value::ScopedJson;
use crate::util::extend;

pub type Object = HashMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

#[derive(Debug)]
enum ResolvedPath<'a> {
    // FIXME: change to borrowed when possible
    // full path
    AbsolutePath(Vec<String>),
    // relative path and path root
    RelativePath(Vec<String>),
    // relative path against block param value
    BlockParamValue(Vec<String>, &'a Json),
    // relative path against derived value,
    LocalValue(Vec<String>, &'a Json),
}

fn parse_json_visitor<'a, 'reg>(
    relative_path: &[PathSeg],
    block_contexts: &'a VecDeque<BlockContext<'reg>>,
    always_for_absolute_path: bool,
) -> ResolvedPath<'a> {
    let mut path_context_depth: i64 = 0;
    let mut with_block_param = None;
    let mut from_root = false;

    // peek relative_path for block param, @root and  "../../"
    for path_seg in relative_path {
        match path_seg {
            PathSeg::Named(the_path) => {
                if let Some((holder, base_path)) = get_in_block_params(&block_contexts, the_path) {
                    with_block_param = Some((holder, base_path));
                }
                break;
            }
            PathSeg::Ruled(the_rule) => match the_rule {
                Rule::path_root => {
                    from_root = true;
                    break;
                }
                Rule::path_up => path_context_depth += 1,
                _ => break,
            },
        }
    }

    let mut path_stack = Vec::with_capacity(relative_path.len() + 5);
    match with_block_param {
        Some((BlockParamHolder::Value(ref value), _)) => {
            merge_json_path(&mut path_stack, &relative_path[1..]);
            ResolvedPath::BlockParamValue(path_stack, value)
        }
        Some((BlockParamHolder::Path(ref paths), base_path)) => {
            extend(&mut path_stack, base_path);
            if !paths.is_empty() {
                extend(&mut path_stack, paths);
            }
            merge_json_path(&mut path_stack, &relative_path[1..]);

            ResolvedPath::AbsolutePath(path_stack)
        }
        None => {
            if path_context_depth > 0 {
                let blk = block_contexts
                    .get(path_context_depth as usize)
                    .or_else(|| block_contexts.front());

                if let Some(base_value) = blk.and_then(|blk| blk.base_value()) {
                    merge_json_path(&mut path_stack, relative_path);
                    ResolvedPath::LocalValue(path_stack, base_value)
                } else {
                    if let Some(base_path) = blk.map(|blk| blk.base_path()) {
                        extend(&mut path_stack, base_path);
                    }
                    merge_json_path(&mut path_stack, relative_path);
                    ResolvedPath::AbsolutePath(path_stack)
                }
            } else if from_root {
                merge_json_path(&mut path_stack, relative_path);
                ResolvedPath::AbsolutePath(path_stack)
            } else if always_for_absolute_path {
                if let Some(base_value) = block_contexts.front().and_then(|blk| blk.base_value()) {
                    merge_json_path(&mut path_stack, relative_path);
                    ResolvedPath::LocalValue(path_stack, base_value)
                } else {
                    if let Some(base_path) = block_contexts.front().map(|blk| blk.base_path()) {
                        extend(&mut path_stack, base_path);
                    }
                    merge_json_path(&mut path_stack, relative_path);
                    ResolvedPath::AbsolutePath(path_stack)
                }
            } else {
                merge_json_path(&mut path_stack, relative_path);
                ResolvedPath::RelativePath(path_stack)
            }
        }
    }
}

fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> {
    let result = match d {
        Some(&Json::Array(ref l)) => p.parse::<usize>().map(|idx_u| l.get(idx_u))?,
        Some(&Json::Object(ref m)) => m.get(p),
        Some(_) => None,
        None => None,
    };
    Ok(result)
}

fn get_in_block_params<'a, 'reg>(
    block_contexts: &'a VecDeque<BlockContext<'reg>>,
    p: &str,
) -> Option<(&'a BlockParamHolder, &'a Vec<String>)> {
    for bc in block_contexts {
        let v = bc.get_block_param(p);
        if v.is_some() {
            return v.map(|v| (v, bc.base_path()));
        }
    }

    None
}

pub(crate) fn merge_json(base: &Json, addition: &HashMap<&str, &Json>) -> Json {
    let mut base_map = match base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert(k.to_string(), (*v).clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with relative path and block scopes
    pub(crate) fn navigate<'reg, 'rc>(
        &'rc self,
        relative_path: &[PathSeg],
        block_contexts: &VecDeque<BlockContext<'reg>>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        // always use absolute at the moment until we get base_value lifetime issue fixed
        let resolved_visitor = parse_json_visitor(&relative_path, block_contexts, true);

        // debug logging
        debug!("Accessing context value: {:?}", resolved_visitor);

        match resolved_visitor {
            ResolvedPath::AbsolutePath(paths) => {
                let mut ptr = Some(self.data());
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }

                Ok(ptr
                    .map(|v| ScopedJson::Context(v, paths))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::RelativePath(_paths) => {
                // relative path is disabled for now
                unreachable!()
                // let mut ptr = block_contexts.front().and_then(|blk| blk.base_value());
                // for p in paths.iter() {
                //     ptr = get_data(ptr, p)?;
                // }

                // Ok(ptr
                //     .map(|v| ScopedJson::Context(v, paths))
                //     .unwrap_or_else(|| ScopedJson::Missing))
            }
            ResolvedPath::BlockParamValue(paths, value)
            | ResolvedPath::LocalValue(paths, value) => {
                let mut ptr = Some(value);
                for p in paths.iter() {
                    ptr = get_data(ptr, p)?;
                }
                Ok(ptr
                    .map(|v| ScopedJson::Derived(v.clone()))
                    .unwrap_or_else(|| ScopedJson::Missing))
            }
        }
    }

    /// Return the Json data wrapped in context
    pub fn data(&self) -> &Json {
        &self.data
    }

    /// Return the mutable reference to Json data wrapped in context
    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

#[cfg(test)]
mod test {
    use crate::block::{BlockContext, BlockParams};
    use crate::context::{self, Context};
    use crate::error::RenderError;
    use crate::json::path::Path;
    use crate::json::value::{self, ScopedJson};
    use serde_json::value::Map;
    use std::collections::{HashMap, VecDeque};

    fn navigate_from_root<'reg, 'rc>(
        ctx: &'rc Context,
        path: &str,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        let relative_path = Path::parse(path).unwrap();
        ctx.navigate(relative_path.segs().unwrap(), &VecDeque::new())
    }

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this").unwrap().render(),
            v.to_string()
        );
    }

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

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

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "./addr/country").unwrap().render(),
            "China".to_string()
        );
        assert_eq!(
            navigate_from_root(&ctx, "addr.[country]").unwrap().render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            navigate_from_root(&ctx2, "this").unwrap().render(),
            "true".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "titles.[0]").unwrap().render(),
            "programmer".to_string()
        );

        assert_eq!(
            navigate_from_root(&ctx, "age").unwrap().render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            navigate_from_root(&ctx1, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx2, "age").unwrap().render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let mut hash = HashMap::new();
        let v = value::to_json("h1");
        hash.insert("tag", &v);

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a1, "age").unwrap().render(),
            "4".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a1, "tag").unwrap().render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            navigate_from_root(&ctx_a2, "this").unwrap().render(),
            "[object]".to_owned()
        );
        assert_eq!(
            navigate_from_root(&ctx_a2, "tag").unwrap().render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            navigate_from_root(&ctx, "this_name").unwrap().render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        let mut block = BlockContext::new();
        *block.base_path_mut() = ["a".to_owned(), "b".to_owned()].to_vec();

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("@root/b").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
    }

    #[test]
    fn test_block_params() {
        let m = json!([{
            "a": [1, 2]
        }, {
            "b": [2, 3]
        }]);

        let ctx = Context::wraps(&m).unwrap();
        let mut block_params = BlockParams::new();
        block_params
            .add_path("z", ["0".to_owned(), "a".to_owned()].to_vec())
            .unwrap();
        block_params.add_value("t", json!("good")).unwrap();

        let mut block = BlockContext::new();
        block.set_block_params(block_params);

        let mut blocks = VecDeque::new();
        blocks.push_front(block);

        assert_eq!(
            ctx.navigate(&Path::parse("z.[1]").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "2".to_string()
        );
        assert_eq!(
            ctx.navigate(&Path::parse("t").unwrap().segs().unwrap(), &blocks)
                .unwrap()
                .render(),
            "good".to_string()
        );
    }
}
<|endoftext|>


<commit_before>extern crate rayon;
extern crate ppbert;
#[macro_use] extern crate clap;

use clap::{Arg, App};
use rayon::prelude::*;

use std::io::{self, Read, Write, BufWriter};
use std::fs::File;
use std::path::PathBuf;
use std::process::exit;
use std::time::Instant;

use ppbert::bertterm::BertTerm;
use ppbert::error::BertError;
use ppbert::parser;

const PROG_NAME: &str = "bert-convert";

fn main() {
    let matches = App::new(PROG_NAME)
        .version(crate_version!())
        .author("Vincent Foley")
        .about("Convert bertconf .bert files to rig .bert2 files")
        .arg(Arg::with_name("input_files")
             .value_name("FILES")
             .multiple(true))
        .arg(Arg::with_name("output-dir")
             .help("Selects the output directory")
             .value_name("DIR")
             .default_value(".")
             .short("d")
             .long("--output-dir"))
        .arg(Arg::with_name("verbose")
             .help("Enables verbose mode")
             .short("v")
             .long("--verbose"))
        .get_matches();

    let files: Vec<&str> = match matches.values_of("input_files") {
        Some(files) => files.collect(),
        None => vec!["-"]
    };

    let verbose = matches.is_present("verbose");
    let output_dir: PathBuf = matches.value_of("output-dir")
        .unwrap_or(".")
        .into();

    if !output_dir.exists() {
        eprintln!("{}: directory {:?} does not exist", PROG_NAME, output_dir);
        exit(1);
    }

    let ret_code_sum: i32 = files
        .into_par_iter()
        .map(|file| {
            match handle(file, verbose, &output_dir) {
                Ok(()) => 0,
                Err(e) => {
                    eprintln!("{}: {}", PROG_NAME, e);
                    1
                }
            }
        })
        .sum();
    exit((ret_code_sum > 0) as i32);
}

fn handle(file: &str, verbose: bool, output_dir: &PathBuf) -> Result<(), BertError> {
    let mut buf: Vec<u8> = Vec::new();
    if file == "-" {
        let mut stdin = io::stdin();
        stdin.read_to_end(&mut buf)?;
    } else {
        let mut f = File::open(file)?;
        f.read_to_end(&mut buf)?;
    }

    let top = Instant::now();
    let mut parser = parser::Parser::new(buf);
    let term = parser.parse().unwrap();
    let dur1 = top.elapsed();

    let top = Instant::now();
    create_bert2_file(term, output_dir)?;
    let dur2 = top.elapsed();
    if verbose {
        eprintln!("{}: {}: Parse time: {}.{:09}s; Dump time: {}.{:09}s",
                  PROG_NAME, file,
                  dur1.as_secs(), dur1.subsec_nanos(),
                  dur2.as_secs(), dur2.subsec_nanos());
    }

    return Ok(());
}

fn create_bert2_file(term: BertTerm, output_dir: &PathBuf) -> Result<(), BertError> {
    // TODO(vfoley): this is easily the ugliest Rust code I've written. Fix!
    if let BertTerm::List(terms) = term {
        for term in terms {
            if let BertTerm::Tuple(tup_terms) = term {
                if tup_terms.len() != 2 {
                    return Err(BertError::NotABertFile);
                }
                if let BertTerm::Atom(ref table_name) = tup_terms[0] {
                    if let BertTerm::List(ref items) = tup_terms[1] {
                        let file_name = format!("{}.bert2", table_name);
                        let path = output_dir.join(file_name);
                        let f = File::create(path)?;
                        let mut stream = BufWriter::new(f);
                        for item in items {
                            let item_bert = item.write_bert();
                            stream.write_all(&usize_to_leb128(item_bert.len()))?;
                            stream.write_all(&item_bert)?;
                        }
                    } else {
                        return Err(BertError::NotABertFile);
                    }
                } else {
                    return Err(BertError::NotABertFile);
                }
            } else {
                return Err(BertError::NotABertFile);
            }
        }
    } else {
        return Err(BertError::NotABertFile);
    }
    return Ok(());
}

fn usize_to_leb128(mut n: usize) -> Vec<u8> {
    let mut bytes: Vec<u8> = Vec::with_capacity(8);
    while n > 0 {
        let mut x: u8 = (n & 0x7f) as u8;
        x |= ((n > 127) as u8) << 7; // if n > 127 { x |= 0x80; }
        bytes.push(x);
        n >>= 7;
    }
    return bytes;
}

#[test]
fn test_usize_to_leb128() {
    assert_eq!(usize_to_leb128(10), vec![0x0A]);
    assert_eq!(usize_to_leb128(300), vec![0xAC, 0x02]);
    assert_eq!(usize_to_leb128(624485), vec![0xE5, 0x8E, 0x26]);
}
<commit_msg>Remove .unwrap() for bert-convert<commit_after>extern crate rayon;
extern crate ppbert;
#[macro_use] extern crate clap;

use clap::{Arg, App};
use rayon::prelude::*;

use std::io::{self, Read, Write, BufWriter};
use std::fs::File;
use std::path::PathBuf;
use std::process::exit;
use std::time::Instant;

use ppbert::bertterm::BertTerm;
use ppbert::error::BertError;
use ppbert::parser;

const PROG_NAME: &str = "bert-convert";

fn main() {
    let matches = App::new(PROG_NAME)
        .version(crate_version!())
        .author("Vincent Foley")
        .about("Convert bertconf .bert files to rig .bert2 files")
        .arg(Arg::with_name("input_files")
             .value_name("FILES")
             .multiple(true))
        .arg(Arg::with_name("output-dir")
             .help("Selects the output directory")
             .value_name("DIR")
             .default_value(".")
             .short("d")
             .long("--output-dir"))
        .arg(Arg::with_name("verbose")
             .help("Enables verbose mode")
             .short("v")
             .long("--verbose"))
        .get_matches();

    let files: Vec<&str> = match matches.values_of("input_files") {
        Some(files) => files.collect(),
        None => vec!["-"]
    };

    let verbose = matches.is_present("verbose");
    let output_dir: PathBuf = matches.value_of("output-dir")
        .unwrap_or(".")
        .into();

    if !output_dir.exists() {
        eprintln!("{}: directory {:?} does not exist", PROG_NAME, output_dir);
        exit(1);
    }

    let ret_code_sum: i32 = files
        .into_par_iter()
        .map(|file| {
            match handle(file, verbose, &output_dir) {
                Ok(()) => 0,
                Err(e) => {
                    eprintln!("{}: {}", PROG_NAME, e);
                    1
                }
            }
        })
        .sum();
    exit((ret_code_sum > 0) as i32);
}

fn handle(file: &str, verbose: bool, output_dir: &PathBuf) -> Result<(), BertError> {
    let mut buf: Vec<u8> = Vec::new();
    if file == "-" {
        let mut stdin = io::stdin();
        stdin.read_to_end(&mut buf)?;
    } else {
        let mut f = File::open(file)?;
        f.read_to_end(&mut buf)?;
    }

    let top = Instant::now();
    let mut parser = parser::Parser::new(buf);
    let term = parser.parse()?;
    let dur1 = top.elapsed();

    let top = Instant::now();
    create_bert2_file(term, output_dir)?;
    let dur2 = top.elapsed();
    if verbose {
        eprintln!("{}: {}: Parse time: {}.{:09}s; Dump time: {}.{:09}s",
                  PROG_NAME, file,
                  dur1.as_secs(), dur1.subsec_nanos(),
                  dur2.as_secs(), dur2.subsec_nanos());
    }

    return Ok(());
}

fn create_bert2_file(term: BertTerm, output_dir: &PathBuf) -> Result<(), BertError> {
    // TODO(vfoley): this is easily the ugliest Rust code I've written. Fix!
    if let BertTerm::List(terms) = term {
        for term in terms {
            if let BertTerm::Tuple(tup_terms) = term {
                if tup_terms.len() != 2 {
                    return Err(BertError::NotABertFile);
                }
                if let BertTerm::Atom(ref table_name) = tup_terms[0] {
                    if let BertTerm::List(ref items) = tup_terms[1] {
                        let file_name = format!("{}.bert2", table_name);
                        let path = output_dir.join(file_name);
                        let f = File::create(path)?;
                        let mut stream = BufWriter::new(f);
                        for item in items {
                            let item_bert = item.write_bert();
                            stream.write_all(&usize_to_leb128(item_bert.len()))?;
                            stream.write_all(&item_bert)?;
                        }
                    } else {
                        return Err(BertError::NotABertFile);
                    }
                } else {
                    return Err(BertError::NotABertFile);
                }
            } else {
                return Err(BertError::NotABertFile);
            }
        }
    } else {
        return Err(BertError::NotABertFile);
    }
    return Ok(());
}

fn usize_to_leb128(mut n: usize) -> Vec<u8> {
    let mut bytes: Vec<u8> = Vec::with_capacity(8);
    while n > 0 {
        let mut x: u8 = (n & 0x7f) as u8;
        x |= ((n > 127) as u8) << 7; // if n > 127 { x |= 0x80; }
        bytes.push(x);
        n >>= 7;
    }
    return bytes;
}

#[test]
fn test_usize_to_leb128() {
    assert_eq!(usize_to_leb128(10), vec![0x0A]);
    assert_eq!(usize_to_leb128(300), vec![0xAC, 0x02]);
    assert_eq!(usize_to_leb128(624485), vec![0xE5, 0x8E, 0x26]);
}
<|endoftext|>


<commit_before>pub mod opcode;
mod state;
mod executor;

use std::io;
use std::path::Path;
use std::num;
use std::collections::HashMap;

use cpu::opcode::ExecInfo;
use cpu::opcode::DebugInfo;

pub use self::state::CpuState;
pub use self::state::DecodeInfo;

pub use self::executor::CpuExecutor;
<commit_msg>removed erroneous code<commit_after>pub mod opcode;
mod state;
mod executor;

pub use self::state::CpuState;
pub use self::state::DecodeInfo;

pub use self::executor::CpuExecutor;

<|endoftext|>


<commit_before>pub mod bookmark_dao;
pub mod query_parser;
<commit_msg>add super use<commit_after>pub mod bookmark_dao;
pub mod query_parser;

pub use super::common::bookmark::*;
<|endoftext|>