<commit_before>// Copyright (C) 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

mod address;

use super::{
    DirectDownload, EntryPoint, Metadata, PrepareLocalInstall, Result, RuntimeSettings, Settings,
    State, StateChangeImpl, Validation,
};
use async_std::{prelude::FutureExt, sync};
use slog_scope::trace;

pub(crate) use address::{AbortDownloadResponse, Addr, ProbeResponse, StateResponse};

pub(super) struct StateMachine {
    state: State,
    context: Context,
}

pub(super) struct Context {
    communication: Channel<(address::Message, sync::Sender<address::Response>)>,
    waker: Channel<()>,
    shared_state: SharedState,
}

#[derive(Debug, PartialEq)]
pub struct SharedState {
    pub settings: Settings,
    pub runtime_settings: RuntimeSettings,
    pub firmware: Metadata,
}

struct Channel<T> {
    sender: sync::Sender<T>,
    receiver: sync::Receiver<T>,
}

impl<T> Channel<T> {
    fn new(cap: usize) -> Self {
        let (sender, receiver) = sync::channel(cap);
        Channel { sender, receiver }
    }
}

impl SharedState {
    pub(super) fn server_address(&self) -> &str {
        self.runtime_settings
            .custom_server_address()
            .unwrap_or(&self.settings.network.server_address)
    }
}

#[derive(Debug)]
pub(super) enum StepTransition {
    Delayed(std::time::Duration),
    Immediate,
    Never,
}

impl StateMachine {
    pub(super) fn new(
        state: State,
        settings: Settings,
        runtime_settings: RuntimeSettings,
        firmware: Metadata,
    ) -> Self {
        StateMachine {
            state,
            context: Context {
                communication: Channel::new(10),
                waker: Channel::new(1),
                shared_state: SharedState { settings, runtime_settings, firmware },
            },
        }
    }

    pub(super) fn address(&self) -> Addr {
        Addr {
            message: self.context.communication.sender.clone(),
            waker: self.context.waker.sender.clone(),
        }
    }

    pub(super) async fn start(mut self) {
        loop {
            // Since the loop is already currently running, we can
            // discharges any wake message received.
            let _ = self.context.waker.receiver.try_recv();

            self.consume_pending_communication().await;

            let (state, transition) = self
                .state
                .move_to_next_state(&mut self.context.shared_state)
                .await
                .unwrap_or_else(|e| (State::from(e), StepTransition::Immediate));
            self.state = state;

            match transition {
                StepTransition::Immediate => {}
                StepTransition::Delayed(t) => {
                    trace!("delaying transition for: {} seconds", t.as_secs());
                    async_std::task::sleep(t).race(self.await_communication()).await;
                }
                StepTransition::Never => {
                    trace!("stopping transition until awoken");
                    let _ = self
                        .context
                        .waker
                        .receiver
                        .clone()
                        .recv()
                        .race(async {
                            self.await_communication().await;
                            Ok(())
                        })
                        .await;
                }
            }
        }
    }

    async fn consume_pending_communication(&mut self) {
        while let Ok((msg, responder)) = self.context.communication.receiver.try_recv() {
            self.handle_communication(msg, responder).await;
        }
    }

    async fn await_communication(&mut self) {
        while let Ok((msg, responder)) = self.context.communication.receiver.recv().await {
            self.handle_communication(msg, responder).await;
        }
    }

    async fn handle_communication(
        &mut self,
        msg: address::Message,
        responder: sync::Sender<address::Response>,
    ) {
        trace!("Received external request: {:?}", msg);

        let response = match msg {
            address::Message::Info => {
                let state = self.state.name().to_owned();
                address::Response::Info(sdk::api::info::Response {
                    state,
                    version: crate::version().to_string(),
                    config: self.context.shared_state.settings.0.clone(),
                    firmware: self.context.shared_state.firmware.0.clone(),
                    runtime_settings: self.context.shared_state.runtime_settings.0.clone(),
                })
            }
            address::Message::Probe(custom_server) => {
                address::Response::Probe(self.handle_probe_request(custom_server).await)
            }
            address::Message::AbortDownload => {
                if self.state.is_handling_download() {
                    self.state = State::EntryPoint(EntryPoint {});
                    address::Response::AbortDownload(
                        address::AbortDownloadResponse::RequestAccepted,
                    )
                } else {
                    address::Response::AbortDownload(address::AbortDownloadResponse::InvalidState)
                }
            }
            address::Message::LocalInstall(update_file) => {
                let state = self.state.name().to_owned();

                if self.state.is_preemptive_state() {
                    crate::logger::start_memory_logging();
                    self.context.waker.sender.send(()).await;

                    self.state = State::PrepareLocalInstall(PrepareLocalInstall { update_file });

                    address::Response::LocalInstall(address::StateResponse::RequestAccepted(state))
                } else {
                    address::Response::LocalInstall(address::StateResponse::InvalidState(state))
                }
            }
            address::Message::RemoteInstall(url) => {
                let state = self.state.name().to_owned();

                if self.state.is_preemptive_state() {
                    crate::logger::start_memory_logging();
                    self.context.waker.sender.send(()).await;

                    self.state = State::DirectDownload(DirectDownload { url });

                    address::Response::RemoteInstall(address::StateResponse::RequestAccepted(state))
                } else {
                    address::Response::RemoteInstall(address::StateResponse::InvalidState(state))
                }
            }
        };

        responder.send(response).await;
    }

    async fn handle_probe_request(
        &mut self,
        custom_server: Option<String>,
    ) -> Result<address::ProbeResponse> {
        use chrono::Utc;
        use cloud::api::ProbeResponse;

        if !self.state.is_preemptive_state() {
            let state = self.state.name().to_owned();
            return Ok(address::ProbeResponse::Busy(state));
        }

        if let Some(server_address) = custom_server {
            self.context.shared_state.runtime_settings.set_custom_server_address(&server_address);
        }

        match crate::CloudClient::new(&self.context.shared_state.server_address())
            .probe(
                self.context.shared_state.runtime_settings.retries() as u64,
                self.context.shared_state.firmware.as_cloud_metadata(),
            )
            .await?
        {
            ProbeResponse::ExtraPoll(s) => Ok(address::ProbeResponse::Delayed(s)),

            ProbeResponse::NoUpdate => {
                self.context.waker.sender.send(()).await;

                // Store timestamp of last polling
                self.context.shared_state.runtime_settings.set_last_polling(Utc::now())?;
                self.state = State::EntryPoint(EntryPoint {});
                Ok(address::ProbeResponse::Unavailable)
            }

            ProbeResponse::Update(package, sign) => {
                self.context.waker.sender.send(()).await;

                // Store timestamp of last polling
                self.context.shared_state.runtime_settings.set_last_polling(Utc::now())?;
                self.state = State::Validation(Validation { package, sign });
                Ok(address::ProbeResponse::Available)
            }
        }
    }
}
<commit_msg>updatehub: Allow machine to be awaken from delayed transition<commit_after>// Copyright (C) 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

mod address;

use super::{
    DirectDownload, EntryPoint, Metadata, PrepareLocalInstall, Result, RuntimeSettings, Settings,
    State, StateChangeImpl, Validation,
};
use async_std::{prelude::FutureExt, sync};
use slog_scope::trace;

pub(crate) use address::{AbortDownloadResponse, Addr, ProbeResponse, StateResponse};

pub(super) struct StateMachine {
    state: State,
    context: Context,
}

pub(super) struct Context {
    communication: Channel<(address::Message, sync::Sender<address::Response>)>,
    waker: Channel<()>,
    shared_state: SharedState,
}

#[derive(Debug, PartialEq)]
pub struct SharedState {
    pub settings: Settings,
    pub runtime_settings: RuntimeSettings,
    pub firmware: Metadata,
}

struct Channel<T> {
    sender: sync::Sender<T>,
    receiver: sync::Receiver<T>,
}

impl<T> Channel<T> {
    fn new(cap: usize) -> Self {
        let (sender, receiver) = sync::channel(cap);
        Channel { sender, receiver }
    }
}

impl SharedState {
    pub(super) fn server_address(&self) -> &str {
        self.runtime_settings
            .custom_server_address()
            .unwrap_or(&self.settings.network.server_address)
    }
}

#[derive(Debug)]
pub(super) enum StepTransition {
    Delayed(std::time::Duration),
    Immediate,
    Never,
}

impl StateMachine {
    pub(super) fn new(
        state: State,
        settings: Settings,
        runtime_settings: RuntimeSettings,
        firmware: Metadata,
    ) -> Self {
        StateMachine {
            state,
            context: Context {
                communication: Channel::new(10),
                waker: Channel::new(1),
                shared_state: SharedState { settings, runtime_settings, firmware },
            },
        }
    }

    pub(super) fn address(&self) -> Addr {
        Addr {
            message: self.context.communication.sender.clone(),
            waker: self.context.waker.sender.clone(),
        }
    }

    pub(super) async fn start(mut self) {
        loop {
            // Since the loop is already currently running, we can
            // discharges any wake message received.
            let _ = self.context.waker.receiver.try_recv();

            self.consume_pending_communication().await;

            let (state, transition) = self
                .state
                .move_to_next_state(&mut self.context.shared_state)
                .await
                .unwrap_or_else(|e| (State::from(e), StepTransition::Immediate));
            self.state = state;

            match transition {
                StepTransition::Immediate => {}
                StepTransition::Delayed(t) => {
                    trace!("delaying transition for: {} seconds", t.as_secs());
                    let waker = self.context.waker.receiver.clone();
                    async_std::task::sleep(t)
                        .race(async {
                            let _ = waker.recv().await;
                        })
                        .race(self.await_communication())
                        .await;
                }
                StepTransition::Never => {
                    trace!("stopping transition until awoken");
                    let _ = self
                        .context
                        .waker
                        .receiver
                        .clone()
                        .recv()
                        .race(async {
                            self.await_communication().await;
                            Ok(())
                        })
                        .await;
                }
            }
        }
    }

    async fn consume_pending_communication(&mut self) {
        while let Ok((msg, responder)) = self.context.communication.receiver.try_recv() {
            self.handle_communication(msg, responder).await;
        }
    }

    async fn await_communication(&mut self) {
        while let Ok((msg, responder)) = self.context.communication.receiver.recv().await {
            self.handle_communication(msg, responder).await;
        }
    }

    async fn handle_communication(
        &mut self,
        msg: address::Message,
        responder: sync::Sender<address::Response>,
    ) {
        trace!("Received external request: {:?}", msg);

        let response = match msg {
            address::Message::Info => {
                let state = self.state.name().to_owned();
                address::Response::Info(sdk::api::info::Response {
                    state,
                    version: crate::version().to_string(),
                    config: self.context.shared_state.settings.0.clone(),
                    firmware: self.context.shared_state.firmware.0.clone(),
                    runtime_settings: self.context.shared_state.runtime_settings.0.clone(),
                })
            }
            address::Message::Probe(custom_server) => {
                address::Response::Probe(self.handle_probe_request(custom_server).await)
            }
            address::Message::AbortDownload => {
                if self.state.is_handling_download() {
                    self.state = State::EntryPoint(EntryPoint {});
                    address::Response::AbortDownload(
                        address::AbortDownloadResponse::RequestAccepted,
                    )
                } else {
                    address::Response::AbortDownload(address::AbortDownloadResponse::InvalidState)
                }
            }
            address::Message::LocalInstall(update_file) => {
                let state = self.state.name().to_owned();

                if self.state.is_preemptive_state() {
                    crate::logger::start_memory_logging();
                    self.context.waker.sender.send(()).await;

                    self.state = State::PrepareLocalInstall(PrepareLocalInstall { update_file });

                    address::Response::LocalInstall(address::StateResponse::RequestAccepted(state))
                } else {
                    address::Response::LocalInstall(address::StateResponse::InvalidState(state))
                }
            }
            address::Message::RemoteInstall(url) => {
                let state = self.state.name().to_owned();

                if self.state.is_preemptive_state() {
                    crate::logger::start_memory_logging();
                    self.context.waker.sender.send(()).await;

                    self.state = State::DirectDownload(DirectDownload { url });

                    address::Response::RemoteInstall(address::StateResponse::RequestAccepted(state))
                } else {
                    address::Response::RemoteInstall(address::StateResponse::InvalidState(state))
                }
            }
        };

        responder.send(response).await;
    }

    async fn handle_probe_request(
        &mut self,
        custom_server: Option<String>,
    ) -> Result<address::ProbeResponse> {
        use chrono::Utc;
        use cloud::api::ProbeResponse;

        if !self.state.is_preemptive_state() {
            let state = self.state.name().to_owned();
            return Ok(address::ProbeResponse::Busy(state));
        }

        if let Some(server_address) = custom_server {
            self.context.shared_state.runtime_settings.set_custom_server_address(&server_address);
        }

        match crate::CloudClient::new(&self.context.shared_state.server_address())
            .probe(
                self.context.shared_state.runtime_settings.retries() as u64,
                self.context.shared_state.firmware.as_cloud_metadata(),
            )
            .await?
        {
            ProbeResponse::ExtraPoll(s) => Ok(address::ProbeResponse::Delayed(s)),

            ProbeResponse::NoUpdate => {
                self.context.waker.sender.send(()).await;

                // Store timestamp of last polling
                self.context.shared_state.runtime_settings.set_last_polling(Utc::now())?;
                self.state = State::EntryPoint(EntryPoint {});
                Ok(address::ProbeResponse::Unavailable)
            }

            ProbeResponse::Update(package, sign) => {
                self.context.waker.sender.send(()).await;

                // Store timestamp of last polling
                self.context.shared_state.runtime_settings.set_last_polling(Utc::now())?;
                self.state = State::Validation(Validation { package, sign });
                Ok(address::ProbeResponse::Available)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::io::prelude::*;

use bio::io::fasta;

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Sequence<'a> {
    pub id: &'a str,
    pub description: Option<String>,
    pub sequence: &'a str,
}

impl<'a> From<Sequence<'a>> for fasta::Record {
    fn from(entry: Sequence<'a>) -> fasta::Record {
        fasta::Record::with_attrs(
            entry.id,
            entry.description.as_ref().map(|s| s.as_str()),
            entry.sequence.as_bytes(),
        )
    }
}
<commit_msg>Remove unused imports<commit_after>use bio::io::fasta;

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Sequence<'a> {
    pub id: &'a str,
    pub description: Option<String>,
    pub sequence: &'a str,
}

impl<'a> From<Sequence<'a>> for fasta::Record {
    fn from(entry: Sequence<'a>) -> fasta::Record {
        fasta::Record::with_attrs(
            entry.id,
            entry.description.as_ref().map(|s| s.as_str()),
            entry.sequence.as_bytes(),
        )
    }
}
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// ### What it does
    /// Use `std::ptr::eq` when applicable
    ///
    /// ### Why is this bad?
    /// `ptr::eq` can be used to compare `&T` references
    /// (which coerce to `*const T` implicitly) by their address rather than
    /// comparing the values they point to.
    ///
    /// ### Example
    /// ```rust
    /// let a = &[1, 2, 3];
    /// let b = &[1, 2, 3];
    ///
    /// assert!(a as *const _ as usize == b as *const _ as usize);
    /// ```
    /// Use instead:
    /// ```rust
    /// let a = &[1, 2, 3];
    /// let b = &[1, 2, 3];
    ///
    /// assert!(std::ptr::eq(a, b));
    /// ```
    pub PTR_EQ,
    style,
    "use `std::ptr::eq` when comparing raw pointers"
}

declare_lint_pass!(PtrEq => [PTR_EQ]);

static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";

impl LateLintPass<'_> for PtrEq {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if in_macro(expr.span) {
            return;
        }

        if let ExprKind::Binary(ref op, left, right) = expr.kind {
            if BinOpKind::Eq == op.node {
                let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
                    (Some(lhs), Some(rhs)) => (lhs, rhs),
                    _ => (left, right),
                };

                if_chain! {
                    if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
                    if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
                    if let Some(left_snip) = snippet_opt(cx, left_var.span);
                    if let Some(right_snip) = snippet_opt(cx, right_var.span);
                    then {
                        span_lint_and_sugg(
                            cx,
                            PTR_EQ,
                            expr.span,
                            LINT_MSG,
                            "try",
                            format!("std::ptr::eq({}, {})", left_snip, right_snip),
                            Applicability::MachineApplicable,
                            );
                    }
                }
            }
        }
    }
}

// If the given expression is a cast to an usize, return the lhs of the cast
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
    if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
        if let ExprKind::Cast(expr, _) = cast_expr.kind {
            return Some(expr);
        }
    }
    None
}

// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
// E.g., `foo as *const _` returns `foo`.
fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
        if let ExprKind::Cast(expr, _) = cast_expr.kind {
            return Some(expr);
        }
    }
    None
}
<commit_msg>Fix typos “an”→“a” and a few different ones that appeared in the same search<commit_after>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// ### What it does
    /// Use `std::ptr::eq` when applicable
    ///
    /// ### Why is this bad?
    /// `ptr::eq` can be used to compare `&T` references
    /// (which coerce to `*const T` implicitly) by their address rather than
    /// comparing the values they point to.
    ///
    /// ### Example
    /// ```rust
    /// let a = &[1, 2, 3];
    /// let b = &[1, 2, 3];
    ///
    /// assert!(a as *const _ as usize == b as *const _ as usize);
    /// ```
    /// Use instead:
    /// ```rust
    /// let a = &[1, 2, 3];
    /// let b = &[1, 2, 3];
    ///
    /// assert!(std::ptr::eq(a, b));
    /// ```
    pub PTR_EQ,
    style,
    "use `std::ptr::eq` when comparing raw pointers"
}

declare_lint_pass!(PtrEq => [PTR_EQ]);

static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";

impl LateLintPass<'_> for PtrEq {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if in_macro(expr.span) {
            return;
        }

        if let ExprKind::Binary(ref op, left, right) = expr.kind {
            if BinOpKind::Eq == op.node {
                let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
                    (Some(lhs), Some(rhs)) => (lhs, rhs),
                    _ => (left, right),
                };

                if_chain! {
                    if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
                    if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
                    if let Some(left_snip) = snippet_opt(cx, left_var.span);
                    if let Some(right_snip) = snippet_opt(cx, right_var.span);
                    then {
                        span_lint_and_sugg(
                            cx,
                            PTR_EQ,
                            expr.span,
                            LINT_MSG,
                            "try",
                            format!("std::ptr::eq({}, {})", left_snip, right_snip),
                            Applicability::MachineApplicable,
                            );
                    }
                }
            }
        }
    }
}

// If the given expression is a cast to a usize, return the lhs of the cast
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
    if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
        if let ExprKind::Cast(expr, _) = cast_expr.kind {
            return Some(expr);
        }
    }
    None
}

// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
// E.g., `foo as *const _` returns `foo`.
fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
        if let ExprKind::Cast(expr, _) = cast_expr.kind {
            return Some(expr);
        }
    }
    None
}
<|endoftext|>


<commit_before>use clap::{App, Arg, Format, ArgMatches};
use std::io::{self, Write};
use std::process::exit;
use std::fs;
use crowbook::Book;

/// Prints an error on stderr and exit the program
pub fn print_error(s: &str) -> ! {
    writeln!(&mut io::stderr(), "{} {}", Format::Error("error:"), s).unwrap();
    exit(0);
}

/// sets the book options according to command line arguments
/// Also print these options to a string, so it can be used at
/// the creation of a book to check that parameters are OK and t
/// then print them to file
pub fn set_book_options(book: &mut Book, matches: &ArgMatches) -> String {
    let mut output = String::new();
    if let Some(iter) = matches.values_of("set") {
        let v:Vec<_> = iter.collect();
        if v.len() %2 != 0 {
            print_error("An odd number of arguments was passed to --set, but it takes a list of key value pairs.");
        }

        for i in 0..v.len()/2 {
            let key = v[i * 2];
            let value = v[i * 2 + 1];
            let res = book.options.set(key, value);
            if let Err(err) = res {
                print_error(&format!("Error in setting key {}: {}", key, err));
            }
            output.push_str(&format!("{}: {}\n", key, value));
        }
    }
    output
}

/// create a book file with the command line arguments
/// and exit the process at the end
pub fn create_book(matches: &ArgMatches) -> ! {
    let mut f:Box<Write> = if let Some(book) = matches.value_of("BOOK") {
        if fs::metadata(book).is_ok() {
            print_error(&format!("Could not create file {}: it already exists!", book));
        }
        Box::new(fs::File::create(book).unwrap())
    } else {
        Box::new(io::stdout())
    };

    if let Some(values) = matches.values_of("create") {
        if matches.is_present("set") {
            let mut book = Book::new();
            let s = set_book_options(&mut book, matches);
            f.write_all(&s.as_bytes()).unwrap();
        } else {
            f.write_all(b"author: Your name
title: Your title
lang: en

# Uncomment and fill to generate files
# output.html: some_file.html
# output.epub: some_file.epub
# output.pdf: some_file.pdf

# Uncomment and fill to set cover image (for Epub)
# cover: some_cover.png\n").unwrap();
        }
        f.write_all(b"\n# List of chapters\n").unwrap();
        for file in values {
            f.write_all(&format!("+ {}\n", file).as_bytes()).unwrap();
        }
        if let Some(s) = matches.value_of("BOOK") {
            println!("Created {}, now you'll have to complete it!", s);
        }
        exit(0);
    } else {
        print_error("--create must be used with a list of additonal files.

USAGE:
\tcrowbook [BOOK] --create <MARKDOWN_FILES>");
    }
}

pub fn create_matches<'a>() -> ArgMatches<'a> {
    let app = App::new("crowbook")
        .version(env!("CARGO_PKG_VERSION"))
        .about("Render a markdown book in Epub, PDF or HTML.")
        .arg_from_usage("-s, --single 'Use a single Markdown file instead of a book configuration file'")
        .arg_from_usage("-v, --verbose 'Print warnings in parsing/rendering'")
        .arg_from_usage("-d, --debug 'Print debugging information'")
        .arg_from_usage("--create [FILES]... 'Creates a new book with existing markdown files.'")
        .arg(Arg::from_usage("-o, --output [FILE] 'Specifies output file.'")
             .requires("to"))
        .arg(Arg::from_usage("-t, --to [FORMAT] 'Generate specific format'")
             .possible_values(&["epub", "pdf", "html", "tex", "odt"]))
        .arg(Arg::from_usage("--set [KEY_VALUES] 'Sets a list of book options'")
             .min_values(2))
        .arg(Arg::from_usage("-l --list-options 'Lists all possible options"))
        .arg_from_usage("--list-options-md 'List all options, formatted in Markdown.'")
        .arg_from_usage("--print-template [TEMPLATE] 'Displays the default value of a template.'")
        .arg(Arg::with_name("BOOK")
             .index(1)
             .help("File containing the book configuration, or a Markdown file when called with --single."));

    let matches = app.get_matches();

    pre_check(&matches);
    matches
}

/// Pre-check the matches to see if there isn't illegal options not detected by clap
fn pre_check(matches: &ArgMatches) {
    if matches.is_present("files") && !matches.is_present("create") {
        print_error("A list of additional files is only valid with the --create option.");
    }
}
<commit_msg>Clean help message<commit_after>use clap::{App, Arg, Format, ArgMatches};
use std::io::{self, Write};
use std::process::exit;
use std::fs;
use crowbook::Book;

/// Prints an error on stderr and exit the program
pub fn print_error(s: &str) -> ! {
    writeln!(&mut io::stderr(), "{} {}", Format::Error("error:"), s).unwrap();
    exit(0);
}

/// sets the book options according to command line arguments
/// Also print these options to a string, so it can be used at
/// the creation of a book to check that parameters are OK and t
/// then print them to file
pub fn set_book_options(book: &mut Book, matches: &ArgMatches) -> String {
    let mut output = String::new();
    if let Some(iter) = matches.values_of("set") {
        let v:Vec<_> = iter.collect();
        if v.len() %2 != 0 {
            print_error("An odd number of arguments was passed to --set, but it takes a list of key value pairs.");
        }

        for i in 0..v.len()/2 {
            let key = v[i * 2];
            let value = v[i * 2 + 1];
            let res = book.options.set(key, value);
            if let Err(err) = res {
                print_error(&format!("Error in setting key {}: {}", key, err));
            }
            output.push_str(&format!("{}: {}\n", key, value));
        }
    }
    output
}

/// create a book file with the command line arguments
/// and exit the process at the end
pub fn create_book(matches: &ArgMatches) -> ! {
    let mut f:Box<Write> = if let Some(book) = matches.value_of("BOOK") {
        if fs::metadata(book).is_ok() {
            print_error(&format!("Could not create file {}: it already exists!", book));
        }
        Box::new(fs::File::create(book).unwrap())
    } else {
        Box::new(io::stdout())
    };

    if let Some(values) = matches.values_of("create") {
        if matches.is_present("set") {
            let mut book = Book::new();
            let s = set_book_options(&mut book, matches);
            f.write_all(&s.as_bytes()).unwrap();
        } else {
            f.write_all(b"author: Your name
title: Your title
lang: en

# Uncomment and fill to generate files
# output.html: some_file.html
# output.epub: some_file.epub
# output.pdf: some_file.pdf

# Uncomment and fill to set cover image (for Epub)
# cover: some_cover.png\n").unwrap();
        }
        f.write_all(b"\n# List of chapters\n").unwrap();
        for file in values {
            f.write_all(&format!("+ {}\n", file).as_bytes()).unwrap();
        }
        if let Some(s) = matches.value_of("BOOK") {
            println!("Created {}, now you'll have to complete it!", s);
        }
        exit(0);
    } else {
        unreachable!(); // because Clap takes care of it
    }
}

pub fn create_matches<'a>() -> ArgMatches<'a> {
    let app = App::new("crowbook")
        .version(env!("CARGO_PKG_VERSION"))
        .about("Render a markdown book in Epub, PDF or HTML.")
        .arg_from_usage("-s, --single 'Use a single Markdown file instead of a book configuration file'")
        .arg_from_usage("-v, --verbose 'Print warnings in parsing/rendering'")
        .arg(Arg::from_usage("-d, --debug 'Print debugging information'")
             .hidden(true))
        .arg_from_usage("-c, --create [FILES]... 'Creates a new book with existing markdown files'")
        .arg(Arg::from_usage("-o, --output [FILE] 'Specifies output file'")
             .requires("to"))
        .arg(Arg::from_usage("-t, --to [FORMAT] 'Generate specific format")
             .possible_values(&["epub", "pdf", "html", "tex", "odt"]))
        .arg(Arg::from_usage("--set [KEY_VALUES] 'Sets a list of book options'")
             .min_values(2))
        .arg(Arg::from_usage("-l --list-options 'Lists all possible options"))
        .arg_from_usage("--list-options-md 'List all options, formatted in Markdown'")
        .arg_from_usage("--print-template [TEMPLATE] 'Displays the default value of a template'")
        .arg(Arg::with_name("BOOK")
             .index(1)
             .help("File containing the book configuration, or a Markdown file when called with --single"));

    let matches = app.get_matches();

    pre_check(&matches);
    matches
}

/// Pre-check the matches to see if there isn't illegal options not detected by clap
fn pre_check(matches: &ArgMatches) {
    if matches.is_present("files") && !matches.is_present("create") {
        print_error("A list of additional files is only valid with the --create option.");
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{run, run_from_stdin};
use rustfmt::config::{Config, WriteMode};

use std::env;
use std::fs::{self, File};
use std::io::{self, ErrorKind, Read, Write};
use std::path::{Path, PathBuf};

use getopts::{Matches, Options};

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format(Vec<PathBuf>, WriteMode),
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Invalid program input, including reason.
    InvalidInput(String),
    /// No file specified, read from stdin
    Stdin(String, WriteMode),
}

/// Try to find a project file in the given directory and its parents.
fn lookup_project_file(dir: &Path) -> io::Result<Option<PathBuf>> {
    let mut current = if dir.is_relative() {
        try!(env::current_dir()).join(dir)
    } else {
        dir.to_path_buf()
    };

    current = try!(fs::canonicalize(current));

    loop {
        let config_file = current.join("rustfmt.toml");
        match fs::metadata(&config_file) {
            Ok(md) => {
                // Properly handle unlikely situation of a directory named `rustfmt.toml`.
                if md.is_file() {
                    return Ok(Some(config_file));
                }
            }
            // If it's not found, we continue searching; otherwise something went wrong and we
            // return the error.
            Err(e) => {
                if e.kind() != ErrorKind::NotFound {
                    return Err(e);
                }
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Resolve the config for input in `dir`.
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
fn resolve_config(dir: &Path) -> io::Result<(Config, Option<PathBuf>)> {
    let path = try!(lookup_project_file(dir));
    if path.is_none() {
        return Ok((Config::default(), None));
    }
    let path = path.unwrap();
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((Config::from_toml(&toml), Some(path)))
}

fn update_config(config: &mut Config, matches: &Matches) {
    config.verbose = matches.opt_present("verbose");
    config.skip_children = matches.opt_present("skip-children");
}

fn execute() -> i32 {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "show progress");
    opts.optopt("",
                "write-mode",
                "mode to write in (not usable when piping from stdin)",
                "[replace|overwrite|display|diff|coverage|checkstyle]");
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag("",
                 "config-help",
                 "show details of rustfmt configuration options");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return 1;
        }
    };

    let operation = determine_operation(&matches);

    match operation {
        Operation::InvalidInput(reason) => {
            print_usage(&opts, &reason);
            1
        }
        Operation::Help => {
            print_usage(&opts, "");
            0
        }
        Operation::Version => {
            print_version();
            0
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            0
        }
        Operation::Stdin(input, write_mode) => {
            // try to read config from local directory
            let (config, _) = resolve_config(&env::current_dir().unwrap())
                                  .expect("Error resolving config");

            run_from_stdin(input, write_mode, &config);
            0
        }
        Operation::Format(files, write_mode) => {
            for file in files {
                let (mut config, path) = resolve_config(file.parent().unwrap())
                                             .expect(&format!("Error resolving config for {}",
                                                              file.display()));
                if let Some(path) = path {
                    println!("Using rustfmt config file {} for {}",
                             path.display(),
                             file.display());
                }

                update_config(&mut config, &matches);
                run(&file, write_mode, &config);
            }
            0
        }
    }
}

fn main() {
    let _ = env_logger::init();
    let exit_code = execute();

    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         env::args_os().next().unwrap().to_string_lossy());
    println!("{}", opts.usage(&reason));
}

fn print_version() {
    println!("{}.{}.{}{}",
             option_env!("CARGO_PKG_VERSION_MAJOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_MINOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PATCH").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
}

fn determine_operation(matches: &Matches) -> Operation {
    if matches.opt_present("h") {
        return Operation::Help;
    }

    if matches.opt_present("config-help") {
        return Operation::ConfigHelp;
    }

    if matches.opt_present("version") {
        return Operation::Version;
    }

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {

        let mut buffer = String::new();
        match io::stdin().read_to_string(&mut buffer) {
            Ok(..) => (),
            Err(e) => return Operation::InvalidInput(e.to_string()),
        }

        // WriteMode is always plain for Stdin
        return Operation::Stdin(buffer, WriteMode::Plain);
    }

    let write_mode = match matches.opt_str("write-mode") {
        Some(mode) => {
            match mode.parse() {
                Ok(mode) => mode,
                Err(..) => return Operation::InvalidInput("Unrecognized write mode".into()),
            }
        }
        None => WriteMode::Default,
    };

    let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();

    Operation::Format(files, write_mode)
}
<commit_msg>docs: Clarify return type of lookup_project_file<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{run, run_from_stdin};
use rustfmt::config::{Config, WriteMode};

use std::env;
use std::fs::{self, File};
use std::io::{self, ErrorKind, Read, Write};
use std::path::{Path, PathBuf};

use getopts::{Matches, Options};

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format(Vec<PathBuf>, WriteMode),
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Invalid program input, including reason.
    InvalidInput(String),
    /// No file specified, read from stdin
    Stdin(String, WriteMode),
}

/// Try to find a project file in the given directory and its parents. Returns the path of a the
/// nearest project file if one exists, or `None` if no project file was found.
fn lookup_project_file(dir: &Path) -> io::Result<Option<PathBuf>> {
    let mut current = if dir.is_relative() {
        try!(env::current_dir()).join(dir)
    } else {
        dir.to_path_buf()
    };

    current = try!(fs::canonicalize(current));

    loop {
        let config_file = current.join("rustfmt.toml");
        match fs::metadata(&config_file) {
            Ok(md) => {
                // Properly handle unlikely situation of a directory named `rustfmt.toml`.
                if md.is_file() {
                    return Ok(Some(config_file));
                }
            }
            // If it's not found, we continue searching; otherwise something went wrong and we
            // return the error.
            Err(e) => {
                if e.kind() != ErrorKind::NotFound {
                    return Err(e);
                }
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Resolve the config for input in `dir`.
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
fn resolve_config(dir: &Path) -> io::Result<(Config, Option<PathBuf>)> {
    let path = try!(lookup_project_file(dir));
    if path.is_none() {
        return Ok((Config::default(), None));
    }
    let path = path.unwrap();
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((Config::from_toml(&toml), Some(path)))
}

fn update_config(config: &mut Config, matches: &Matches) {
    config.verbose = matches.opt_present("verbose");
    config.skip_children = matches.opt_present("skip-children");
}

fn execute() -> i32 {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "show progress");
    opts.optopt("",
                "write-mode",
                "mode to write in (not usable when piping from stdin)",
                "[replace|overwrite|display|diff|coverage|checkstyle]");
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag("",
                 "config-help",
                 "show details of rustfmt configuration options");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return 1;
        }
    };

    let operation = determine_operation(&matches);

    match operation {
        Operation::InvalidInput(reason) => {
            print_usage(&opts, &reason);
            1
        }
        Operation::Help => {
            print_usage(&opts, "");
            0
        }
        Operation::Version => {
            print_version();
            0
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            0
        }
        Operation::Stdin(input, write_mode) => {
            // try to read config from local directory
            let (config, _) = resolve_config(&env::current_dir().unwrap())
                                  .expect("Error resolving config");

            run_from_stdin(input, write_mode, &config);
            0
        }
        Operation::Format(files, write_mode) => {
            for file in files {
                let (mut config, path) = resolve_config(file.parent().unwrap())
                                             .expect(&format!("Error resolving config for {}",
                                                              file.display()));
                if let Some(path) = path {
                    println!("Using rustfmt config file {} for {}",
                             path.display(),
                             file.display());
                }

                update_config(&mut config, &matches);
                run(&file, write_mode, &config);
            }
            0
        }
    }
}

fn main() {
    let _ = env_logger::init();
    let exit_code = execute();

    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         env::args_os().next().unwrap().to_string_lossy());
    println!("{}", opts.usage(&reason));
}

fn print_version() {
    println!("{}.{}.{}{}",
             option_env!("CARGO_PKG_VERSION_MAJOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_MINOR").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PATCH").unwrap_or("X"),
             option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
}

fn determine_operation(matches: &Matches) -> Operation {
    if matches.opt_present("h") {
        return Operation::Help;
    }

    if matches.opt_present("config-help") {
        return Operation::ConfigHelp;
    }

    if matches.opt_present("version") {
        return Operation::Version;
    }

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {

        let mut buffer = String::new();
        match io::stdin().read_to_string(&mut buffer) {
            Ok(..) => (),
            Err(e) => return Operation::InvalidInput(e.to_string()),
        }

        // WriteMode is always plain for Stdin
        return Operation::Stdin(buffer, WriteMode::Plain);
    }

    let write_mode = match matches.opt_str("write-mode") {
        Some(mode) => {
            match mode.parse() {
                Ok(mode) => mode,
                Err(..) => return Operation::InvalidInput("Unrecognized write mode".into()),
            }
        }
        None => WriteMode::Default,
    };

    let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();

    Operation::Format(files, write_mode)
}
<|endoftext|>


<commit_before>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use std::sync::atomic::{AtomicBool, Ordering};

use futures::future;
use futures::future::FutureResult;
use memcached::Client;
use memcached::proto::ProtoType;

use config::config::ConfigMemcached;

pub struct CacheStoreBuilder;

pub struct CacheStore {
    config_memcached: ConfigMemcached,
    is_connected: AtomicBool
    // client: Client  <-- TODO: impl. clone for Client?
}

type CacheResult = FutureResult<Option<()>, &'static str>;

impl CacheStoreBuilder {
    pub fn new(config_memcached: ConfigMemcached) -> CacheStore {
        CacheStore {
            config_memcached: config_memcached,
            is_connected: AtomicBool::new(false)
        }
    }
}

impl CacheStore {
    pub fn bind(&self) {
        // TODO: bind to ConfigMemcached.inet

        // TODO: enforce config values:
        //   - ConfigMemcached.pool_size
        //   - ConfigMemcached.reconnect
        //   - ConfigMemcached.timeout

        // TODO: ensure following contracts:
            // if first connect fails, panic!()
            // if connection to memcached is lost at any point, mark as \
            //   disconnected and immediately return get/set futures w/o \
            //   trying to access the network (this doesnt add extra-latency \
            //   to api requests). but: keep trying to reconnect in bg.
            //   (best-effort retries, hit the api directly in that case and \
            //   return response w/ the DIRECT bloom status header)

        info!("Binding to store backend at {}", self.config_memcached.inet);

        let tcp_addr = format!("tcp://{}:{}", self.config_memcached.inet.ip(),
            self.config_memcached.inet.port());
        let servers = [(tcp_addr.as_str(), 1)];

        match Client::connect(&servers, ProtoType::Binary) {
            Ok(client) => {
                // TODO: assign to struct
                // self.client = client

                self.is_connected.store(true, Ordering::Relaxed);
            }
            Err(err) => panic!("could not connect to memcached: {}", err)
        }

        info!("Bound to store backend");
    }

    pub fn get(&self, key: &str) -> CacheResult {
        if self.is_connected.load(Ordering::Relaxed) == true {
            // TODO

            return future::ok(None)
        }

        future::err("disconnected")
    }

    pub fn set(&self, key: &str, value: &str, ttl: u32) -> CacheResult {
        if self.is_connected.load(Ordering::Relaxed) == true {
            // TODO

            return future::ok(None)
        }

        future::err("disconnected")

        // TODO: set and return a future (needed? maybe we dont even need to \
        //   ack as this is best effort, maybe just log write errors) \
        //   (w/ 'true' value or 'false if fail)
        // TODO: value maybe would be better be a stream to avoid large buffers

        // TODO: enforce config values:
        //   - ConfigMemcached.max_key_size
        //   - ConfigMemcached.max_key_expiration
    }

    pub fn purge(&self, key: &str) -> CacheResult {
        if self.is_connected.load(Ordering::Relaxed) == true {
            // TODO

            return future::ok(None)
        }

        future::err("disconnected")
    }
}
<commit_msg>Freeze<commit_after>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use std::sync::atomic::{AtomicBool, Ordering};

use futures::future;
use futures::future::FutureResult;
use memcached::Client;
use memcached::proto::ProtoType;

use config::config::ConfigMemcached;

pub struct CacheStoreBuilder;

pub struct CacheStore {
    config_memcached: ConfigMemcached,
    is_connected: AtomicBool
    // client: Client
}

type CacheResult = FutureResult<Option<()>, &'static str>;

impl CacheStoreBuilder {
    pub fn new(config_memcached: ConfigMemcached) -> CacheStore {
        CacheStore {
            config_memcached: config_memcached,
            is_connected: AtomicBool::new(false)
        }
    }
}

impl CacheStore {
    pub fn bind(&self) {
        // TODO: bind to ConfigMemcached.inet

        // TODO: enforce config values:
        //   - ConfigMemcached.pool_size
        //   - ConfigMemcached.reconnect
        //   - ConfigMemcached.timeout

        // TODO: ensure following contracts:
            // if first connect fails, panic!()
            // if connection to memcached is lost at any point, mark as \
            //   disconnected and immediately return get/set futures w/o \
            //   trying to access the network (this doesnt add extra-latency \
            //   to api requests). but: keep trying to reconnect in bg.
            //   (best-effort retries, hit the api directly in that case and \
            //   return response w/ the DIRECT bloom status header)

        info!("Binding to store backend at {}", self.config_memcached.inet);

        let tcp_addr = format!("tcp://{}:{}", self.config_memcached.inet.ip(),
            self.config_memcached.inet.port());
        let servers = [(tcp_addr.as_str(), 1)];

        match Client::connect(&servers, ProtoType::Binary) {
            Ok(client) => {
                // TODO: assign to struct
                // self.client = client

                self.is_connected.store(true, Ordering::Relaxed);
            }
            Err(err) => panic!("could not connect to memcached: {}", err)
        }

        info!("Bound to store backend");
    }

    pub fn get(&self, key: &str) -> CacheResult {
        if self.is_connected.load(Ordering::Relaxed) == true {
            // TODO: handle success or error (if empty or netwrk error)
            // let (value, _) = self.client.get(key);

            return future::ok(None)
        }

        future::err("disconnected")
    }

    pub fn set(&self, key: &str, value: &str, ttl: u32) -> CacheResult {
        if self.is_connected.load(Ordering::Relaxed) == true {
            // TODO: flags + expiration
            // TODO: handle success or error
            // self.client.set_noreply(key, value, 0x00000001, 20).unwrap();

            return future::ok(None)
        }

        future::err("disconnected")

        // TODO: set and return a future (needed? maybe we dont even need to \
        //   ack as this is best effort, maybe just log write errors) \
        //   (w/ 'true' value or 'false if fail)
        // TODO: value maybe would be better be a stream to avoid large buffers

        // TODO: enforce config values:
        //   - ConfigMemcached.max_key_size
        //   - ConfigMemcached.max_key_expiration
    }

    pub fn purge(&self, key: &str) -> CacheResult {
        if self.is_connected.load(Ordering::Relaxed) == true {
            // TODO: handle success or error (if no such key or netwrk error)
            // self.client.delete(key);

            return future::ok(None)
        }

        future::err("disconnected")
    }
}
<|endoftext|>


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

use app_units::Au;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{NativeFontHandle, GlyphOptions};
use webrender_traits::{GlyphKey};

use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
use freetype::freetype::{FT_Library, FT_Set_Char_Size};
use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
use freetype::freetype::{FT_Done_Face, FT_Error};

use std::{cmp, mem, ptr, slice};
use std::collections::HashMap;

struct Face {
    face: FT_Face,
}

pub struct FontContext {
    lib: FT_Library,
    faces: HashMap<FontKey, Face>,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn float_to_fixed(before: usize, f: f64) -> i32 {
    ((1i32 << before) as f64 * f) as i32
}

fn float_to_fixed_ft(f: f64) -> i32 {
    float_to_fixed(6, f)
}

const SUCCESS: FT_Error = FT_Error(0);

impl FontContext {
    pub fn new() -> FontContext {
        let mut lib: FT_Library = ptr::null_mut();
        unsafe {
            let result = FT_Init_FreeType(&mut lib);
            assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);

            // TODO(gw): Check result of this to determine if freetype build supports subpixel.
            let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
            if !result.succeeded() {
                println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
            }
        }

        FontContext {
            lib: lib,
            faces: HashMap::new(),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
        if !self.faces.contains_key(&font_key) {
            let mut face: FT_Face = ptr::null_mut();
            let result = unsafe {
                FT_New_Memory_Face(self.lib,
                                   bytes.as_ptr(),
                                   bytes.len() as FT_Long,
                                   index as FT_Long,
                                   &mut face)
            };
            if result.succeeded() && !face.is_null() {
                self.faces.insert(*font_key, Face {
                    face: face,
                    //_bytes: bytes
                });
            } else {
                println!("WARN: webrender failed to load font {:?}", font_key);
            }
        }
    }

    pub fn add_native_font(&mut self, _font_key: &FontKey, _native_font_handle: NativeFontHandle) {
        panic!("TODO: Not supported on Linux");
    }

    pub fn delete_font(&mut self, font_key: &FontKey) {
        if let Some(face) = self.faces.remove(font_key) {
            let result = unsafe {
                FT_Done_Face(face.face)
            };
            assert!(result.succeeded());
        }
    }

    fn load_glyph(&self,
                  font_key: FontKey,
                  size: Au,
                  character: u32) -> Option<FT_GlyphSlot> {

        debug_assert!(self.faces.contains_key(&font_key));
        let face = self.faces.get(&font_key).unwrap();
        let char_size = float_to_fixed_ft(size.to_f64_px());

        assert_eq!(SUCCESS, unsafe {
            FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0)
        });

        let result = unsafe {
            FT_Load_Glyph(face.face, character as FT_UInt, 0)
        };

        if result == SUCCESS {
            let slot = unsafe { (*face.face).glyph };
            assert!(slot != ptr::null_mut());
            Some(slot)
        } else {
            error!("Unable to load glyph for {} of size {:?} from font {:?}, {:?}",
                character, size, font_key, result);
            None
        }
    }

    fn get_glyph_dimensions_impl(slot: FT_GlyphSlot) -> Option<GlyphDimensions> {
        let metrics = unsafe { &(*slot).metrics };
        if metrics.width == 0 || metrics.height == 0 {
            None
        } else {
            let left = metrics.horiBearingX >> 6;
            let top = metrics.horiBearingY >> 6;
            let right = (metrics.horiBearingX + metrics.width + 0x3f) >> 6;
            let bottom = (metrics.horiBearingY + metrics.height + 0x3f) >> 6;
            Some(GlyphDimensions {
                left: left as i32,
                top: top as i32,
                width: (right - left) as u32,
                height: (bottom - top) as u32,
            })
        }
    }

    pub fn get_glyph_dimensions(&mut self,
                                key: &GlyphKey) -> Option<GlyphDimensions> {
        self.load_glyph(key.font_key, key.size, key.index)
            .and_then(Self::get_glyph_dimensions_impl)
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           _glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {

        let slot = match self.load_glyph(key.font_key, key.size, key.index) {
            Some(slot) => slot,
            None => return None,
        };
        let render_mode = match render_mode {
            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
            FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
        };

        let result = unsafe { FT_Render_Glyph(slot, render_mode) };
        if result != SUCCESS {
            error!("Unable to rasterize {:?} with {:?}, {:?}", key, render_mode, result);
            return None;
        }

        let dimensions = Self::get_glyph_dimensions_impl(slot).unwrap();
        let bitmap = unsafe { &(*slot).bitmap };
        let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
        info!("Rasterizing {:?} as {:?} with dimensions {:?}", key, render_mode, dimensions);
        // we may be filling only a part of the buffer, so initialize the whole thing with 0
        let mut final_buffer = Vec::with_capacity(dimensions.width as usize *
                                                  dimensions.height as usize *
                                                  4);

        let offset_x = dimensions.left - unsafe { (*slot).bitmap_left };

        for y in 0 .. dimensions.height {
            let src_y = y as i32 + dimensions.top - unsafe { (*slot).bitmap_top };
            if src_y < 0 || src_y >= bitmap.rows as i32 {
                for _x in 0 .. dimensions.width {
                    final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                }
                continue
            }
            let base = unsafe {
                bitmap.buffer.offset((src_y * bitmap.pitch) as isize)
            };
            // determine the destination range of texels that `bitmap` provides data for
            let dst_start = cmp::max(0, -offset_x);
            let dst_end = cmp::min(dimensions.width as i32, bitmap.width as i32 - offset_x);
            for _x in 0 .. dst_start {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            match pixel_mode {
                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
                    for x in dst_start .. dst_end {
                        let src_x = x + offset_x;
                        let mask = 0x80 >> (src_x & 0x7);
                        let byte = unsafe {
                            *base.offset((src_x >> 3) as isize)
                        };
                        let alpha = if byte & mask != 0 { 0xff } else { 0 };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
                    for x in dst_start .. dst_end {
                        let alpha = unsafe {
                            *base.offset((x + offset_x) as isize)
                        };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                    for x in dst_start .. dst_end {
                        let src_x = ((x + offset_x) * 3) as isize;
                        assert!(src_x+2 < bitmap.pitch as isize);
                        let t = unsafe {
                            slice::from_raw_parts(base.offset(src_x), 3)
                        };
                        final_buffer.extend_from_slice(&[t[2], t[1], t[0], 0xff]);
                    }
                }
                _ => panic!("Unsupported {:?}", pixel_mode)
            }
            for _x in dst_end .. dimensions.width as i32 {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            assert_eq!(final_buffer.len(), ((y+1) * dimensions.width * 4) as usize);
        }

        Some(RasterizedGlyph {
            width: dimensions.width as u32,
            height: dimensions.height as u32,
            bytes: final_buffer,
        })
    }
}

impl Drop for FontContext {
    fn drop(&mut self) {
        unsafe {
            FT_Done_FreeType(self.lib);
        }
    }
}
<commit_msg>Unix font vertical alignment<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{NativeFontHandle, GlyphOptions};
use webrender_traits::{GlyphKey};

use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
use freetype::freetype::{FT_Library, FT_Set_Char_Size};
use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
use freetype::freetype::{FT_Done_Face, FT_Error};

use std::{cmp, mem, ptr, slice};
use std::collections::HashMap;

struct Face {
    face: FT_Face,
}

pub struct FontContext {
    lib: FT_Library,
    faces: HashMap<FontKey, Face>,
}

pub struct RasterizedGlyph {
    pub width: u32,
    pub height: u32,
    pub bytes: Vec<u8>,
}

fn float_to_fixed(before: usize, f: f64) -> i32 {
    ((1i32 << before) as f64 * f) as i32
}

fn float_to_fixed_ft(f: f64) -> i32 {
    float_to_fixed(6, f)
}

const SUCCESS: FT_Error = FT_Error(0);

impl FontContext {
    pub fn new() -> FontContext {
        let mut lib: FT_Library = ptr::null_mut();
        unsafe {
            let result = FT_Init_FreeType(&mut lib);
            assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);

            // TODO(gw): Check result of this to determine if freetype build supports subpixel.
            let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
            if !result.succeeded() {
                println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
            }
        }

        FontContext {
            lib: lib,
            faces: HashMap::new(),
        }
    }

    pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
        if !self.faces.contains_key(&font_key) {
            let mut face: FT_Face = ptr::null_mut();
            let result = unsafe {
                FT_New_Memory_Face(self.lib,
                                   bytes.as_ptr(),
                                   bytes.len() as FT_Long,
                                   index as FT_Long,
                                   &mut face)
            };
            if result.succeeded() && !face.is_null() {
                self.faces.insert(*font_key, Face {
                    face: face,
                    //_bytes: bytes
                });
            } else {
                println!("WARN: webrender failed to load font {:?}", font_key);
            }
        }
    }

    pub fn add_native_font(&mut self, _font_key: &FontKey, _native_font_handle: NativeFontHandle) {
        panic!("TODO: Not supported on Linux");
    }

    pub fn delete_font(&mut self, font_key: &FontKey) {
        if let Some(face) = self.faces.remove(font_key) {
            let result = unsafe {
                FT_Done_Face(face.face)
            };
            assert!(result.succeeded());
        }
    }

    fn load_glyph(&self,
                  font_key: FontKey,
                  size: Au,
                  character: u32) -> Option<FT_GlyphSlot> {

        debug_assert!(self.faces.contains_key(&font_key));
        let face = self.faces.get(&font_key).unwrap();
        let char_size = float_to_fixed_ft(size.to_f64_px());

        assert_eq!(SUCCESS, unsafe {
            FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0)
        });

        let result = unsafe {
            FT_Load_Glyph(face.face, character as FT_UInt, 0)
        };

        if result == SUCCESS {
            let slot = unsafe { (*face.face).glyph };
            assert!(slot != ptr::null_mut());
            Some(slot)
        } else {
            error!("Unable to load glyph for {} of size {:?} from font {:?}, {:?}",
                character, size, font_key, result);
            None
        }
    }

    fn get_glyph_dimensions_impl(slot: FT_GlyphSlot) -> Option<GlyphDimensions> {
        let metrics = unsafe { &(*slot).metrics };
        if metrics.width == 0 || metrics.height == 0 {
            None
        } else {
            let left = metrics.horiBearingX >> 6;
            let top = metrics.horiBearingY >> 6;
            let right = (metrics.horiBearingX + metrics.width + 0x3f) >> 6;
            let bottom = (metrics.horiBearingY + metrics.height + 0x3f) >> 6;
            Some(GlyphDimensions {
                left: left as i32,
                top: top as i32,
                width: (right - left) as u32,
                height: (bottom - top) as u32,
            })
        }
    }

    pub fn get_glyph_dimensions(&mut self,
                                key: &GlyphKey) -> Option<GlyphDimensions> {
        self.load_glyph(key.font_key, key.size, key.index)
            .and_then(Self::get_glyph_dimensions_impl)
    }

    pub fn rasterize_glyph(&mut self,
                           key: &GlyphKey,
                           render_mode: FontRenderMode,
                           _glyph_options: Option<GlyphOptions>)
                           -> Option<RasterizedGlyph> {

        let slot = match self.load_glyph(key.font_key, key.size, key.index) {
            Some(slot) => slot,
            None => return None,
        };
        let render_mode = match render_mode {
            FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
            FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
            FontRenderMode::Subpixel => FT_Render_Mode::FT_RENDER_MODE_LCD,
        };

        let result = unsafe { FT_Render_Glyph(slot, render_mode) };
        if result != SUCCESS {
            error!("Unable to rasterize {:?} with {:?}, {:?}", key, render_mode, result);
            return None;
        }

        let dimensions = Self::get_glyph_dimensions_impl(slot).unwrap();
        let bitmap = unsafe { &(*slot).bitmap };
        let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
        info!("Rasterizing {:?} as {:?} with dimensions {:?}", key, render_mode, dimensions);
        // we may be filling only a part of the buffer, so initialize the whole thing with 0
        let mut final_buffer = Vec::with_capacity(dimensions.width as usize *
                                                  dimensions.height as usize *
                                                  4);

        let offset_x = dimensions.left - unsafe { (*slot).bitmap_left };

        for y in 0 .. dimensions.height {
            let src_y = y as i32 - dimensions.top + unsafe { (*slot).bitmap_top };
            if src_y < 0 || src_y >= bitmap.rows as i32 {
                for _x in 0 .. dimensions.width {
                    final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
                }
                continue
            }
            let base = unsafe {
                bitmap.buffer.offset((src_y * bitmap.pitch) as isize)
            };
            // determine the destination range of texels that `bitmap` provides data for
            let dst_start = cmp::max(0, -offset_x);
            let dst_end = cmp::min(dimensions.width as i32, bitmap.width as i32 - offset_x);
            for _x in 0 .. dst_start {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            match pixel_mode {
                FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
                    for x in dst_start .. dst_end {
                        let src_x = x + offset_x;
                        let mask = 0x80 >> (src_x & 0x7);
                        let byte = unsafe {
                            *base.offset((src_x >> 3) as isize)
                        };
                        let alpha = if byte & mask != 0 { 0xff } else { 0 };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
                    for x in dst_start .. dst_end {
                        let alpha = unsafe {
                            *base.offset((x + offset_x) as isize)
                        };
                        final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, alpha]);
                    }
                }
                FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                    for x in dst_start .. dst_end {
                        let src_x = ((x + offset_x) * 3) as isize;
                        assert!(src_x+2 < bitmap.pitch as isize);
                        let t = unsafe {
                            slice::from_raw_parts(base.offset(src_x), 3)
                        };
                        final_buffer.extend_from_slice(&[t[2], t[1], t[0], 0xff]);
                    }
                }
                _ => panic!("Unsupported {:?}", pixel_mode)
            }
            for _x in dst_end .. dimensions.width as i32 {
                final_buffer.extend_from_slice(&[0xff, 0xff, 0xff, 0]);
            }
            assert_eq!(final_buffer.len(), ((y+1) * dimensions.width * 4) as usize);
        }

        Some(RasterizedGlyph {
            width: dimensions.width as u32,
            height: dimensions.height as u32,
            bytes: final_buffer,
        })
    }
}

impl Drop for FontContext {
    fn drop(&mut self) {
        unsafe {
            FT_Done_FreeType(self.lib);
        }
    }
}
<|endoftext|>


<commit_before>use luminance::framebuffer::Framebuffer;
use luminance::pixel::{Depth32F, RGBA32F};
use luminance::tess::{Mode, Tess};
use luminance::texture::{Dim2, Flat, Texture, Unit};
use luminance::pipeline::Pipeline;
use luminance::shader::program;

pub use luminance::blending::{Equation, Factor};

use framebuffer::Framebuffer2D;
use resource::{Res, ResCache};
use shader::{Program, Uniform, UniformBuilder, UniformInterface, UniformWarning, UnwrapOrUnbound};

pub type ColorMap = Texture<Flat, Dim2, RGBA32F>;
pub type DepthMap = Texture<Flat, Dim2, Depth32F>;

/// Render layer.
///
/// A render layer is used whenever a render must be completed. It can be for 3D purposes – i.e. a
/// scene render – or for 2D effects. Combinators are provided to help you build such a layer.
pub struct Layer<'a> {
  render: Box<Fn(&Framebuffer2D<ColorMap, DepthMap>) + 'a>
}

impl<'a> Layer<'a> {
  /// Lowest construct to build a `Layer`.
  ///
  /// This function takes a closure and turns it into a `Layer`, enabling you to borrow things on
  /// the fly inside the closure if needed.
  pub fn new<F>(f: F) -> Self where F: 'a + Fn(&Framebuffer2D<ColorMap, DepthMap>) {
    Layer {
      render: Box::new(f)
    }
  }
}

/// Compositor object; used to consume `Layer`s and output to screen.
pub struct Compositor {
  // width
  w: u32,
  // height
  h: u32,
  // allocated framebuffers that might contain nodes’ output
  framebuffers: Vec<Framebuffer2D<ColorMap, DepthMap>>,
  // free list of available framebuffers
  free_framebuffers: Vec<usize>,
  // program used to compose nodes
  forward_program: Res<Program<QuadVert, (), ComposeUniforms>>,
  // program used to render scaled textures
  texture_program: Res<Program<QuadVert, (), TextureUniforms>>,
  // attributeless fullscreen quad for compositing
  quad: Tess<QuadVert>
}

type QuadVert = [f32; 2];

struct ComposeUniforms {
  source: Uniform<Unit>
}

impl UniformInterface for ComposeUniforms {
  fn uniform_interface(builder: UniformBuilder) -> program::Result<(Self, Vec<UniformWarning>)> {
    let mut warnings = Vec::new();

    let iface = ComposeUniforms {
      source: builder.ask("source").unwrap_or_unbound(&builder, &mut warnings)
    };

    Ok((iface, warnings))
  }
}

struct TextureUniforms {
  source: Uniform<Unit>,
  scale: Uniform<[f32; 2]>
}

impl UniformInterface for TextureUniforms {
  fn uniform_interface(builder: UniformBuilder) -> program::Result<(Self, Vec<UniformWarning>)> {
    let mut warnings = Vec::new();

    let iface = TextureUniforms {
      source: builder.ask("source").unwrap_or_unbound(&builder, &mut warnings),
      scale: builder.ask("scale").unwrap_or_unbound(&builder, &mut warnings)
    };

    Ok((iface, warnings))
  }
}

impl Compositor {
  /// Construct a new `Compositor` that will output to a screen which dimension is `w × h`.
  pub fn new(w: u32, h: u32, cache: &mut ResCache) -> Self {
    Compositor {
      w: w,
      h: h,
      framebuffers: Vec::new(),
      free_framebuffers: Vec::new(),
      forward_program: cache.get("spectra/compositing/forward.glsl", ()).unwrap(),
      texture_program: cache.get("spectra/compositing/texture.glsl", ()).unwrap(),
      quad: Tess::attributeless(Mode::TriangleStrip, 4)
    }
  }

  /// Whenever a layer must be composed, we need a framebuffer to render into. This function pulls a
  /// framebuffer to use (via self.framebuffers) by returning an index. It might allocate a new
  /// framebuffer if there isn’t enough framebuffers to be pulled.
  fn pull_framebuffer(&mut self) -> usize {
    self.free_framebuffers.pop().unwrap_or_else(|| {
      let framebuffer_index = self.framebuffers.len();

      let framebuffer = Framebuffer::new([self.w, self.h], 0).unwrap();
      self.framebuffers.push(framebuffer);

      framebuffer_index
    })
  }

  /// Whenever a layer has finished being composed, we *might* need to dispose the framebuffer it
  /// has pulled. This function does that job.
  ///
  /// It *never* deallocates memory. It brings in an important property: once a framebuffer is
  /// pulled, calling that function will make it available for other layers, improving memory usage
  /// for the next calls.
  #[inline]
  fn dispose_framebuffer(&mut self, framebuffer_index: usize) {
    self.free_framebuffers.push(framebuffer_index);
  }

  /// Consume and display a list of render layers.
  pub fn display<'a, L>(&mut self, layers: L) where L: IntoIterator<Item = &'a Layer<'a>> {
    let fb_index = self.pull_framebuffer();

    {
      let fb = &self.framebuffers[fb_index];

      for layer in layers {
        (layer.render)(fb);
      }

      let screen = Framebuffer::default([self.w, self.h]);
      Pipeline::new(&screen, [0., 0., 0., 1.], &[&*fb.color_slot], &[]).enter(|shd_gate| {
        shd_gate.new(&self.forward_program.borrow(), &[], &[]).enter(|rdr_gate, uniforms| {
          uniforms.source.update(Unit::new(0));

          rdr_gate.new(None, false, &[], &[]).enter(|tess_gate| {
            let quad = &self.quad;
            tess_gate.render(quad.into(), &[], &[])
          });
        });
      });
    }

    self.dispose_framebuffer(fb_index);
  }
}
<commit_msg>Removed texture handling from the compositor.<commit_after>use luminance::framebuffer::Framebuffer;
use luminance::pixel::{Depth32F, RGBA32F};
use luminance::tess::{Mode, Tess};
use luminance::texture::{Dim2, Flat, Texture, Unit};
use luminance::pipeline::Pipeline;
use luminance::shader::program;

pub use luminance::blending::{Equation, Factor};

use framebuffer::Framebuffer2D;
use resource::{Res, ResCache};
use shader::{Program, Uniform, UniformBuilder, UniformInterface, UniformWarning, UnwrapOrUnbound};

pub type ColorMap = Texture<Flat, Dim2, RGBA32F>;
pub type DepthMap = Texture<Flat, Dim2, Depth32F>;

/// Render layer.
///
/// A render layer is used whenever a render must be completed. It can be for 3D purposes – i.e. a
/// scene render – or for 2D effects. Combinators are provided to help you build such a layer.
pub struct Layer<'a> {
  render: Box<Fn(&Framebuffer2D<ColorMap, DepthMap>) + 'a>
}

impl<'a> Layer<'a> {
  /// Lowest construct to build a `Layer`.
  ///
  /// This function takes a closure and turns it into a `Layer`, enabling you to borrow things on
  /// the fly inside the closure if needed.
  pub fn new<F>(f: F) -> Self where F: 'a + Fn(&Framebuffer2D<ColorMap, DepthMap>) {
    Layer {
      render: Box::new(f)
    }
  }
}

/// Compositor object; used to consume `Layer`s and output to screen.
pub struct Compositor {
  // width
  w: u32,
  // height
  h: u32,
  // allocated framebuffers that might contain nodes’ output
  framebuffers: Vec<Framebuffer2D<ColorMap, DepthMap>>,
  // free list of available framebuffers
  free_framebuffers: Vec<usize>,
  // program used to compose nodes
  forward_program: Res<Program<QuadVert, (), ComposeUniforms>>,
  // attributeless fullscreen quad for compositing
  quad: Tess<QuadVert>
}

type QuadVert = [f32; 2];

struct ComposeUniforms {
  source: Uniform<Unit>
}

impl UniformInterface for ComposeUniforms {
  fn uniform_interface(builder: UniformBuilder) -> program::Result<(Self, Vec<UniformWarning>)> {
    let mut warnings = Vec::new();

    let iface = ComposeUniforms {
      source: builder.ask("source").unwrap_or_unbound(&builder, &mut warnings)
    };

    Ok((iface, warnings))
  }
}

impl Compositor {
  /// Construct a new `Compositor` that will output to a screen which dimension is `w × h`.
  pub fn new(w: u32, h: u32, cache: &mut ResCache) -> Self {
    Compositor {
      w: w,
      h: h,
      framebuffers: Vec::new(),
      free_framebuffers: Vec::new(),
      forward_program: cache.get("spectra/compositing/forward.glsl", ()).unwrap(),
      quad: Tess::attributeless(Mode::TriangleStrip, 4)
    }
  }

  /// Whenever a layer must be composed, we need a framebuffer to render into. This function pulls a
  /// framebuffer to use (via self.framebuffers) by returning an index. It might allocate a new
  /// framebuffer if there isn’t enough framebuffers to be pulled.
  fn pull_framebuffer(&mut self) -> usize {
    self.free_framebuffers.pop().unwrap_or_else(|| {
      let framebuffer_index = self.framebuffers.len();

      let framebuffer = Framebuffer::new([self.w, self.h], 0).unwrap();
      self.framebuffers.push(framebuffer);

      framebuffer_index
    })
  }

  /// Whenever a layer has finished being composed, we *might* need to dispose the framebuffer it
  /// has pulled. This function does that job.
  ///
  /// It *never* deallocates memory. It brings in an important property: once a framebuffer is
  /// pulled, calling that function will make it available for other layers, improving memory usage
  /// for the next calls.
  #[inline]
  fn dispose_framebuffer(&mut self, framebuffer_index: usize) {
    self.free_framebuffers.push(framebuffer_index);
  }

  /// Consume and display a list of render layers.
  pub fn display<'a, L>(&mut self, layers: L) where L: IntoIterator<Item = &'a Layer<'a>> {
    let fb_index = self.pull_framebuffer();

    {
      let fb = &self.framebuffers[fb_index];

      for layer in layers {
        (layer.render)(fb);
      }

      let screen = Framebuffer::default([self.w, self.h]);
      Pipeline::new(&screen, [0., 0., 0., 1.], &[&*fb.color_slot], &[]).enter(|shd_gate| {
        shd_gate.new(&self.forward_program.borrow(), &[], &[]).enter(|rdr_gate, uniforms| {
          uniforms.source.update(Unit::new(0));

          rdr_gate.new(None, false, &[], &[]).enter(|tess_gate| {
            let quad = &self.quad;
            tess_gate.render(quad.into(), &[], &[])
          });
        });
      });
    }

    self.dispose_framebuffer(fb_index);
  }
}
<|endoftext|>


<commit_before>//
use std::fs::File;
use std::io::prelude::*;
//
pub type Id = u64;
pub type Offset = u64;
pub type PageSize = usize;

//
#[derive(Debug)]
pub enum OpenMode {
    ReadOnly = 0,
    ReadWrite = 1,
}

/// The **Buffer** represent a linear array of bytes.

/// it can be in memory only or backed by an on disk file.

/// The editor **Modes** use this api to read/modify the content
/// of the file at the byte level
#[derive(Debug)]
pub struct Buffer {
    pub id: Id,
    pub file_name: String,
    pub size: usize, // proxy to underlying structs
    pub nr_changes: u64, // number of changes since last save
    pub file: File, //
    mode: OpenMode, //
    pub data: Vec<u8>, // file bytes
}

impl Buffer {
    /// Creates a new `Buffer`.
    ///
    /// file_name param[in] path to the file we want to load in the buffer,
    /// use "/dev/null" to create empty buffer
    /// this function allocate a buffer
    /// if file_name is null the content will be stored in heap
    /// if file_name is non null the the content will be read from the file
    /// if document_name is null , file_name will be used to give a name to the buffer
    /// mode = 0 : read only , mode 1 : read_write
    /// the allocated_bid pointer will be filled on successfull open operation
    pub fn new(file_name: &String, mode: OpenMode) -> Option<Buffer> {

        // TODO: check permission
        let mut file = match File::open(file_name) {
            Ok(f) => f,
            Err(e) => {
                println!("cannot open '{}' : {}", file_name, e);
                return None;
            }
        };

        // TODO: check file's type => ignore directory (for now)

        let mut data = Vec::new();
        let size = file.read_to_end(&mut data).unwrap_or(0);

        println!("'{}' opened mode '{:?}' size '{}'", file_name, mode, size);
        /*
            for c in &data {
                println!("c {} char '{}' ", *c, *c as char);
            }
        */
        Some(Buffer {
                 id: 0,
                 file_name: file_name.clone(),
                 mode,
                 size,
                 nr_changes: 0,
                 file,
                 data,
             })
    }

    /// close a previously opened buffer see buffer_open
    pub fn close(&mut self) -> bool {
        false
    }

    /// returns the name of the file associated to the buffer
    pub fn get_file_name(&self) -> String {
        self.file_name.clone()
    }

    /// change the on disk target file
    pub fn set_file_name(&mut self, name: String) -> bool {
        self.file_name = name;
        true
    }

    /// returns the number of bytes a given buffer contains
    pub fn size(&self) -> usize {
        self.size
    }

    /// returns the number of changes sine the last save

    ///     0  => the no change since last save

    ///     >0 => the number of changes since last save

    pub fn nr_changes(&self) -> u64 {
        self.nr_changes
    }

    /// copy the content of the buffer up to 'nr_bytes' into the data Vec
    /// the read bytes are appended to the data Vec
    /// return XXX on error (use ioresult)
    pub fn read(&self, offset: u64, nr_bytes: usize, data: &mut Vec<u8>) -> usize {
        let start_offset = ::std::cmp::min(offset as usize, self.size);
        let end_offset = ::std::cmp::min(start_offset + nr_bytes, self.size);
        let nr_copied = (end_offset - start_offset) as usize;
        data.reserve(nr_copied);
        for b in &self.data[start_offset..end_offset] {
            data.push(*b);
        }
        nr_copied
    }

    /// insert the 'data' Vec content in the buffer up to 'nr_bytes'
    /// return the number of written bytes (TODO: use io::Result)
    pub fn write(&mut self, offset: u64, nr_bytes: usize, data: &Vec<u8>) -> usize {

        let index = offset as usize;
        for n in 0..nr_bytes {
            self.data.insert(index + n, data[n]);
        }
        self.size += nr_bytes;
        self.nr_changes += 1;

        nr_bytes
    }

    /// remove up to 'nr_bytes' from the buffer starting at offset
    /// if removed_data is provided will call self.read(offset, nr_bytes, data)
    /// before remove the bytes
    pub fn remove(&mut self,
                  offset: u64,
                  nr_bytes: usize,
                  removed_data: Option<&mut Vec<u8>>)
                  -> usize {

        let start_offset = ::std::cmp::min(offset as usize, self.size);
        let end_offset = ::std::cmp::min(start_offset + nr_bytes, self.size);
        let nr_bytes_removed = (end_offset - start_offset) as usize;

        // copy removed data
        if let Some(v) = removed_data {
            self.read(offset, nr_bytes_removed, v);
        }

        self.data.drain(start_offset..end_offset);
        self.size -= nr_bytes_removed;
        self.nr_changes += 1;

        nr_bytes_removed
    }

    /// can be used to know the number of blocks that compose the buffer,
    /// api to be used by indexer etc...
    pub fn nr_pages(&self) -> u64 {
        1
    }

    /// returns the position and size of a given page
    pub fn get_page_info(&self, page_index: u64) -> (Offset, PageSize) {
        if page_index > 0 {
            (0, self.size)
        } else {
            (0, self.size)
        }
    }
}

#[test]
fn test_buffer() {
    let mut bb = Buffer::new(&"/dev/null".to_owned(), OpenMode::ReadWrite).unwrap();

    let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

    bb.write(0, 10, &data);
    assert_eq!(bb.data, data);
    assert_eq!(data.len(), bb.size());

    let mut rdata = Vec::new();

    let nread = bb.read(0, bb.size(), &mut rdata);
    assert_eq!(rdata, data);
    assert_eq!(nread, bb.size());

    let data = vec![0, 1, 2, 6, 7, 8, 9];
    let mut rm = vec![];
    let n = bb.remove(3, 3, Some(&mut rm));
    assert_eq!(n, 3);
    assert_eq!(bb.data, data);
    let rm_expect = vec![3, 4, 5];
    assert_eq!(rm, rm_expect);
    println!("rm {:?}", rm);
    println!("rm_expect {:?}", rm_expect);
}
<commit_msg>buffer api simplification<commit_after>//
use std::fs::File;
use std::io::prelude::*;
//
pub type Id = u64;
pub type Offset = u64;
pub type PageSize = usize;

//
#[derive(Debug)]
pub enum OpenMode {
    ReadOnly = 0,
    ReadWrite = 1,
}

/// The **Buffer** represent a linear array of bytes.

/// it can be in memory only or backed by an on disk file.

/// The editor **Modes** use this api to read/modify the content
/// of the file at the byte level
#[derive(Debug)]
pub struct Buffer {
    pub id: Id,
    pub file_name: String,
    pub size: usize, // proxy to underlying structs
    pub nr_changes: u64, // number of changes since last save
    pub file: File, //
    mode: OpenMode, //
    pub data: Vec<u8>, // file bytes
}

impl Buffer {
    /// Creates a new `Buffer`.
    ///
    /// file_name param[in] path to the file we want to load in the buffer,
    /// use "/dev/null" to create empty buffer
    /// this function allocate a buffer
    /// if file_name is null the content will be stored in heap
    /// if file_name is non null the the content will be read from the file
    /// if document_name is null , file_name will be used to give a name to the buffer
    /// mode = 0 : read only , mode 1 : read_write
    /// the allocated_bid pointer will be filled on successfull open operation
    pub fn new(file_name: &String, mode: OpenMode) -> Option<Buffer> {

        // TODO: check permission
        let mut file = match File::open(file_name) {
            Ok(f) => f,
            Err(e) => {
                println!("cannot open '{}' : {}", file_name, e);
                return None;
            }
        };

        // TODO: check file's type => ignore directory (for now)

        let mut data = Vec::new();
        let size = file.read_to_end(&mut data).unwrap_or(0);

        println!("'{}' opened mode '{:?}' size '{}'", file_name, mode, size);
        /*
            for c in &data {
                println!("c {} char '{}' ", *c, *c as char);
            }
        */
        Some(Buffer {
                 id: 0,
                 file_name: file_name.clone(),
                 mode,
                 size,
                 nr_changes: 0,
                 file,
                 data,
             })
    }

    /// close a previously opened buffer see buffer_open
    pub fn close(&mut self) -> bool {
        false
    }

    /// returns the name of the file associated to the buffer
    pub fn get_file_name(&self) -> String {
        self.file_name.clone()
    }

    /// change the on disk target file
    pub fn set_file_name(&mut self, name: String) -> bool {
        self.file_name = name;
        true
    }

    /// returns the number of bytes a given buffer contains
    pub fn size(&self) -> usize {
        self.size
    }

    /// returns the number of changes sine the last save

    ///     0  => the no change since last save

    ///     >0 => the number of changes since last save

    pub fn nr_changes(&self) -> u64 {
        self.nr_changes
    }

    /// copy the content of the buffer up to 'nr_bytes' into the data Vec
    /// the read bytes are appended to the data Vec
    /// return XXX on error (use ioresult)
    pub fn read(&self, offset: u64, nr_bytes: usize, data: &mut Vec<u8>) -> usize {
        let start_offset = ::std::cmp::min(offset as usize, self.size);
        let end_offset = ::std::cmp::min(start_offset + nr_bytes, self.size);
        let nr_copied = (end_offset - start_offset) as usize;
        data.reserve(nr_copied);
        for b in &self.data[start_offset..end_offset] {
            data.push(*b);
        }
        nr_copied
    }

    /// insert the 'data' Vec content in the buffer up to 'nr_bytes'
    /// return the number of written bytes (TODO: use io::Result)
    pub fn write(&mut self, offset: u64, nr_bytes: usize, data: &[u8]) -> usize {

        let index = offset as usize;
        for n in 0..nr_bytes {
            self.data.insert(index + n, data[n]);
        }
        self.size += nr_bytes;
        self.nr_changes += 1;

        nr_bytes
    }

    /// remove up to 'nr_bytes' from the buffer starting at offset
    /// if removed_data is provided will call self.read(offset, nr_bytes, data)
    /// before remove the bytes
    pub fn remove(&mut self,
                  offset: u64,
                  nr_bytes: usize,
                  removed_data: Option<&mut Vec<u8>>)
                  -> usize {

        let start_offset = ::std::cmp::min(offset as usize, self.size);
        let end_offset = ::std::cmp::min(start_offset + nr_bytes, self.size);
        let nr_bytes_removed = (end_offset - start_offset) as usize;

        // copy removed data
        if let Some(v) = removed_data {
            self.read(offset, nr_bytes_removed, v);
        }

        self.data.drain(start_offset..end_offset);
        self.size -= nr_bytes_removed;
        self.nr_changes += 1;

        nr_bytes_removed
    }

    /// can be used to know the number of blocks that compose the buffer,
    /// api to be used by indexer etc...
    pub fn nr_pages(&self) -> u64 {
        1
    }

    /// returns the position and size of a given page
    pub fn get_page_info(&self, page_index: u64) -> (Offset, PageSize) {
        if page_index > 0 {
            (0, self.size)
        } else {
            (0, self.size)
        }
    }
}

#[test]
fn test_buffer() {
    let mut bb = Buffer::new(&"/dev/null".to_owned(), OpenMode::ReadWrite).unwrap();

    let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

    bb.write(0, 10, &data);
    assert_eq!(bb.data, data);
    assert_eq!(data.len(), bb.size());

    let mut rdata = Vec::new();

    let nread = bb.read(0, bb.size(), &mut rdata);
    assert_eq!(rdata, data);
    assert_eq!(nread, bb.size());

    let data = vec![0, 1, 2, 6, 7, 8, 9];
    let mut rm = vec![];
    let n = bb.remove(3, 3, Some(&mut rm));
    assert_eq!(n, 3);
    assert_eq!(bb.data, data);
    let rm_expect = vec![3, 4, 5];
    assert_eq!(rm, rm_expect);
    println!("rm {:?}", rm);
    println!("rm_expect {:?}", rm_expect);
}
<|endoftext|>