<commit_before>//! Kata OS process management support

#![cfg_attr(not(test), no_std)]

extern crate alloc;
use alloc::string::String;
use alloc::boxed::Box;
use kata_memory_interface::ObjDescBundle;
use kata_os_common::cspace_slot::CSpaceSlot;
use kata_proc_interface::Bundle;
use kata_proc_interface::BundleImplInterface;
use kata_proc_interface::BundleIdArray;
use kata_proc_interface::PackageManagementInterface;
use kata_proc_interface::ProcessControlInterface;
use kata_proc_interface::ProcessManagerError;
use kata_proc_interface::ProcessManagerInterface;
use kata_security_interface::kata_security_install;
use kata_security_interface::kata_security_load_application;
use kata_security_interface::kata_security_uninstall;
use log::trace;
use spin::Mutex;

mod sel4bundle;
use sel4bundle::seL4BundleImpl;

mod proc_manager;
pub use proc_manager::ProcessManager;

// NB: KATA_PROC cannot be used before setup is completed with a call to init()
#[cfg(not(test))]
pub static mut KATA_PROC: KataProcManager = KataProcManager::empty();

// KataProcManager bundles an instance of the ProcessManager that operates
// on KataOS interfaces and synchronizes public use with a Mutex. There is
// a two-step dance to setup an instance because we want KATA_PROC static
// and ProcessManager is incapable of supplying a const fn due it's use of
// hashbrown::HashMap.
pub struct KataProcManager {
    manager: Mutex<Option<ProcessManager>>,
}
impl KataProcManager {
    // Constructs a partially-initialized instance; to complete call init().
    // This is needed because we need a const fn for static setup and with
    // that constraint we cannot reference self.interface.
    const fn empty() -> KataProcManager {
        KataProcManager {
            manager: Mutex::new(None),
        }
    }

    // Finishes the setup started by empty():
    pub fn init(&self) {
        *self.manager.lock() = Some(ProcessManager::new(KataManagerInterface));
    }

    // Returns the bundle capacity.
    pub fn capacity(&self) -> usize {
        self.manager.lock().as_ref().unwrap().capacity()
    }
}
// These just lock accesses and handle the necessary indirection.
impl PackageManagementInterface for KataProcManager {
    fn install(
        &mut self,
        pkg_contents: &ObjDescBundle,
    ) -> Result<String, ProcessManagerError> {
        self.manager
            .lock()
            .as_mut()
            .unwrap()
            .install(pkg_contents)
    }
    fn uninstall(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        self.manager.lock().as_mut().unwrap().uninstall(bundle_id)
    }
}
impl ProcessControlInterface for KataProcManager {
    fn start(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        self.manager.lock().as_mut().unwrap().start(bundle_id)
    }
    fn stop(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        self.manager.lock().as_mut().unwrap().stop(bundle_id)
    }
    fn get_running_bundles(&self) -> Result<BundleIdArray, ProcessManagerError> {
        self.manager.lock().as_ref().unwrap().get_running_bundles()
    }
}

struct KataManagerInterface;
impl ProcessManagerInterface for KataManagerInterface {
    fn install(
        &mut self,
        pkg_contents: &ObjDescBundle,
    ) -> Result<String, ProcessManagerError> {
        trace!("ProcessManagerInterface::install pkg_contents {}", pkg_contents);

        // Package contains: application manifest, application binary, and
        // (optional) ML workload binary to run on vector core.
        // Manifest contains bundle_id.
        // Resulting flash file/pathname is fixed (1 app / bundle), only need bundle_id.
        // Pass opaque package contents through; get back bundle_id.

        // This is handled by the SecurityCoordinator.
        Ok(kata_security_install(pkg_contents)?)
    }
    fn uninstall(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        trace!("ProcessManagerInterface::uninstall bundle_id {}", bundle_id);

        // NB: the caller has already checked no running application exists
        // NB: the Security Core is assumed to invalidate/remove any kv store

        // This is handled by the SecurityCoordinator.
        Ok(kata_security_uninstall(bundle_id)?)
    }
    fn start(&mut self, bundle: &Bundle) -> Result<Box<dyn BundleImplInterface>, ProcessManagerError> {
        trace!("ProcessManagerInterface::start {:?}", bundle);

        // Design doc says:
        // 1. Ask security core for application footprint with SizeBuffer
        // 2. Ask security core for manifest (maybe piggyback on SizeBuffer)
        //    and parse for necessary info (e.g. whether kv Storage is
        //    required, other privileges/capabilities)
        // 3. Ask MemoryManager for shared memory pages for the application
        //    (model handled separately by MlCoordinator since we do not know
        //    which model will be used)
        // 4. Allocate other seL4 resources:
        //     - VSpace, TCB & necessary capabiltiies
        // 5. Ask security core to VerifyAndLoad app into shared memory pages
        // 6. Complete seL4 setup:
        //     - Setup application system context and start thread
        //     - Badge seL4 recv cap w/ bundle_id for (optional) StorageManager
        //       access
        // What we do atm is:
        // 1. Ask SecurityCoordinator to return the application contents to load.
        //    Data are delivered as a read-only ObjDescBundle ready to copy into
        //    the VSpace.
        // 2. Do 4+6 with BundleImplInterface::start.

        // TODO(sleffler): awkward container_slot ownership
        let mut container_slot = CSpaceSlot::new();
        let bundle_frames =
            kata_security_load_application(&bundle.app_id, &container_slot)?;
        let mut sel4_bundle = seL4BundleImpl::new(bundle, &bundle_frames)?;
        sel4_bundle.start()?;

        // sel4_bundle owns container_slot now; release our ref so it's not
        // reclaimed when container_slot goes out of scope.
        container_slot.release();

        Ok(Box::new(sel4_bundle) as _)
    }
    fn stop(&mut self, bundle_impl: &mut dyn BundleImplInterface) -> Result<(), ProcessManagerError> {
        trace!("ProcessManagerInterface::stop");

        // 0. Assume thread is running (caller verifies)
        // 1. Notify application so it can do cleanup; e.g. ask the
        //    MLCoordinator to stop any ML workloads
        // 2. Wait some period of time for an ack from application
        // 3. Stop thread
        // 4. Reclaim seL4 resources: TCB, VSpace, memory, capabilities, etc.
        // TODO(sleffler): fill-in 1+2
        bundle_impl.stop()
    }
}
<commit_msg>kata-proc-manager: small code shuffle<commit_after>//! Kata OS process management support

#![cfg_attr(not(test), no_std)]

extern crate alloc;
use alloc::string::String;
use alloc::boxed::Box;
use kata_memory_interface::ObjDescBundle;
use kata_os_common::cspace_slot::CSpaceSlot;
use kata_proc_interface::Bundle;
use kata_proc_interface::BundleImplInterface;
use kata_proc_interface::BundleIdArray;
use kata_proc_interface::PackageManagementInterface;
use kata_proc_interface::ProcessControlInterface;
use kata_proc_interface::ProcessManagerError;
use kata_proc_interface::ProcessManagerInterface;
use kata_security_interface::kata_security_install;
use kata_security_interface::kata_security_load_application;
use kata_security_interface::kata_security_uninstall;
use log::trace;
use spin::Mutex;

mod sel4bundle;
use sel4bundle::seL4BundleImpl;

mod proc_manager;
pub use proc_manager::ProcessManager;

// NB: KATA_PROC cannot be used before setup is completed with a call to init()
#[cfg(not(test))]
pub static mut KATA_PROC: KataProcManager = KataProcManager::empty();

// KataProcManager bundles an instance of the ProcessManager that operates
// on KataOS interfaces and synchronizes public use with a Mutex. There is
// a two-step dance to setup an instance because we want KATA_PROC static
// and ProcessManager is incapable of supplying a const fn due it's use of
// hashbrown::HashMap.
pub struct KataProcManager {
    manager: Mutex<Option<ProcessManager>>,
}
impl KataProcManager {
    // Constructs a partially-initialized instance; to complete call init().
    // This is needed because we need a const fn for static setup and with
    // that constraint we cannot reference self.interface.
    const fn empty() -> KataProcManager {
        KataProcManager {
            manager: Mutex::new(None),
        }
    }

    // Finishes the setup started by empty():
    pub fn init(&self) {
        *self.manager.lock() = Some(ProcessManager::new(KataManagerInterface));
    }

    // Returns the bundle capacity.
    pub fn capacity(&self) -> usize {
        self.manager.lock().as_ref().unwrap().capacity()
    }
}
// These just lock accesses and handle the necessary indirection.
impl PackageManagementInterface for KataProcManager {
    fn install(
        &mut self,
        pkg_contents: &ObjDescBundle,
    ) -> Result<String, ProcessManagerError> {
        self.manager
            .lock()
            .as_mut()
            .unwrap()
            .install(pkg_contents)
    }
    fn uninstall(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        self.manager.lock().as_mut().unwrap().uninstall(bundle_id)
    }
}
impl ProcessControlInterface for KataProcManager {
    fn start(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        self.manager.lock().as_mut().unwrap().start(bundle_id)
    }
    fn stop(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        self.manager.lock().as_mut().unwrap().stop(bundle_id)
    }
    fn get_running_bundles(&self) -> Result<BundleIdArray, ProcessManagerError> {
        self.manager.lock().as_ref().unwrap().get_running_bundles()
    }
}

struct KataManagerInterface;
impl ProcessManagerInterface for KataManagerInterface {
    fn install(
        &mut self,
        pkg_contents: &ObjDescBundle,
    ) -> Result<String, ProcessManagerError> {
        trace!("ProcessManagerInterface::install pkg_contents {}", pkg_contents);

        // Package contains: application manifest, application binary, and
        // (optional) ML workload binary to run on vector core.
        // Manifest contains bundle_id.
        // Resulting flash file/pathname is fixed (1 app / bundle), only need bundle_id.
        // Pass opaque package contents through; get back bundle_id.

        // This is handled by the SecurityCoordinator.
        Ok(kata_security_install(pkg_contents)?)
    }
    fn uninstall(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
        trace!("ProcessManagerInterface::uninstall bundle_id {}", bundle_id);

        // NB: the caller has already checked no running application exists
        // NB: the Security Core is assumed to invalidate/remove any kv store

        // This is handled by the SecurityCoordinator.
        Ok(kata_security_uninstall(bundle_id)?)
    }
    fn start(&mut self, bundle: &Bundle) -> Result<Box<dyn BundleImplInterface>, ProcessManagerError> {
        trace!("ProcessManagerInterface::start {:?}", bundle);

        // Design doc says:
        // 1. Ask security core for application footprint with SizeBuffer
        // 2. Ask security core for manifest (maybe piggyback on SizeBuffer)
        //    and parse for necessary info (e.g. whether kv Storage is
        //    required, other privileges/capabilities)
        // 3. Ask MemoryManager for shared memory pages for the application
        //    (model handled separately by MlCoordinator since we do not know
        //    which model will be used)
        // 4. Allocate other seL4 resources:
        //     - VSpace, TCB & necessary capabiltiies
        // 5. Ask security core to VerifyAndLoad app into shared memory pages
        // 6. Complete seL4 setup:
        //     - Setup application system context and start thread
        //     - Badge seL4 recv cap w/ bundle_id for (optional) StorageManager
        //       access
        // What we do atm is:
        // 1. Ask SecurityCoordinator to return the application contents to load.
        //    Data are delivered as a read-only ObjDescBundle ready to copy into
        //    the VSpace.
        // 2. Do 4+6 with BundleImplInterface::start.

        // TODO(sleffler): awkward container_slot ownership
        let mut container_slot = CSpaceSlot::new();
        let bundle_frames =
            kata_security_load_application(&bundle.app_id, &container_slot)?;
        let mut sel4_bundle = seL4BundleImpl::new(bundle, &bundle_frames)?;
        // sel4_bundle owns container_slot now; release our ref so it's not
        // reclaimed when container_slot goes out of scope.
        container_slot.release();

        sel4_bundle.start()?;

        Ok(Box::new(sel4_bundle) as _)
    }
    fn stop(&mut self, bundle_impl: &mut dyn BundleImplInterface) -> Result<(), ProcessManagerError> {
        trace!("ProcessManagerInterface::stop");

        // 0. Assume thread is running (caller verifies)
        // 1. Notify application so it can do cleanup; e.g. ask the
        //    MLCoordinator to stop any ML workloads
        // 2. Wait some period of time for an ack from application
        // 3. Stop thread
        // 4. Reclaim seL4 resources: TCB, VSpace, memory, capabilities, etc.
        // TODO(sleffler): fill-in 1+2
        bundle_impl.stop()
    }
}
<|endoftext|>


<commit_before>use nom_lua53;
use nom_lua53::Statement;
use nom_lua53::stat_expr_types::Block;
use types::LuaState;
use expression;
use std;
use super::{Result, var_to_string};
use super::types::LuaValue;

#[derive(PartialEq)]
pub enum FlowControl {
    None,
    Return(Vec<LuaValue>),
    Break,
}

pub fn exec_statement(stmt: &Statement, ctx: &mut LuaState) -> Result<FlowControl> {
    match stmt {
        &Statement::LVarAssign(ref ass) => {
            let values = ass.vals
                .as_ref()
                .expect("There should be some values. Why isn't there any value?!");
            for (var, val) in ass.vars.iter().zip(values.iter()) {
                let computed_value = expression::eval_expr(val, &ctx);
                let local_scope = ctx.get_mutable_local_scope().unwrap();
                local_scope.set_string(var_to_string(var), &computed_value.unwrap());
            };
            Ok(FlowControl::None)
        }
        &Statement::Assignment(ref ass) => {
            let mut values = Vec::with_capacity(ass.vals.len());
            for exp in ass.vals.iter() {
                values.push(expression::eval_expr(&exp, &ctx)?);
            }

            for (prefexp, val) in ass.vars.iter().zip(values.drain(..)) {
                let assignment = expression::prefixexp::resolve_prefix_expr(
                    &prefexp.prefix,
                    &prefexp.suffix_chain,
                    ctx,
                )?;
                assignment.environment.set(&assignment.index, &val)?;
            }
            println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
            Ok(FlowControl::None)
        }
        &Statement::Semicolon => {
            Ok(FlowControl::None)
        }
        &Statement::Ite(ref ite) => {
            exec_if_then_else(ite, ctx)
        }
        &Statement::While(ref blk) => {
            exec_while(blk, ctx)
        }
        &Statement::Break => Ok(FlowControl::Break),
        _ => {Ok(FlowControl::None)}
    }
}

pub fn exec_block(block: &Block, ctx: &mut LuaState) -> Result<FlowControl> {
    ctx.push_scope();
    let mut ret = FlowControl::None;
    for stmt in block.stmts.iter() {
        match exec_statement(&stmt, ctx)? {
            FlowControl::Break => { ret =  FlowControl::Break; break },
            FlowControl::Return(val) => { ret = FlowControl::Return(val); break },
            FlowControl::None => {}
        }
    };

    if ret != FlowControl::None {
        if let Some(ref expressions) = block.ret_stmt {
            let mut results = std::vec::Vec::with_capacity(expressions.len());
            for exp in expressions {
                results.push(expression::eval_expr(exp, ctx)?)
            }
            ret = FlowControl::Return(results)
        }
    }

    ctx.pop_scope();
    Ok(ret)
}

pub fn exec_if_then_else(ite: &nom_lua53::IfThenElse, ctx: &mut LuaState) -> Result<FlowControl> {
    if expression::boolean_coercion(&expression::eval_expr(&ite.cond, ctx)?) {
        exec_block(&ite.then_blk, ctx)
    } else {
        match ite.elseifs.iter().fold(None,
         |current, &(ref exp, ref block)|
          if current.is_none() {
              match expression::eval_expr(&exp, ctx) {
                  Ok(_) => Some(exec_block(&block, ctx)),
                  Err(err) => Some(Err(err))
              }
          } else { current }) {
              Some(val) => val,
              None => match &ite.else_blk {
                  &Some(ref blk) => exec_block(blk, ctx),
                  &None => Ok(FlowControl::None)
              }
          }
    }
}

pub fn exec_while(blk: &nom_lua53::WhileBlock, ctx: &mut LuaState) -> Result<FlowControl> {
    while expression::boolean_coercion(&expression::eval_expr(&blk.cond, ctx)?) {
        let disrupt = exec_block(&blk.block, ctx)?;
        match disrupt {
            FlowControl::Return(val) => {
                return Ok(FlowControl::Return(val))
            }
            FlowControl::Break => {
                return Ok(FlowControl::None)
            }
            FlowControl::None => {}
        }
    }
    return Ok(FlowControl::None)
}<commit_msg>Implement repeat loops<commit_after>use nom_lua53;
use nom_lua53::Statement;
use nom_lua53::RepeatBlock;
use nom_lua53::stat_expr_types::Block;
use types::LuaState;
use expression;
use std;
use super::{Result, var_to_string};
use super::types::LuaValue;

#[derive(PartialEq)]
pub enum FlowControl {
    None,
    Return(Vec<LuaValue>),
    Break,
}

pub fn exec_statement(stmt: &Statement, ctx: &mut LuaState) -> Result<FlowControl> {
    match stmt {
        &Statement::LVarAssign(ref ass) => {
            let values = ass.vals
                .as_ref()
                .expect("There should be some values. Why isn't there any value?!");
            for (var, val) in ass.vars.iter().zip(values.iter()) {
                let computed_value = expression::eval_expr(val, &ctx);
                let local_scope = ctx.get_mutable_local_scope().unwrap();
                local_scope.set_string(var_to_string(var), &computed_value.unwrap());
            };
            Ok(FlowControl::None)
        }
        &Statement::Assignment(ref ass) => {
            let mut values = Vec::with_capacity(ass.vals.len());
            for exp in ass.vals.iter() {
                values.push(expression::eval_expr(&exp, &ctx)?);
            }

            for (prefexp, val) in ass.vars.iter().zip(values.drain(..)) {
                let assignment = expression::prefixexp::resolve_prefix_expr(
                    &prefexp.prefix,
                    &prefexp.suffix_chain,
                    ctx,
                )?;
                assignment.environment.set(&assignment.index, &val)?;
            }
            println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
            Ok(FlowControl::None)
        }
        &Statement::Semicolon => {
            Ok(FlowControl::None)
        }
        &Statement::Ite(ref ite) => {
            exec_if_then_else(ite, ctx)
        }
        &Statement::While(ref blk) => {
            exec_while(blk, ctx)
        }
        &Statement::Repeat(ref blk) => {
            exec_repeat(blk, ctx)
        }
        &Statement::Break => Ok(FlowControl::Break),
        _ => {Ok(FlowControl::None)}
    }
}

pub fn exec_block(block: &Block, ctx: &mut LuaState) -> Result<FlowControl> {
    ctx.push_scope();
    let mut ret = FlowControl::None;
    for stmt in block.stmts.iter() {
        match exec_statement(&stmt, ctx)? {
            FlowControl::Break => { ret =  FlowControl::Break; break },
            FlowControl::Return(val) => { ret = FlowControl::Return(val); break },
            FlowControl::None => {}
        }
    };

    if ret != FlowControl::None {
        if let Some(ref expressions) = block.ret_stmt {
            let mut results = std::vec::Vec::with_capacity(expressions.len());
            for exp in expressions {
                results.push(expression::eval_expr(exp, ctx)?)
            }
            ret = FlowControl::Return(results)
        }
    }

    ctx.pop_scope();
    Ok(ret)
}

pub fn exec_if_then_else(ite: &nom_lua53::IfThenElse, ctx: &mut LuaState) -> Result<FlowControl> {
    if expression::boolean_coercion(&expression::eval_expr(&ite.cond, ctx)?) {
        exec_block(&ite.then_blk, ctx)
    } else {
        match ite.elseifs.iter().fold(None,
         |current, &(ref exp, ref block)|
          if current.is_none() {
              match expression::eval_expr(&exp, ctx) {
                  Ok(_) => Some(exec_block(&block, ctx)),
                  Err(err) => Some(Err(err))
              }
          } else { current }) {
              Some(val) => val,
              None => match &ite.else_blk {
                  &Some(ref blk) => exec_block(blk, ctx),
                  &None => Ok(FlowControl::None)
              }
          }
    }
}

pub fn exec_while(blk: &nom_lua53::WhileBlock, ctx: &mut LuaState) -> Result<FlowControl> {
    while expression::boolean_coercion(&expression::eval_expr(&blk.cond, ctx)?) {
        let disrupt = exec_block(&blk.block, ctx)?;
        match disrupt {
            FlowControl::Return(val) => {
                return Ok(FlowControl::Return(val))
            }
            FlowControl::Break => {
                return Ok(FlowControl::None)
            }
            FlowControl::None => {}
        }
    }
    return Ok(FlowControl::None)
}

pub fn exec_repeat(blk: &RepeatBlock, ctx: &mut LuaState) -> Result<FlowControl> {
    let mut ret = FlowControl::None;
    loop {
        ctx.push_scope();
        for stmt in blk.block.stmts.iter() {
            match exec_statement(&stmt, ctx)? {
                FlowControl::Break => { ret =  FlowControl::Break; break },
                FlowControl::Return(val) => { ret = FlowControl::Return(val); break },
                FlowControl::None => {}
            }
        };
        if ret != FlowControl::None {
            if let Some(ref expressions) = blk.block.ret_stmt {
                let mut results = std::vec::Vec::with_capacity(expressions.len());
                for exp in expressions {
                    results.push(expression::eval_expr(exp, ctx)?)
                }
                ret = FlowControl::Return(results);
            }
        }
        if ret != FlowControl::None || expression::boolean_coercion(&expression::eval_expr(&blk.cond, ctx)?) {
            ctx.pop_scope();
            break
        }
        ctx.pop_scope();
    };
    Ok(ret)
}<|endoftext|>


<commit_before><commit_msg>Renumber and reoganize SIMD opcodes<commit_after><|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimaglink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::process::exit;
use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagutil::trace::trace_error;
use libimaglink::external::ExternalLinker;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let name = "imag-link";
    let version = &version!()[..];
    let about = "Link entries";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => {
                    warn!("No commandline call");
                    exit(1);
                },
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimaglink::internal::InternalLinker;
    use libimagutil::trace::trace_error;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(",") {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(e) => {
                    e.get_internal_links()
                        .map(|links| {
                            let mut i = 0;
                            for link in links.iter().map(|l| l.to_str()).filter_map(|x| x) {
                                println!("{: <3}: {}", i, link);
                                i += 1;
                            }
                        })
                        .map_err(|e| trace_error(&e))
                        .ok();
                },

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn!("No 'from' entry");
                exit(1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn!("No 'to' entry");
                exit(1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(e) => Some(e),
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(e) => v.push(e),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<FileLockEntry<'a>, StoreError> {
    use libimagstore::storeid::build_entry_path;
    build_entry_path(rt.store(), name)
        .and_then(|path| rt.store().retrieve(path))
}

fn handle_external_linking(rt: &Runtime) {
    use libimagutil::trace::trace_error;

    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error(&entry.err().unwrap());
        exit(1);
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("add").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        debug!("URL parsing error...");
        trace_error(&link.err().unwrap());
        debug!("Exiting");
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.add_external_link(store, link) {
        debug!("Error while adding external link...");
        trace_error(&e);
    } else {
        debug!("Everything worked well");
        info!("Ok");
    }
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("remove").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        trace_error(&link.err().unwrap());
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.remove_external_link(store, link) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(",")
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    if let Err(e) = entry.set_external_links(store, links) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    let res = entry.get_external_links(store)
        .and_then(|links| {
            let mut i = 0;
            for link in links {
                println!("{: <3}: {}", i, link);
                i += 1;
            }
            Ok(())
        });

    match res {
        Err(e) => {
            trace_error(&e);
        },
        Ok(_) => {
            info!("Ok");
        },
    }
}

<commit_msg>Enable lints<commit_after>#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimaglink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::process::exit;
use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagutil::trace::trace_error;
use libimaglink::external::ExternalLinker;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let name = "imag-link";
    let version = &version!()[..];
    let about = "Link entries";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => {
                    warn!("No commandline call");
                    exit(1);
                },
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimaglink::internal::InternalLinker;
    use libimagutil::trace::trace_error;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(",") {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(e) => {
                    e.get_internal_links()
                        .map(|links| {
                            let mut i = 0;
                            for link in links.iter().map(|l| l.to_str()).filter_map(|x| x) {
                                println!("{: <3}: {}", i, link);
                                i += 1;
                            }
                        })
                        .map_err(|e| trace_error(&e))
                        .ok();
                },

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn!("No 'from' entry");
                exit(1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn!("No 'to' entry");
                exit(1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(e) => Some(e),
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(e) => v.push(e),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<FileLockEntry<'a>, StoreError> {
    use libimagstore::storeid::build_entry_path;
    build_entry_path(rt.store(), name)
        .and_then(|path| rt.store().retrieve(path))
}

fn handle_external_linking(rt: &Runtime) {
    use libimagutil::trace::trace_error;

    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error(&entry.err().unwrap());
        exit(1);
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("add").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        debug!("URL parsing error...");
        trace_error(&link.err().unwrap());
        debug!("Exiting");
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.add_external_link(store, link) {
        debug!("Error while adding external link...");
        trace_error(&e);
    } else {
        debug!("Everything worked well");
        info!("Ok");
    }
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("remove").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        trace_error(&link.err().unwrap());
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.remove_external_link(store, link) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(",")
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    if let Err(e) = entry.set_external_links(store, links) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    let res = entry.get_external_links(store)
        .and_then(|links| {
            let mut i = 0;
            for link in links {
                println!("{: <3}: {}", i, link);
                i += 1;
            }
            Ok(())
        });

    match res {
        Err(e) => {
            trace_error(&e);
        },
        Ok(_) => {
            info!("Ok");
        },
    }
}

<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

extern crate clap;
#[macro_use] extern crate log;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagentryview;
#[macro_use] extern crate libimagerror;

use std::process::exit;
use std::path::PathBuf;

use libimagrt::setup::generate_runtime_setup;
use libimagerror::trace::trace_error_exit;
use libimagentryview::builtin::stdout::StdoutViewer;
use libimagentryview::viewer::Viewer;

mod ui;
mod editor;

use ui::build_ui;
use editor::Editor;

fn main() {
    let rt = generate_runtime_setup( "imag-view",
                                     &version!()[..],
                                     "View entries (readonly)",
                                     build_ui);

    let entry_id     = rt.cli().value_of("id").unwrap(); // enforced by clap
    let view_header  = rt.cli().is_present("view-header");
    let view_content = rt.cli().is_present("view-content");

    let entry = match rt.store().get(PathBuf::from(entry_id)) {
        Ok(Some(fle)) => fle,
        Ok(None) => {
            error!("Cannot get {}, there is no such id in the store", entry_id);
            exit(1);
        }
        Err(e) => {
            trace_error_exit(&e, 1);
        }
    };

    let res = {
        match rt.cli().subcommand_matches("view-in") {
            None => {
                debug!("No commandline call");
                debug!("Assuming to view in cli (stdout)");
            },
            Some(s) => {
                if s.is_present("view-in-stdout") {
                } else if s.is_present("view-in-ui") {
                    warn!("Viewing in UI is currently not supported, switch to stdout");
                } else if s.is_present("view-in-browser") {
                    warn!("Viewing in browser is currently not supported, switch to stdout");
                } else if s.is_present("view-in-texteditor") {
                    if let Err(e) = Editor::new(&rt, &entry).show() {
                        error!("Cannot view in editor: {}", e);
                        trace_error_exit(&e, 1);
                    }
                } else if s.is_present("view-in-custom") {
                    warn!("Viewing in custom is currently not supported, switch to stdout");
                }
            },
        };

        StdoutViewer::new(view_header, view_content).view_entry(&entry)
    };

    if let Err(e) = res {
        trace_error_exit(&e, 1);
    }
}

<commit_msg>Remove unused macro_use import<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

extern crate clap;
#[macro_use] extern crate log;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate libimagentryview;
extern crate libimagerror;
extern crate libimagrt;
extern crate libimagstore;

use std::process::exit;
use std::path::PathBuf;

use libimagrt::setup::generate_runtime_setup;
use libimagerror::trace::trace_error_exit;
use libimagentryview::builtin::stdout::StdoutViewer;
use libimagentryview::viewer::Viewer;

mod ui;
mod editor;

use ui::build_ui;
use editor::Editor;

fn main() {
    let rt = generate_runtime_setup( "imag-view",
                                     &version!()[..],
                                     "View entries (readonly)",
                                     build_ui);

    let entry_id     = rt.cli().value_of("id").unwrap(); // enforced by clap
    let view_header  = rt.cli().is_present("view-header");
    let view_content = rt.cli().is_present("view-content");

    let entry = match rt.store().get(PathBuf::from(entry_id)) {
        Ok(Some(fle)) => fle,
        Ok(None) => {
            error!("Cannot get {}, there is no such id in the store", entry_id);
            exit(1);
        }
        Err(e) => {
            trace_error_exit(&e, 1);
        }
    };

    let res = {
        match rt.cli().subcommand_matches("view-in") {
            None => {
                debug!("No commandline call");
                debug!("Assuming to view in cli (stdout)");
            },
            Some(s) => {
                if s.is_present("view-in-stdout") {
                } else if s.is_present("view-in-ui") {
                    warn!("Viewing in UI is currently not supported, switch to stdout");
                } else if s.is_present("view-in-browser") {
                    warn!("Viewing in browser is currently not supported, switch to stdout");
                } else if s.is_present("view-in-texteditor") {
                    if let Err(e) = Editor::new(&rt, &entry).show() {
                        error!("Cannot view in editor: {}", e);
                        trace_error_exit(&e, 1);
                    }
                } else if s.is_present("view-in-custom") {
                    warn!("Viewing in custom is currently not supported, switch to stdout");
                }
            },
        };

        StdoutViewer::new(view_header, view_content).view_entry(&entry)
    };

    if let Err(e) = res {
        trace_error_exit(&e, 1);
    }
}

<|endoftext|>


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

use error::{Error,Result};
use token::Token;
use html::HtmlRenderer;
use book::Book;
use number::Number;
use zipper::Zipper;
use templates::epub::*;
use templates::epub3;

use mustache;
use chrono;
use uuid;

use std::io::{Read,Write};
use std::path::Path;
use std::fs::File;
use std::borrow::Cow;
use mime_guess::guess_mime_type_opt;

/// Renderer for Epub
///
/// Uses part of the HTML renderer
pub struct EpubRenderer<'a> {
    book: &'a Book,
    toc: Vec<String>,
    html: HtmlRenderer<'a>,
}

impl<'a> EpubRenderer<'a> {
    /// Creates a new Epub renderer
    pub fn new(book: &'a Book) -> EpubRenderer<'a> {
        let mut html = HtmlRenderer::new(book);
        html.toc.numbered(true);
        html.handler.set_images_mapping(true);
        EpubRenderer {
            book: book,
            html: html,
            toc: vec!(),
        }
    }

    /// Render a book
    pub fn render_book(&mut self) -> Result<String> {
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.html.handler.add_link(filename.clone(), filenamer(i));
        }

        let mut zipper = try!(Zipper::new(&self.book.options.get_path("temp_dir").unwrap()));

        // Write mimetype
        try!(zipper.write("mimetype", b"application/epub+zip", true));

        // Write chapters
        for (i, &(n, ref v)) in self.book.chapters.iter().enumerate() {
            self.html.filename = filenamer(i);
            self.html.current_hide = false;
            let book_numbering = self.book.options.get_i32("numbering").unwrap();
            match n {
                Number::Unnumbered => self.html.current_numbering = 0,
                Number::Default => self.html.current_numbering = book_numbering,
                Number::Specified(n) => {
                    self.html.current_numbering = book_numbering;
                    self.html.current_chapter[0] = n - 1;
                },
                Number::Hidden => {
                    self.html.current_numbering = 0;
                    self.html.current_hide = true;
                }
            }
            let chapter = try!(self.render_chapter(v));

            try!(zipper.write(&filenamer(i), &chapter.as_bytes(), true));
        }

        // Render the CSS file and write it
        let template_css = mustache::compile_str(try!(self.book.get_template("epub.css")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_bool(self.book.options.get_str("lang").unwrap(), true)
            .build();
        let mut res:Vec<u8> = vec!();
        template_css.render_data(&mut res, &data);
        let css = String::from_utf8_lossy(&res);
        try!(zipper.write("stylesheet.css",
                          css.as_bytes(), true));

        // Write titlepage
        try!(zipper.write("title_page.xhtml", &try!(self.render_titlepage()).as_bytes(), true));

        // Write file for ibook (why?)
        try!(zipper.write("META-INF/com.apple.ibooks.display-options.xml", IBOOK.as_bytes(), true));

        // Write container.xml
        try!(zipper.write("META-INF/container.xml", CONTAINER.as_bytes(), true));

        // Write nav.xhtml
        try!(zipper.write("nav.xhtml", &try!(self.render_nav()).as_bytes(), true));

        // Write content.opf
        try!(zipper.write("content.opf", &try!(self.render_opf()).as_bytes(), true));

        // Write toc.ncx
        try!(zipper.write("toc.ncx", &try!(self.render_toc()).as_bytes(), true));

        // Write cover.xhtml (if needs be)
        if self.book.options.get_path("cover").is_ok() {
            try!(zipper.write("cover.xhtml", &try!(self.render_cover()).as_bytes(), true));
        }

        // Write all images (including cover)
        for (source, dest) in self.html.handler.images_mapping() {
            let mut f = try!(File::open(self.book.root.join(source)).map_err(|_| Error::FileNotFound(source.to_owned())));
            let mut content = vec!();
            try!(f.read_to_end(&mut content).map_err(|_| Error::Render("error while reading image file")));
            try!(zipper.write(dest, &content, true));
        }

        if let Ok(epub_file) = self.book.options.get_path("output.epub") {
            let res = try!(zipper.generate_epub(self.book.options.get_str("zip.command").unwrap(), &epub_file));
            Ok(res)
        } else {
            Err(Error::Render("no output epub file specified in book config"))
        }
    }

    /// Render the titlepgae
    fn render_titlepage(&self) -> Result<String> {
        let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::TITLE} else {TITLE});
        let data = self.book.get_mapbuilder("none")
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in titlepage was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// Render toc.ncx
    fn render_toc(&self) -> Result<String> {
        let mut nav_points = String::new();

        for (n, ref title) in self.toc.iter().enumerate() {
            let filename = filenamer(n);
            let id = format!("navPoint-{}", n + 1);
            nav_points.push_str(&format!(
"   <navPoint id=\"{}\">
      <navLabel>
        <text>{}</text>
      </navLabel>
      <content src = \"{}\" />
    </navPoint>\n", id, title, filename));
        }
        let template = mustache::compile_str(TOC);
        let data = self.book.get_mapbuilder("none")
            .insert_str("nav_points", nav_points)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in toc.ncx was not valid utf-8")),
            Ok(res) => Ok(res)
        }
    }

    /// Render content.opf
    fn render_opf(&mut self) -> Result<String> {
        // Optional metadata
        let mut cover_xhtml = String::new();
        let mut optional = String::new();
        if let Ok(s) = self.book.options.get_str("description") {
            optional.push_str(&format!("<dc:description>{}</dc:description>\n", s));
        }
        if let Ok(s) = self.book.options.get_str("subject") {
            optional.push_str(&format!("<dc:subject>{}</dc:subject>\n", s));
        }
        if let Ok(ref s) = self.book.options.get_path("cover") {
            optional.push_str(&format!("<meta name = \"cover\" content = \"{}\" />\n",
                                       self.html.handler.map_image(Cow::Borrowed(s))));
            cover_xhtml.push_str(&format!("<reference type=\"cover\" title=\"Cover\" href=\"cover.xhtml\" />"));
        }

        // date
        let date = chrono::UTC::now().format("%Y-%m-%dT%H:%M:%SZ");

        // uuid
        let uuid = uuid::Uuid::new_v4().to_urn_string();

        let mut items = String::new();
        let mut itemrefs = String::new();
        let mut coverref = String::new();
        if self.book.options.get("cover").is_ok() {
            items.push_str("<item id = \"cover_xhtml\" href = \"cover.xhtml\" media-type = \"application/xhtml+xml\" />\n");
            coverref.push_str("<itemref idref = \"cover_xhtml\" />");
        }
        for n in 0..self.toc.len() {
            let filename = filenamer(n);
            items.push_str(&format!("<item id = \"{}\" href = \"{}\" media-type=\"application/xhtml+xml\" />\n",
                                    to_id(&filename),
                                    filename));
            itemrefs.push_str(&format!("<itemref idref=\"{}\" />\n", to_id(&filename)));
        }

        // put the images in the manifest too
        for image in self.html.handler.images_mapping().values() {
            let format = self.get_format(image);
            items.push_str(&format!("<item media-type = \"image/{}\" id = \"{}\" href = \"{}\" />\n",
                                    format, to_id(image), image));
        }

        let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::OPF} else {OPF});
        let data = self.book.get_mapbuilder("none")
            .insert_str("optional", optional)
            .insert_str("items", items)
            .insert_str("itemrefs", itemrefs)
            .insert_str("date", date)
            .insert_str("uuid", uuid)
            .insert_str("cover_xhtml", cover_xhtml)
            .insert_str("coverref", coverref)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in content.opf was not valid utf-8")),
            Ok(res) => Ok(res)
        }
    }

    /// Render cover.xhtml
    fn render_cover(&mut self) -> Result<String> {
        if let Ok(cover) = self.book.options.get_path("cover") {
            let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::COVER} else {COVER});
            let data = self.book.get_mapbuilder("none")
                .insert_str("cover", self.html.handler.map_image(Cow::Owned(cover)).into_owned())
                .build();
            let mut res:Vec<u8> = vec!();
            template.render_data(&mut res, &data);
            match String::from_utf8(res) {
                Err(_) => Err(Error::Render("generated HTML for cover.xhtml was not utf-8 valid")),
                Ok(res) => Ok(res)
            }
        } else {
            panic!("Why is this method called if cover is None???");
        }
    }

    /// Render nav.xhtml
    fn render_nav(&self) -> Result<String> {
        let content = self.html.toc.render();

        let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::NAV} else {NAV});
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in nav.xhtml was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// Render a chapter
    pub fn render_chapter(&mut self, v: &[Token]) -> Result<String> {
        let mut content = String::new();
        let mut title = String::new();

        for token in v {
            content.push_str(&self.parse_token(&token, &mut title));
            self.html.render_side_notes(&mut content);
        }
        self.html.render_end_notes(&mut content);

        if title.is_empty() {
            if self.html.current_numbering >= 1 {
                let number = self.html.current_chapter[0] + 1;
                title = try!(self.book.get_header(number, ""));
            }
        }
        self.toc.push(title.clone());

        let template = mustache::compile_str(try!(self.book.get_template("epub.template")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("chapter_title", title)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    fn parse_token(&mut self, token: &Token, title: &mut String) -> String {
        match *token {
            Token::Header(n, ref vec) => {
                if n == 1 {
                    if self.html.current_hide || self.html.current_numbering == 0 {
                        if title.is_empty() {
                            *title = self.html.render_vec(vec);
                        } else {
                            self.book.logger.warning("EPUB: detected two chapter titles inside the same markdown file...");
                            self.book.logger.warning("EPUB: ...in a file where chapter titles are not even rendered.");
                        }
                    } else {
                        let res = self.book.get_header(self.html.current_chapter[0] + 1, &self.html.render_vec(vec));
                        let s = res.unwrap();
                        if title.is_empty() {
                            *title = s;
                        } else {
                            self.book.logger.warning("EPUB: detected two chapters inside the same markdown file.");
                            self.book.logger.warning(format!("EPUB: conflict between: {} and {}", title, s));
                        }
                    }
                }
                self.html.parse_token(token)
            },
            _ => self.html.parse_token(token)
        }
    }

    // Get the format of an image file, based on its extension
    fn get_format(&self, s: &str) -> String {
        let opt = guess_mime_type_opt(s);
        match opt {
            Some(s) => s.to_string(),
            None => {
                self.book.logger.warning(format!("EPUB: could not guess the format of {} based on extension. Assuming png.", s));
                String::from("png")
            }
        }
    }
}

// generate an id compatible string, replacing / and . by _
fn to_id(s: &str) -> String {
    s.replace(".", "_").replace("/", "_")
}

/// Generate a file name given an int
fn filenamer(i: usize) -> String {
    format!("chapter_{:03}.xhtml", i)
}
<commit_msg>Remove unused import in epub.rs<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use error::{Error,Result};
use token::Token;
use html::HtmlRenderer;
use book::Book;
use number::Number;
use zipper::Zipper;
use templates::epub::*;
use templates::epub3;

use mustache;
use chrono;
use uuid;

use std::io::{Read,Write};
use std::fs::File;
use std::borrow::Cow;
use mime_guess::guess_mime_type_opt;

/// Renderer for Epub
///
/// Uses part of the HTML renderer
pub struct EpubRenderer<'a> {
    book: &'a Book,
    toc: Vec<String>,
    html: HtmlRenderer<'a>,
}

impl<'a> EpubRenderer<'a> {
    /// Creates a new Epub renderer
    pub fn new(book: &'a Book) -> EpubRenderer<'a> {
        let mut html = HtmlRenderer::new(book);
        html.toc.numbered(true);
        html.handler.set_images_mapping(true);
        EpubRenderer {
            book: book,
            html: html,
            toc: vec!(),
        }
    }

    /// Render a book
    pub fn render_book(&mut self) -> Result<String> {
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.html.handler.add_link(filename.clone(), filenamer(i));
        }

        let mut zipper = try!(Zipper::new(&self.book.options.get_path("temp_dir").unwrap()));

        // Write mimetype
        try!(zipper.write("mimetype", b"application/epub+zip", true));

        // Write chapters
        for (i, &(n, ref v)) in self.book.chapters.iter().enumerate() {
            self.html.filename = filenamer(i);
            self.html.current_hide = false;
            let book_numbering = self.book.options.get_i32("numbering").unwrap();
            match n {
                Number::Unnumbered => self.html.current_numbering = 0,
                Number::Default => self.html.current_numbering = book_numbering,
                Number::Specified(n) => {
                    self.html.current_numbering = book_numbering;
                    self.html.current_chapter[0] = n - 1;
                },
                Number::Hidden => {
                    self.html.current_numbering = 0;
                    self.html.current_hide = true;
                }
            }
            let chapter = try!(self.render_chapter(v));

            try!(zipper.write(&filenamer(i), &chapter.as_bytes(), true));
        }

        // Render the CSS file and write it
        let template_css = mustache::compile_str(try!(self.book.get_template("epub.css")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_bool(self.book.options.get_str("lang").unwrap(), true)
            .build();
        let mut res:Vec<u8> = vec!();
        template_css.render_data(&mut res, &data);
        let css = String::from_utf8_lossy(&res);
        try!(zipper.write("stylesheet.css",
                          css.as_bytes(), true));

        // Write titlepage
        try!(zipper.write("title_page.xhtml", &try!(self.render_titlepage()).as_bytes(), true));

        // Write file for ibook (why?)
        try!(zipper.write("META-INF/com.apple.ibooks.display-options.xml", IBOOK.as_bytes(), true));

        // Write container.xml
        try!(zipper.write("META-INF/container.xml", CONTAINER.as_bytes(), true));

        // Write nav.xhtml
        try!(zipper.write("nav.xhtml", &try!(self.render_nav()).as_bytes(), true));

        // Write content.opf
        try!(zipper.write("content.opf", &try!(self.render_opf()).as_bytes(), true));

        // Write toc.ncx
        try!(zipper.write("toc.ncx", &try!(self.render_toc()).as_bytes(), true));

        // Write cover.xhtml (if needs be)
        if self.book.options.get_path("cover").is_ok() {
            try!(zipper.write("cover.xhtml", &try!(self.render_cover()).as_bytes(), true));
        }

        // Write all images (including cover)
        for (source, dest) in self.html.handler.images_mapping() {
            let mut f = try!(File::open(self.book.root.join(source)).map_err(|_| Error::FileNotFound(source.to_owned())));
            let mut content = vec!();
            try!(f.read_to_end(&mut content).map_err(|_| Error::Render("error while reading image file")));
            try!(zipper.write(dest, &content, true));
        }

        if let Ok(epub_file) = self.book.options.get_path("output.epub") {
            let res = try!(zipper.generate_epub(self.book.options.get_str("zip.command").unwrap(), &epub_file));
            Ok(res)
        } else {
            Err(Error::Render("no output epub file specified in book config"))
        }
    }

    /// Render the titlepgae
    fn render_titlepage(&self) -> Result<String> {
        let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::TITLE} else {TITLE});
        let data = self.book.get_mapbuilder("none")
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in titlepage was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// Render toc.ncx
    fn render_toc(&self) -> Result<String> {
        let mut nav_points = String::new();

        for (n, ref title) in self.toc.iter().enumerate() {
            let filename = filenamer(n);
            let id = format!("navPoint-{}", n + 1);
            nav_points.push_str(&format!(
"   <navPoint id=\"{}\">
      <navLabel>
        <text>{}</text>
      </navLabel>
      <content src = \"{}\" />
    </navPoint>\n", id, title, filename));
        }
        let template = mustache::compile_str(TOC);
        let data = self.book.get_mapbuilder("none")
            .insert_str("nav_points", nav_points)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in toc.ncx was not valid utf-8")),
            Ok(res) => Ok(res)
        }
    }

    /// Render content.opf
    fn render_opf(&mut self) -> Result<String> {
        // Optional metadata
        let mut cover_xhtml = String::new();
        let mut optional = String::new();
        if let Ok(s) = self.book.options.get_str("description") {
            optional.push_str(&format!("<dc:description>{}</dc:description>\n", s));
        }
        if let Ok(s) = self.book.options.get_str("subject") {
            optional.push_str(&format!("<dc:subject>{}</dc:subject>\n", s));
        }
        if let Ok(ref s) = self.book.options.get_path("cover") {
            optional.push_str(&format!("<meta name = \"cover\" content = \"{}\" />\n",
                                       self.html.handler.map_image(Cow::Borrowed(s))));
            cover_xhtml.push_str(&format!("<reference type=\"cover\" title=\"Cover\" href=\"cover.xhtml\" />"));
        }

        // date
        let date = chrono::UTC::now().format("%Y-%m-%dT%H:%M:%SZ");

        // uuid
        let uuid = uuid::Uuid::new_v4().to_urn_string();

        let mut items = String::new();
        let mut itemrefs = String::new();
        let mut coverref = String::new();
        if self.book.options.get("cover").is_ok() {
            items.push_str("<item id = \"cover_xhtml\" href = \"cover.xhtml\" media-type = \"application/xhtml+xml\" />\n");
            coverref.push_str("<itemref idref = \"cover_xhtml\" />");
        }
        for n in 0..self.toc.len() {
            let filename = filenamer(n);
            items.push_str(&format!("<item id = \"{}\" href = \"{}\" media-type=\"application/xhtml+xml\" />\n",
                                    to_id(&filename),
                                    filename));
            itemrefs.push_str(&format!("<itemref idref=\"{}\" />\n", to_id(&filename)));
        }

        // put the images in the manifest too
        for image in self.html.handler.images_mapping().values() {
            let format = self.get_format(image);
            items.push_str(&format!("<item media-type = \"image/{}\" id = \"{}\" href = \"{}\" />\n",
                                    format, to_id(image), image));
        }

        let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::OPF} else {OPF});
        let data = self.book.get_mapbuilder("none")
            .insert_str("optional", optional)
            .insert_str("items", items)
            .insert_str("itemrefs", itemrefs)
            .insert_str("date", date)
            .insert_str("uuid", uuid)
            .insert_str("cover_xhtml", cover_xhtml)
            .insert_str("coverref", coverref)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in content.opf was not valid utf-8")),
            Ok(res) => Ok(res)
        }
    }

    /// Render cover.xhtml
    fn render_cover(&mut self) -> Result<String> {
        if let Ok(cover) = self.book.options.get_path("cover") {
            let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::COVER} else {COVER});
            let data = self.book.get_mapbuilder("none")
                .insert_str("cover", self.html.handler.map_image(Cow::Owned(cover)).into_owned())
                .build();
            let mut res:Vec<u8> = vec!();
            template.render_data(&mut res, &data);
            match String::from_utf8(res) {
                Err(_) => Err(Error::Render("generated HTML for cover.xhtml was not utf-8 valid")),
                Ok(res) => Ok(res)
            }
        } else {
            panic!("Why is this method called if cover is None???");
        }
    }

    /// Render nav.xhtml
    fn render_nav(&self) -> Result<String> {
        let content = self.html.toc.render();

        let template = mustache::compile_str(if self.book.options.get_i32("epub.version").unwrap() == 3 {epub3::NAV} else {NAV});
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML in nav.xhtml was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// Render a chapter
    pub fn render_chapter(&mut self, v: &[Token]) -> Result<String> {
        let mut content = String::new();
        let mut title = String::new();

        for token in v {
            content.push_str(&self.parse_token(&token, &mut title));
            self.html.render_side_notes(&mut content);
        }
        self.html.render_end_notes(&mut content);

        if title.is_empty() {
            if self.html.current_numbering >= 1 {
                let number = self.html.current_chapter[0] + 1;
                title = try!(self.book.get_header(number, ""));
            }
        }
        self.toc.push(title.clone());

        let template = mustache::compile_str(try!(self.book.get_template("epub.template")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("chapter_title", title)
            .build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    fn parse_token(&mut self, token: &Token, title: &mut String) -> String {
        match *token {
            Token::Header(n, ref vec) => {
                if n == 1 {
                    if self.html.current_hide || self.html.current_numbering == 0 {
                        if title.is_empty() {
                            *title = self.html.render_vec(vec);
                        } else {
                            self.book.logger.warning("EPUB: detected two chapter titles inside the same markdown file...");
                            self.book.logger.warning("EPUB: ...in a file where chapter titles are not even rendered.");
                        }
                    } else {
                        let res = self.book.get_header(self.html.current_chapter[0] + 1, &self.html.render_vec(vec));
                        let s = res.unwrap();
                        if title.is_empty() {
                            *title = s;
                        } else {
                            self.book.logger.warning("EPUB: detected two chapters inside the same markdown file.");
                            self.book.logger.warning(format!("EPUB: conflict between: {} and {}", title, s));
                        }
                    }
                }
                self.html.parse_token(token)
            },
            _ => self.html.parse_token(token)
        }
    }

    // Get the format of an image file, based on its extension
    fn get_format(&self, s: &str) -> String {
        let opt = guess_mime_type_opt(s);
        match opt {
            Some(s) => s.to_string(),
            None => {
                self.book.logger.warning(format!("EPUB: could not guess the format of {} based on extension. Assuming png.", s));
                String::from("png")
            }
        }
    }
}

// generate an id compatible string, replacing / and . by _
fn to_id(s: &str) -> String {
    s.replace(".", "_").replace("/", "_")
}

/// Generate a file name given an int
fn filenamer(i: usize) -> String {
    format!("chapter_{:03}.xhtml", i)
}
<|endoftext|>