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

use std::path::PathBuf;
use std::collections::BTreeMap;
use std::fs::File;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::Store;

use toml::Value;

use error::RefErrorKind as REK;
use error::RefError as RE;
use error::ResultExt;
use error::Result;
use flags::RefFlags;
use hasher::*;
use module_path::ModuleEntryPath;
use util::*;

pub trait RefStore {

    /// Check whether there is a reference to the file at `pb`
    fn exists(&self, pb: PathBuf) -> Result<bool>;

    /// Try to get `si` as Ref object from the store
    fn get<'a>(&'a self, si: StoreId) -> Result<FileLockEntry<'a>>;

    /// Get a Ref object from the store by hash.
    ///
    /// Returns None if the hash cannot be found.
    fn get_by_hash<'a>(&'a self, hash: String) -> Result<Option<FileLockEntry<'a>>>;

    /// Delete a ref by hash
    ///
    /// If the returned Result contains an error, the ref might not be deleted.
    fn delete_by_hash(&self, hash: String) -> Result<()>;

    /// Create a Ref object which refers to `pb`
    fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result<FileLockEntry<'a>>;

    fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, h: H)
        -> Result<FileLockEntry<'a>>;

}

impl RefStore for Store {

    /// Check whether there is a reference to the file at `pb`
    fn exists(&self, pb: PathBuf) -> Result<bool> {
        pb.canonicalize()
            .chain_err(|| REK::PathCanonicalizationError)
            .and_then(|c| hash_path(&c))
            .chain_err(|| REK::PathHashingError)
            .and_then(|hash| {
                self.retrieve_for_module("ref").map(|iter| (hash, iter)).map_err(From::from)
            })
            .and_then(|(hash, possible_refs)| {
                // This is kind of a manual Iterator::filter() call what we do here, but with the
                // actual ::filter method we cannot return the error in a nice way, so we do it
                // manually here. If you can come up with a better version of this, feel free to
                // take this note as a todo.
                for r in possible_refs {
                    let contains_hash = try!(r.to_str()
                        .chain_err(|| REK::TypeConversionError)
                        .map(|s| s.contains(&hash[..]))
                    );

                    if !contains_hash {
                        continue;
                    }

                    match self.get(r.clone())? {
                        Some(fle) => {
                            if read_reference(&fle).map(|path| path == pb).unwrap_or(false) {
                                return Ok(true)
                            }
                        },

                        None => {
                            let e = format!("Failed to get from store: {}", r);
                            return Err(e).map_err(From::from)
                        },
                    }
                }

                Ok(false)
            })
    }

    /// Try to get `si` as Ref object from the store
    fn get<'a>(&'a self, si: StoreId) -> Result<FileLockEntry<'a>> {
        match self.get(si)? {
            None      => return Err(RE::from_kind(REK::RefNotInStore)),
            Some(fle) => Ok(fle),
        }
    }

    /// Get a Ref object from the store by hash.
    ///
    /// Returns None if the hash cannot be found.
    fn get_by_hash<'a>(&'a self, hash: String) -> Result<Option<FileLockEntry<'a>>> {
        ModuleEntryPath::new(hash)
            .into_storeid()
            .and_then(|id| self.get(id))
            .map_err(From::from)
    }

    /// Delete a ref by hash
    ///
    /// If the returned Result contains an error, the ref might not be deleted.
    fn delete_by_hash(&self, hash: String) -> Result<()> {
        ModuleEntryPath::new(hash)
            .into_storeid()
            .and_then(|id| self.delete(id))
            .map_err(From::from)
    }

    /// Create a Ref object which refers to `pb`
    fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result<FileLockEntry<'a>> {
        self.create_with_hasher(pb, flags, DefaultHasher::new())
    }

    fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, mut h: H)
        -> Result<FileLockEntry<'a>>
    {
        use toml_query::insert::TomlValueInsertExt;

        if !pb.exists() {
            return Err(RE::from_kind(REK::RefTargetDoesNotExist));
        }
        if flags.get_content_hashing() && pb.is_dir() {
            return Err(RE::from_kind(REK::RefTargetCannotBeHashed));
        }

        let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
            try!(File::open(pb.clone())
                .chain_err(|| REK::RefTargetFileCannotBeOpened)

                // If we were able to open this file,
                // we hash the contents of the file and return (file, hash)
                .and_then(|mut file| {
                    let opt_contenthash = if flags.get_content_hashing() {
                        Some(try!(h.create_hash(&pb, &mut file)))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash))
                })

                // and then we get the permissions if we have to
                // and return (file, content hash, permissions)
                .and_then(|(file, opt_contenthash)| {
                    let opt_permissions = if flags.get_permission_tracking() {
                        Some(try!(file
                                  .metadata()
                                  .map(|md| md.permissions())
                                  .chain_err(|| REK::RefTargetCannotReadPermissions)
                        ))
                    } else {
                        None
                    };

                    Ok((opt_contenthash, opt_permissions))
                })

                // and then we try to canonicalize the PathBuf, because we want to store a
                // canonicalized path
                // and return (file, content hash, permissions, canonicalized path)
                .and_then(|(opt_contenthash, opt_permissions)| {
                    pb.canonicalize()
                        .map(|can| (opt_contenthash, opt_permissions, can))
                        // if PathBuf::canonicalize() failed, build an error from the return value
                        .chain_err(|| REK::PathCanonicalizationError)
                })

                // and then we hash the canonicalized path
                // and return (file, content hash, permissions, canonicalized path, path hash)
                .and_then(|(opt_contenthash, opt_permissions, can)| {
                    let path_hash = try!(hash_path(&can).chain_err(|| REK::PathHashingError));

                    Ok((opt_contenthash, opt_permissions, can, path_hash))
                })

                // and then we convert the PathBuf of the canonicalized path to a String to be able
                // to save it in the Ref FileLockEntry obj
                // and return
                // (file, content hash, permissions, canonicalized path as String, path hash)
                .and_then(|(opt_conhash, opt_perm, can, path_hash)| {
                    match can.to_str().map(String::from) {
                        // UTF convert error in PathBuf::to_str(),
                        None      => Err(RE::from_kind(REK::PathUTF8Error)),
                        Some(can) => Ok((opt_conhash, opt_perm, can, path_hash))
                    }
                })

                // and then we create the FileLockEntry in the Store
                // and return (filelockentry, content hash, permissions, canonicalized path)
                .and_then(|(opt_conhash, opt_perm, can, path_hash)| {
                    let fle = try!(self.create(ModuleEntryPath::new(path_hash)));
                    Ok((fle, opt_conhash, opt_perm, can))
                })
            )
        };

        for tpl in [
                Some((String::from("ref"),              Value::Table(BTreeMap::new()))),
                Some((String::from("ref.permissions"),  Value::Table(BTreeMap::new()))),
                Some((String::from("ref.path"),         Value::String(canonical_path))),
                Some((String::from("ref.content_hash"), Value::Table(BTreeMap::new()))),

                content_hash.map(|hash| {
                    (format!("ref.content_hash.{}", h.hash_name()), Value::String(hash))
                }),
                permissions.map(|p| {
                    (String::from("ref.permissions.ro"), Value::Boolean(p.readonly()))
                }),
            ].into_iter()
        {
            match tpl {
                &Some((ref s, ref v)) => {
                    match fle.get_header_mut().insert(s, v.clone()) {
                        Ok(Some(_)) => {
                            let e = RE::from_kind(REK::HeaderFieldAlreadyExistsError);
                            return Err(e).chain_err(|| REK::HeaderFieldWriteError);
                        },
                        Ok(None) => {
                            // Okay, we just inserted a new header value...
                        },
                        Err(e) => return Err(e).chain_err(|| REK::HeaderFieldWriteError),
                    }
                }
                &None => {
                    debug!("Not going to insert.");
                }
            }
        }

        Ok(fle)
    }

}

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

use std::path::PathBuf;
use std::collections::BTreeMap;
use std::fs::File;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::store::Store;

use toml::Value;

use error::RefErrorKind as REK;
use error::RefError as RE;
use error::ResultExt;
use error::Result;
use flags::RefFlags;
use hasher::*;
use module_path::ModuleEntryPath;
use util::*;

pub trait RefStore {

    /// Check whether there is a reference to the file at `pb`
    fn exists(&self, pb: PathBuf) -> Result<bool>;

    /// Try to get `si` as Ref object from the store
    fn get<'a>(&'a self, si: StoreId) -> Result<FileLockEntry<'a>>;

    /// Get a Ref object from the store by hash.
    ///
    /// Returns None if the hash cannot be found.
    fn get_by_hash<'a>(&'a self, hash: String) -> Result<Option<FileLockEntry<'a>>>;

    /// Delete a ref by hash
    ///
    /// If the returned Result contains an error, the ref might not be deleted.
    fn delete_by_hash(&self, hash: String) -> Result<()>;

    /// Create a Ref object which refers to `pb`
    fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result<FileLockEntry<'a>>;

    fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, h: H)
        -> Result<FileLockEntry<'a>>;

    /// Get all reference objects
    fn all_references(&self) -> Result<StoreIdIterator>;

}

impl RefStore for Store {

    /// Check whether there is a reference to the file at `pb`
    fn exists(&self, pb: PathBuf) -> Result<bool> {
        pb.canonicalize()
            .chain_err(|| REK::PathCanonicalizationError)
            .and_then(|c| hash_path(&c))
            .chain_err(|| REK::PathHashingError)
            .and_then(|hash| {
                self.retrieve_for_module("ref").map(|iter| (hash, iter)).map_err(From::from)
            })
            .and_then(|(hash, possible_refs)| {
                // This is kind of a manual Iterator::filter() call what we do here, but with the
                // actual ::filter method we cannot return the error in a nice way, so we do it
                // manually here. If you can come up with a better version of this, feel free to
                // take this note as a todo.
                for r in possible_refs {
                    let contains_hash = try!(r.to_str()
                        .chain_err(|| REK::TypeConversionError)
                        .map(|s| s.contains(&hash[..]))
                    );

                    if !contains_hash {
                        continue;
                    }

                    match self.get(r.clone())? {
                        Some(fle) => {
                            if read_reference(&fle).map(|path| path == pb).unwrap_or(false) {
                                return Ok(true)
                            }
                        },

                        None => {
                            let e = format!("Failed to get from store: {}", r);
                            return Err(e).map_err(From::from)
                        },
                    }
                }

                Ok(false)
            })
    }

    /// Try to get `si` as Ref object from the store
    fn get<'a>(&'a self, si: StoreId) -> Result<FileLockEntry<'a>> {
        match self.get(si)? {
            None      => return Err(RE::from_kind(REK::RefNotInStore)),
            Some(fle) => Ok(fle),
        }
    }

    /// Get a Ref object from the store by hash.
    ///
    /// Returns None if the hash cannot be found.
    fn get_by_hash<'a>(&'a self, hash: String) -> Result<Option<FileLockEntry<'a>>> {
        ModuleEntryPath::new(hash)
            .into_storeid()
            .and_then(|id| self.get(id))
            .map_err(From::from)
    }

    /// Delete a ref by hash
    ///
    /// If the returned Result contains an error, the ref might not be deleted.
    fn delete_by_hash(&self, hash: String) -> Result<()> {
        ModuleEntryPath::new(hash)
            .into_storeid()
            .and_then(|id| self.delete(id))
            .map_err(From::from)
    }

    /// Create a Ref object which refers to `pb`
    fn create<'a>(&'a self, pb: PathBuf, flags: RefFlags) -> Result<FileLockEntry<'a>> {
        self.create_with_hasher(pb, flags, DefaultHasher::new())
    }

    fn create_with_hasher<'a, H: Hasher>(&'a self, pb: PathBuf, flags: RefFlags, mut h: H)
        -> Result<FileLockEntry<'a>>
    {
        use toml_query::insert::TomlValueInsertExt;

        if !pb.exists() {
            return Err(RE::from_kind(REK::RefTargetDoesNotExist));
        }
        if flags.get_content_hashing() && pb.is_dir() {
            return Err(RE::from_kind(REK::RefTargetCannotBeHashed));
        }

        let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
            try!(File::open(pb.clone())
                .chain_err(|| REK::RefTargetFileCannotBeOpened)

                // If we were able to open this file,
                // we hash the contents of the file and return (file, hash)
                .and_then(|mut file| {
                    let opt_contenthash = if flags.get_content_hashing() {
                        Some(try!(h.create_hash(&pb, &mut file)))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash))
                })

                // and then we get the permissions if we have to
                // and return (file, content hash, permissions)
                .and_then(|(file, opt_contenthash)| {
                    let opt_permissions = if flags.get_permission_tracking() {
                        Some(try!(file
                                  .metadata()
                                  .map(|md| md.permissions())
                                  .chain_err(|| REK::RefTargetCannotReadPermissions)
                        ))
                    } else {
                        None
                    };

                    Ok((opt_contenthash, opt_permissions))
                })

                // and then we try to canonicalize the PathBuf, because we want to store a
                // canonicalized path
                // and return (file, content hash, permissions, canonicalized path)
                .and_then(|(opt_contenthash, opt_permissions)| {
                    pb.canonicalize()
                        .map(|can| (opt_contenthash, opt_permissions, can))
                        // if PathBuf::canonicalize() failed, build an error from the return value
                        .chain_err(|| REK::PathCanonicalizationError)
                })

                // and then we hash the canonicalized path
                // and return (file, content hash, permissions, canonicalized path, path hash)
                .and_then(|(opt_contenthash, opt_permissions, can)| {
                    let path_hash = try!(hash_path(&can).chain_err(|| REK::PathHashingError));

                    Ok((opt_contenthash, opt_permissions, can, path_hash))
                })

                // and then we convert the PathBuf of the canonicalized path to a String to be able
                // to save it in the Ref FileLockEntry obj
                // and return
                // (file, content hash, permissions, canonicalized path as String, path hash)
                .and_then(|(opt_conhash, opt_perm, can, path_hash)| {
                    match can.to_str().map(String::from) {
                        // UTF convert error in PathBuf::to_str(),
                        None      => Err(RE::from_kind(REK::PathUTF8Error)),
                        Some(can) => Ok((opt_conhash, opt_perm, can, path_hash))
                    }
                })

                // and then we create the FileLockEntry in the Store
                // and return (filelockentry, content hash, permissions, canonicalized path)
                .and_then(|(opt_conhash, opt_perm, can, path_hash)| {
                    let fle = try!(self.create(ModuleEntryPath::new(path_hash)));
                    Ok((fle, opt_conhash, opt_perm, can))
                })
            )
        };

        for tpl in [
                Some((String::from("ref"),              Value::Table(BTreeMap::new()))),
                Some((String::from("ref.permissions"),  Value::Table(BTreeMap::new()))),
                Some((String::from("ref.path"),         Value::String(canonical_path))),
                Some((String::from("ref.content_hash"), Value::Table(BTreeMap::new()))),

                content_hash.map(|hash| {
                    (format!("ref.content_hash.{}", h.hash_name()), Value::String(hash))
                }),
                permissions.map(|p| {
                    (String::from("ref.permissions.ro"), Value::Boolean(p.readonly()))
                }),
            ].into_iter()
        {
            match tpl {
                &Some((ref s, ref v)) => {
                    match fle.get_header_mut().insert(s, v.clone()) {
                        Ok(Some(_)) => {
                            let e = RE::from_kind(REK::HeaderFieldAlreadyExistsError);
                            return Err(e).chain_err(|| REK::HeaderFieldWriteError);
                        },
                        Ok(None) => {
                            // Okay, we just inserted a new header value...
                        },
                        Err(e) => return Err(e).chain_err(|| REK::HeaderFieldWriteError),
                    }
                }
                &None => {
                    debug!("Not going to insert.");
                }
            }
        }

        Ok(fle)
    }

    fn all_references(&self) -> Result<StoreIdIterator> {
        self.retrieve_for_module("ref").map_err(From::from)
    }
}

<|endoftext|>


<commit_before>use ansi_term::{ANSIString, Colour, Style};
use config::Config;

/// Merges in the default values for the program
pub fn merge_defaults(c: &mut Config) {
    c.set_default("global.modules",
                     vec!["exit_code", "directory", "git", "prompt"])
        .unwrap();
    c.set_default("global.foreground", "bright_white").unwrap();
    c.set_default("global.background", "blue").unwrap();
    c.set_default("global.style", "default").unwrap();
    c.set_default("global.separator", "").unwrap();
    c.set_default("global.padding_left", " ").unwrap();
    c.set_default("global.padding_right", " ").unwrap();

    c.set_default("modules.directory.max_depth", 4).unwrap();

    c.set_default("modules.exit_code.bg_success", "green").unwrap();
    c.set_default("modules.exit_code.bg_error", "red").unwrap();

    c.set_default("modules.git.symbol_insertion", "+").unwrap();
    c.set_default("modules.git.symbol_deletion", "-").unwrap();
    c.set_default("modules.git.symbol_push", "⇡").unwrap();
    c.set_default("modules.git.symbol_pull", "⇣").unwrap();
    c.set_default("modules.git.show_changed", true).unwrap();
    c.set_default("modules.git.show_diff_stats", false).unwrap();
    c.set_default("modules.git.show_unpushed", true).unwrap();

    c.set_default("modules.prompt.output", "$").unwrap();
    c.set_default("modules.prompt.bg_success", "green").unwrap();
    c.set_default("modules.prompt.bg_error", "red").unwrap();
}

/// Generic formatting method
pub fn format_module<'a>(c: &mut Config,
                         name: &'a str,
                         output: Option<String>,
                         last_successful: Option<&str>)
                         -> (Option<&'a str>, Option<ANSIString<'static>>) {
    // Formatting was not successful if there was nothing to format
    if c.get_str(&format!("modules.{}.output", name)).is_none() && output.is_none() {
        return (None, None);
    }

    // Get config options
    let fg = c.get_str(&format!("modules.{}.foreground", name))
        .unwrap_or_else(|| c.get_str("global.foreground").unwrap_or_default());
    let fg = string_to_colour(fg);

    let bg = c.get_str(&format!("modules.{}.background", name))
        .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
    let bg = string_to_colour(bg);

    let padding_left = c.get_str(&format!("modules.{}.padding_left", name))
        .unwrap_or_else(|| c.get_str("global.padding_left").unwrap_or_default());
    let padding_right = c.get_str(&format!("modules.{}.padding_right", name))
        .unwrap_or_else(|| c.get_str("global.padding_right").unwrap_or_default());
    let separator = c.get_str(&format!("modules.{}.separator", name))
        .unwrap_or_else(|| c.get_str("global.separator").unwrap_or_default());

    let main_style = c.get_str(&format!("modules.{}.style", name))
        .unwrap_or_else(|| c.get_str("global.style").unwrap_or_default());
    let main_style = string_to_style(main_style).on(bg).fg(fg);

    // Override the output if there is "output" in this module's config
    let output = if let Some(s) = c.get_str(&format!("modules.{}.output", name)) {
        s
    } else {
        output.unwrap()
    };

    // Format the main content
    let mut content = format!("\\[{}\\]{}{}{}\\[{}\\]",
                              main_style.prefix(),
                              padding_left,
                              output,
                              padding_right,
                              main_style.suffix());

    // Format the separator
    let main_style = main_style.on(fg).fg(bg);
    if let Some(name) = last_successful {
        // There is a visible module that comes after this one
        let next_bg = c.get_str(&format!("modules.{}.background", name))
            .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
        let next_bg = string_to_colour(next_bg);

        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          main_style.on(next_bg).prefix(),
                          separator,
                          main_style.on(next_bg).suffix());
    } else {
        // This is the final module
        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          bg.prefix(),
                          separator,
                          bg.suffix());
    }

    (Some(name), Some(ANSIString::from(content)))
}

// Converts a string (from the config file) to a Colour
// See: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
fn string_to_colour(s: String) -> Colour {
    if let Ok(i) = s.parse::<u8>() {
        Colour::Fixed(i)
    } else {
        let s = s.to_lowercase();
        match s.as_ref() {
            "black" => Colour::Fixed(0),
            "bright_black" => Colour::Fixed(8),
            "red" => Colour::Fixed(1),
            "bright_red" => Colour::Fixed(9),
            "green" => Colour::Fixed(2),
            "bright_green" => Colour::Fixed(10),
            "yellow" => Colour::Fixed(3),
            "bright_yellow" => Colour::Fixed(11),
            "blue" => Colour::Fixed(4),
            "bright_blue" => Colour::Fixed(12),
            "purple" => Colour::Fixed(5),
            "bright_purple" => Colour::Fixed(13),
            "cyan" => Colour::Fixed(6),
            "bright_cyan" => Colour::Fixed(14),
            "white" => Colour::Fixed(7),
            "bright_white" => Colour::Fixed(15),
            _ => panic!("Invalid color option: {} in config file!", s),
        }
    }
}

fn string_to_style(s: String) -> Style {
    let s = s.to_lowercase();
    match s.as_ref() {
        "default" | "" | "normal" => Style::new(),
        "bold" => Style::new().bold(),
        "dimmed" => Style::new().dimmed(),
        "italic" => Style::new().italic(),
        "underline" => Style::new().underline(),
        "blink" => Style::new().blink(),
        "reverse" => Style::new().reverse(),
        "hidden" => Style::new().hidden(),
        "strikethrough" => Style::new().strikethrough(),
        _ => panic!("Unknown style property: {} in config file!", s),
    }
}

pub fn format_module_prompt<'a>(c: &mut Config,
                                last_successful: Option<&'a str>,
                                exit_code: &str)
                                -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.prompt.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.prompt.bg_error").unwrap_or_default(),
    };
    c.set("modules.prompt.background", bg).unwrap();

    let output = c.get_str("modules.prompt.output").unwrap_or_default();

    format_module(c, "prompt", Some(output), last_successful)
}

pub fn format_module_exit_code<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>,
                                   exit_code: &str)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.exit_code.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.exit_code.bg_error").unwrap_or_default(),
    };
    c.set("modules.exit_code.background", bg).unwrap();

    format_module(c, "exit_code", Some(exit_code.to_string()), last_successful)
}

pub fn format_module_directory<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use std::env;
    use std::path::PathBuf;

    let home = env::var("HOME").unwrap();
    let cwd = env::current_dir().unwrap();

    // Convert "/home/user/directory" to "~/directory"
    let mut shortened_cwd: PathBuf;
    if let Ok(stripped_cwd) = cwd.strip_prefix(&home) {
        shortened_cwd = PathBuf::from("~").join(stripped_cwd);
    } else {
        shortened_cwd = env::current_dir().unwrap();
    }

    let depth = shortened_cwd.components().count();

    // Max number of directories we want to see
    let max_depth = c.get_int("modules.directory.max_depth").unwrap_or_default() as usize;

    if depth > max_depth {
        let comp_iter = shortened_cwd.clone();
        let comp_iter = comp_iter.components();

        shortened_cwd = PathBuf::new();
        for (i, component) in comp_iter.enumerate() {
            if i < (max_depth / 2) || i >= (depth - (max_depth / 2)) {
                shortened_cwd.push(component.as_os_str());
            } else if i == (max_depth / 2) {
                shortened_cwd.push("...");
            }
        }
    }

    format_module(c,
                  "directory",
                  Some(format!("{}", shortened_cwd.display())),
                  last_successful)
}

pub fn format_module_git<'a>(c: &mut Config,
                             last_successful: Option<&'a str>)
                             -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use git2::{Branch, Repository};
    use std::env;

    let mut output = String::new();

    if let Ok(repo) = Repository::discover(env::current_dir().unwrap_or_default()) {
        let local = repo.head().unwrap();

        // Output current branch name
        output.push_str(local.shorthand().unwrap());

        // Show local changes
        let show_diffs = c.get_bool("modules.git.show_diff_stats").unwrap_or_default();
        let show_changed = c.get_bool("modules.git.show_changed").unwrap_or_default();
        if show_changed {
            let diff_stats = repo.diff_index_to_workdir(None, None).unwrap();
            let diff_stats = diff_stats.stats().unwrap();

            if diff_stats.files_changed() > 0 {
                let symbol_insertion = c.get_str("modules.git.symbol_insertion")
                    .unwrap_or_default();

                if show_diffs {
                    // Show actual number of insertions or deletions
                    let symbol_deletion = c.get_str("modules.git.symbol_deletion")
                        .unwrap_or_default();
                    output.push_str(&format!(" ({}{}, {}{})",
                                             symbol_deletion,
                                             diff_stats.deletions(),
                                             symbol_insertion,
                                             diff_stats.insertions()));
                } else {
                    // Do not show insertions nor deletions, just say
                    // something was changed
                    output.push_str(&format!(" {}", symbol_insertion));
                }
            }
        }

        let local = Branch::wrap(local);

        // Show unpushed/unpulled commits
        let show_unpushed = c.get_bool("modules.git.show_unpushed").unwrap_or_default();
        if show_unpushed {
            if let Ok(upstream) = local.upstream() {
                let local_ref = local.get();
                let upstream_ref = upstream.get();

                if let Ok((ahead, behind)) =
                    repo.graph_ahead_behind(local_ref.target().unwrap(),
                                            upstream_ref.target().unwrap()) {

                    if ahead != 0 {
                        let symbol_push = c.get_str("modules.git.symbol_push").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_push, ahead));
                    }

                    if behind != 0 {
                        let symbol_pull = c.get_str("modules.git.symbol_pull").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_pull, behind));
                    }
                }
            }
        }
    }

    if output.is_empty() {
        (None, None)
    } else {
        format_module(c, "git", Some(output), last_successful)
    }
    // let symbol_clean = c.get_str("modules.git.symbol_clean").unwrap_or_default();
}

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

    #[test]
    fn test_string_to_colour() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            input: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         input: "green",
                         expected: &format!("\x1B[38;5;2m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         input: "bright_green",
                         expected: &format!("\x1B[38;5;10m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let result = string_to_colour(test.input.to_string());
            let result = format!("{}", result.paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    #[should_panic]
    fn test_string_to_colour_invalid_input() {
        struct Test {
            input: &'static str,
        }

        let tests = [Test { input: "green" }, Test { input: "invalid" }];

        for test in &tests {
            string_to_colour(test.input.to_string());
        }
    }
}
<commit_msg>fix: remove commented-out code<commit_after>use ansi_term::{ANSIString, Colour, Style};
use config::Config;

/// Merges in the default values for the program
pub fn merge_defaults(c: &mut Config) {
    c.set_default("global.modules",
                     vec!["exit_code", "directory", "git", "prompt"])
        .unwrap();
    c.set_default("global.foreground", "bright_white").unwrap();
    c.set_default("global.background", "blue").unwrap();
    c.set_default("global.style", "default").unwrap();
    c.set_default("global.separator", "").unwrap();
    c.set_default("global.padding_left", " ").unwrap();
    c.set_default("global.padding_right", " ").unwrap();

    c.set_default("modules.directory.max_depth", 4).unwrap();

    c.set_default("modules.exit_code.bg_success", "green").unwrap();
    c.set_default("modules.exit_code.bg_error", "red").unwrap();

    c.set_default("modules.git.symbol_insertion", "+").unwrap();
    c.set_default("modules.git.symbol_deletion", "-").unwrap();
    c.set_default("modules.git.symbol_push", "⇡").unwrap();
    c.set_default("modules.git.symbol_pull", "⇣").unwrap();
    c.set_default("modules.git.show_changed", true).unwrap();
    c.set_default("modules.git.show_diff_stats", false).unwrap();
    c.set_default("modules.git.show_unpushed", true).unwrap();

    c.set_default("modules.prompt.output", "$").unwrap();
    c.set_default("modules.prompt.bg_success", "green").unwrap();
    c.set_default("modules.prompt.bg_error", "red").unwrap();
}

/// Generic formatting method
pub fn format_module<'a>(c: &mut Config,
                         name: &'a str,
                         output: Option<String>,
                         last_successful: Option<&str>)
                         -> (Option<&'a str>, Option<ANSIString<'static>>) {
    // Formatting was not successful if there was nothing to format
    if c.get_str(&format!("modules.{}.output", name)).is_none() && output.is_none() {
        return (None, None);
    }

    // Get config options
    let fg = c.get_str(&format!("modules.{}.foreground", name))
        .unwrap_or_else(|| c.get_str("global.foreground").unwrap_or_default());
    let fg = string_to_colour(fg);

    let bg = c.get_str(&format!("modules.{}.background", name))
        .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
    let bg = string_to_colour(bg);

    let padding_left = c.get_str(&format!("modules.{}.padding_left", name))
        .unwrap_or_else(|| c.get_str("global.padding_left").unwrap_or_default());
    let padding_right = c.get_str(&format!("modules.{}.padding_right", name))
        .unwrap_or_else(|| c.get_str("global.padding_right").unwrap_or_default());
    let separator = c.get_str(&format!("modules.{}.separator", name))
        .unwrap_or_else(|| c.get_str("global.separator").unwrap_or_default());

    let main_style = c.get_str(&format!("modules.{}.style", name))
        .unwrap_or_else(|| c.get_str("global.style").unwrap_or_default());
    let main_style = string_to_style(main_style).on(bg).fg(fg);

    // Override the output if there is "output" in this module's config
    let output = if let Some(s) = c.get_str(&format!("modules.{}.output", name)) {
        s
    } else {
        output.unwrap()
    };

    // Format the main content
    let mut content = format!("\\[{}\\]{}{}{}\\[{}\\]",
                              main_style.prefix(),
                              padding_left,
                              output,
                              padding_right,
                              main_style.suffix());

    // Format the separator
    let main_style = main_style.on(fg).fg(bg);
    if let Some(name) = last_successful {
        // There is a visible module that comes after this one
        let next_bg = c.get_str(&format!("modules.{}.background", name))
            .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
        let next_bg = string_to_colour(next_bg);

        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          main_style.on(next_bg).prefix(),
                          separator,
                          main_style.on(next_bg).suffix());
    } else {
        // This is the final module
        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          bg.prefix(),
                          separator,
                          bg.suffix());
    }

    (Some(name), Some(ANSIString::from(content)))
}

// Converts a string (from the config file) to a Colour
// See: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
fn string_to_colour(s: String) -> Colour {
    if let Ok(i) = s.parse::<u8>() {
        Colour::Fixed(i)
    } else {
        let s = s.to_lowercase();
        match s.as_ref() {
            "black" => Colour::Fixed(0),
            "bright_black" => Colour::Fixed(8),
            "red" => Colour::Fixed(1),
            "bright_red" => Colour::Fixed(9),
            "green" => Colour::Fixed(2),
            "bright_green" => Colour::Fixed(10),
            "yellow" => Colour::Fixed(3),
            "bright_yellow" => Colour::Fixed(11),
            "blue" => Colour::Fixed(4),
            "bright_blue" => Colour::Fixed(12),
            "purple" => Colour::Fixed(5),
            "bright_purple" => Colour::Fixed(13),
            "cyan" => Colour::Fixed(6),
            "bright_cyan" => Colour::Fixed(14),
            "white" => Colour::Fixed(7),
            "bright_white" => Colour::Fixed(15),
            _ => panic!("Invalid color option: {} in config file!", s),
        }
    }
}

fn string_to_style(s: String) -> Style {
    let s = s.to_lowercase();
    match s.as_ref() {
        "default" | "" | "normal" => Style::new(),
        "bold" => Style::new().bold(),
        "dimmed" => Style::new().dimmed(),
        "italic" => Style::new().italic(),
        "underline" => Style::new().underline(),
        "blink" => Style::new().blink(),
        "reverse" => Style::new().reverse(),
        "hidden" => Style::new().hidden(),
        "strikethrough" => Style::new().strikethrough(),
        _ => panic!("Unknown style property: {} in config file!", s),
    }
}

pub fn format_module_prompt<'a>(c: &mut Config,
                                last_successful: Option<&'a str>,
                                exit_code: &str)
                                -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.prompt.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.prompt.bg_error").unwrap_or_default(),
    };
    c.set("modules.prompt.background", bg).unwrap();

    let output = c.get_str("modules.prompt.output").unwrap_or_default();

    format_module(c, "prompt", Some(output), last_successful)
}

pub fn format_module_exit_code<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>,
                                   exit_code: &str)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.exit_code.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.exit_code.bg_error").unwrap_or_default(),
    };
    c.set("modules.exit_code.background", bg).unwrap();

    format_module(c, "exit_code", Some(exit_code.to_string()), last_successful)
}

pub fn format_module_directory<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use std::env;
    use std::path::PathBuf;

    let home = env::var("HOME").unwrap();
    let cwd = env::current_dir().unwrap();

    // Convert "/home/user/directory" to "~/directory"
    let mut shortened_cwd: PathBuf;
    if let Ok(stripped_cwd) = cwd.strip_prefix(&home) {
        shortened_cwd = PathBuf::from("~").join(stripped_cwd);
    } else {
        shortened_cwd = env::current_dir().unwrap();
    }

    let depth = shortened_cwd.components().count();

    // Max number of directories we want to see
    let max_depth = c.get_int("modules.directory.max_depth").unwrap_or_default() as usize;

    if depth > max_depth {
        let comp_iter = shortened_cwd.clone();
        let comp_iter = comp_iter.components();

        shortened_cwd = PathBuf::new();
        for (i, component) in comp_iter.enumerate() {
            if i < (max_depth / 2) || i >= (depth - (max_depth / 2)) {
                shortened_cwd.push(component.as_os_str());
            } else if i == (max_depth / 2) {
                shortened_cwd.push("...");
            }
        }
    }

    format_module(c,
                  "directory",
                  Some(format!("{}", shortened_cwd.display())),
                  last_successful)
}

pub fn format_module_git<'a>(c: &mut Config,
                             last_successful: Option<&'a str>)
                             -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use git2::{Branch, Repository};
    use std::env;

    let mut output = String::new();

    if let Ok(repo) = Repository::discover(env::current_dir().unwrap_or_default()) {
        let local = repo.head().unwrap();

        // Output current branch name
        output.push_str(local.shorthand().unwrap());

        // Show local changes
        let show_diffs = c.get_bool("modules.git.show_diff_stats").unwrap_or_default();
        let show_changed = c.get_bool("modules.git.show_changed").unwrap_or_default();
        if show_changed {
            let diff_stats = repo.diff_index_to_workdir(None, None).unwrap();
            let diff_stats = diff_stats.stats().unwrap();

            if diff_stats.files_changed() > 0 {
                let symbol_insertion = c.get_str("modules.git.symbol_insertion")
                    .unwrap_or_default();

                if show_diffs {
                    // Show actual number of insertions or deletions
                    let symbol_deletion = c.get_str("modules.git.symbol_deletion")
                        .unwrap_or_default();
                    output.push_str(&format!(" ({}{}, {}{})",
                                             symbol_deletion,
                                             diff_stats.deletions(),
                                             symbol_insertion,
                                             diff_stats.insertions()));
                } else {
                    // Do not show insertions nor deletions, just say
                    // something was changed
                    output.push_str(&format!(" {}", symbol_insertion));
                }
            }
        }

        let local = Branch::wrap(local);

        // Show unpushed/unpulled commits
        let show_unpushed = c.get_bool("modules.git.show_unpushed").unwrap_or_default();
        if show_unpushed {
            if let Ok(upstream) = local.upstream() {
                let local_ref = local.get();
                let upstream_ref = upstream.get();

                if let Ok((ahead, behind)) =
                    repo.graph_ahead_behind(local_ref.target().unwrap(),
                                            upstream_ref.target().unwrap()) {

                    if ahead != 0 {
                        let symbol_push = c.get_str("modules.git.symbol_push").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_push, ahead));
                    }

                    if behind != 0 {
                        let symbol_pull = c.get_str("modules.git.symbol_pull").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_pull, behind));
                    }
                }
            }
        }
    }

    if output.is_empty() {
        (None, None)
    } else {
        format_module(c, "git", Some(output), last_successful)
    }
}

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

    #[test]
    fn test_string_to_colour() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            input: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         input: "green",
                         expected: &format!("\x1B[38;5;2m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         input: "bright_green",
                         expected: &format!("\x1B[38;5;10m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let result = string_to_colour(test.input.to_string());
            let result = format!("{}", result.paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    #[should_panic]
    fn test_string_to_colour_invalid_input() {
        struct Test {
            input: &'static str,
        }

        let tests = [Test { input: "green" }, Test { input: "invalid" }];

        for test in &tests {
            string_to_colour(test.input.to_string());
        }
    }
}
<|endoftext|>


<commit_before>use alloc::boxed::{Box, FnBox};

use super::stack::Stack;

#[cfg(target_arch = "x86")]
mod i686;

#[cfg(target_arch = "x86_64")]
mod x86_64;

mod imp {
    #[cfg(target_arch = "x86")]
    pub use super::i686::*;

    #[cfg(target_arch = "x86_64")]
    pub use super::x86_64::*;
}

/// A thread has to be wrapped by a calling function. This function
/// is responsible to setup/cleanup for the actual closure that the user wants
/// to call. This closure is passed as first parameter to the wrapping function
/// and is named 'f' here. This pointer *IS* extracted from a Box via a call
/// to Box::into_raw. The wrapper function is now responsible for its proper
/// deallocation when the thread is terminated
pub type WrapperFn = extern "C" fn(f: *mut u8) -> !;

#[derive(Debug)]
pub struct Context {
    regs: imp::Registers,
}

impl Context {
    pub unsafe fn new<F>(wrapper: WrapperFn, mut f: F, stack: &mut Stack) -> Self
        where F: FnMut() -> (), F: Send + 'static {
        let fun = move || {
            f();
        };
        let boxed_fun: Box<FnBox()> = Box::new(fun);

        let fun_ptr = Box::into_raw(Box::new(boxed_fun)) as *mut u8;

        Context {
            regs: imp::Registers::new(wrapper, fun_ptr, stack.top().unwrap()),
        }
    }

    /// Load the current context to the CPU
    pub fn load(&self) -> ! {
        unsafe {
            imp::registers_load(&self.regs);
        }
    }

    #[inline(always)]
    /// Save the current context. This function will actually return twice.
    /// The first return is just after saving the context and will return false
    /// The second time is when the saved context gets restored and will return
    /// true
    ///
    /// This function is always inlined because otherwise the stack frame gets
    /// corrupted as this will return twice. The second time the function
    /// returns the stack frame will most likely be corrupted. To avoid that
    /// inline the function to merge the stack frame of this function with
    /// the caller's.
    pub fn save(&mut self) -> bool {
        unsafe {
            imp::registers_save(&mut self.regs)
        }
    }
}
<commit_msg>thread/context: Silence a clippy warning<commit_after>use alloc::boxed::{Box, FnBox};

use super::stack::Stack;

#[cfg(target_arch = "x86")]
mod i686;

#[cfg(target_arch = "x86_64")]
mod x86_64;

mod imp {
    #[cfg(target_arch = "x86")]
    pub use super::i686::*;

    #[cfg(target_arch = "x86_64")]
    pub use super::x86_64::*;
}

/// A thread has to be wrapped by a calling function. This function
/// is responsible to setup/cleanup for the actual closure that the user wants
/// to call. This closure is passed as first parameter to the wrapping function
/// and is named 'f' here. This pointer *IS* extracted from a Box via a call
/// to Box::into_raw. The wrapper function is now responsible for its proper
/// deallocation when the thread is terminated
pub type WrapperFn = extern "C" fn(f: *mut u8) -> !;

#[derive(Debug)]
pub struct Context {
    regs: imp::Registers,
}

impl Context {
    pub unsafe fn new<F>(wrapper: WrapperFn, mut f: F, stack: &mut Stack) -> Self
        where F: FnMut() -> (), F: Send + 'static {
        let fun = move || {
            f();
        };
        let boxed_fun: Box<FnBox()> = Box::new(fun);

        let fun_ptr = Box::into_raw(Box::new(boxed_fun)) as *mut u8;

        Context {
            regs: imp::Registers::new(wrapper, fun_ptr, stack.top().unwrap()),
        }
    }

    /// Load the current context to the CPU
    pub fn load(&self) -> ! {
        unsafe {
            imp::registers_load(&self.regs);
        }
    }

    #[inline(always)]
    #[cfg_attr(feature = "clippy", allow(inline_always))]
    /// Save the current context. This function will actually return twice.
    /// The first return is just after saving the context and will return false
    /// The second time is when the saved context gets restored and will return
    /// true
    ///
    /// This function is always inlined because otherwise the stack frame gets
    /// corrupted as this will return twice. The second time the function
    /// returns the stack frame will most likely be corrupted. To avoid that
    /// inline the function to merge the stack frame of this function with
    /// the caller's.
    pub fn save(&mut self) -> bool {
        unsafe {
            imp::registers_save(&mut self.regs)
        }
    }
}
<|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)

extern crate ini;

use std::net::IpAddr;
use std::net::SocketAddr;

use self::ini::Ini;

use super::defaults;
use super::config::Config;
use super::config::ConfigListen;
use super::config::ConfigMemcached;

pub struct ReaderBuilder;
pub struct Reader;
struct ReaderGetterBuilder;
struct ReaderGetter;

static MODULE: &'static str = "config:reader";

impl ReaderBuilder {
    pub fn new() -> Reader {
        Reader {}
    }
}

impl Reader {
    pub fn read(&self, path: &'static str) -> Config {
        debug!("[{}] reading config file: {}", MODULE, path);

        let conf = Ini::load_from_file(path).unwrap();

        debug!("[{}] read config file: {}", MODULE, path);

        self.make(&conf)
    }

    fn make(&self, conf: &Ini) -> Config {
        let getter = ReaderGetterBuilder::new();

        Config {
            listen: ConfigListen {
                inet: getter.get_inet(&conf, "listen", "inet",
                "host", "port", defaults::LISTEN_HOST,
                defaults::LISTEN_PORT)
            },

            memcached: ConfigMemcached {
                inet: getter.get_inet(&conf, "memcached", "inet",
                "host", "port", defaults::MEMCACHED_HOST,
                defaults::MEMCACHED_PORT),

                max_key_size: getter.get_u32(&conf, "memcached",
                "max_key_size", defaults::MEMCACHED_MAX_KEY_SIZE),

                max_key_expiration: getter.get_u32(&conf, "memcached",
                "max_key_expiration", defaults::MEMCACHED_MAX_KEY_EXPIRATION),

                pool_size: getter.get_u8(&conf, "memcached",
                "pool_size", defaults::MEMCACHED_POOL_SIZE),

                reconnect: getter.get_u16(&conf, "memcached",
                "reconnect", defaults::MEMCACHED_RECONNECT),

                timeout: getter.get_u16(&conf, "memcached",
                "timeout", defaults::MEMCACHED_TIMEOUT)
            }
        }
    }
}

impl ReaderGetterBuilder {
    pub fn new() -> ReaderGetter {
        ReaderGetter {}
    }
}

impl ReaderGetter {
    pub fn get_inet(
        &self, conf: &Ini, group: &'static str, key: &'static str,
        key_host: &'static str, key_port: &'static str,
        default_host: &'static str, default_port: &'static str
    ) -> SocketAddr {
        let value_host = (*conf).get_from_or(Some(group), key_host,
            default_host).parse::<IpAddr>().unwrap();

        let value_port = (*conf).get_from_or(Some(group), key_port,
            default_port).parse::<u16>().unwrap();

        let value_inet = SocketAddr::new(value_host, value_port);

        debug!("[{}] parsed @{}:{} <inet> => {}", MODULE, group, key,
            value_inet);

        value_inet
    }

    pub fn get_u8(
        &self, conf: &Ini, group: &'static str, key: &'static str,
        default: &'static str
    ) -> u8 {
        let value_u8 = (*conf).get_from_or(Some(group), key,
            default).parse::<u8>().unwrap();

        debug!("[{}] parsed @{}:{} <u8> => {}", MODULE, group, key, value_u8);

        value_u8
    }

    pub fn get_u16(
        &self, conf: &Ini, group: &'static str, key: &'static str,
        default: &'static str
    ) -> u16 {
        let value_u16 = (*conf).get_from_or(Some(group), key,
            default).parse::<u16>().unwrap();

        debug!("[{}] parsed @{}:{} <u16> => {}", MODULE, group, key, value_u16);

        value_u16
    }

    pub fn get_u32(
        &self, conf: &Ini, group: &'static str, key: &'static str,
        default: &'static str
    ) -> u32 {
        let value_u32 = (*conf).get_from_or(Some(group), key,
            default).parse::<u32>().unwrap();

        debug!("[{}] parsed @{}:{} <u32> => {}", MODULE, group, key, value_u32);

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

extern crate ini;

use std::net::IpAddr;
use std::net::SocketAddr;

use self::ini::Ini;

use super::defaults;
use super::config::Config;
use super::config::ConfigListen;
use super::config::ConfigMemcached;

pub struct ReaderBuilder;
pub struct Reader;
struct ReaderGetter;

static MODULE: &'static str = "config:reader";

impl ReaderBuilder {
    pub fn new() -> Reader {
        Reader {}
    }
}

impl Reader {
    pub fn read(&self, path: &'static str) -> Config {
        debug!("[{}] reading config file: {}", MODULE, path);

        let conf = Ini::load_from_file(path).unwrap();

        debug!("[{}] read config file: {}", MODULE, path);

        self.make(&conf)
    }

    fn make(&self, conf: &Ini) -> Config {
        Config {
            listen: ConfigListen {
                inet: ReaderGetter::get_inet(&conf, "listen", "inet",
                "host", "port", defaults::LISTEN_HOST,
                defaults::LISTEN_PORT)
            },

            memcached: ConfigMemcached {
                inet: ReaderGetter::get_inet(&conf, "memcached", "inet",
                "host", "port", defaults::MEMCACHED_HOST,
                defaults::MEMCACHED_PORT),

                max_key_size: ReaderGetter::get_u32(&conf, "memcached",
                "max_key_size", defaults::MEMCACHED_MAX_KEY_SIZE),

                max_key_expiration: ReaderGetter::get_u32(&conf, "memcached",
                "max_key_expiration", defaults::MEMCACHED_MAX_KEY_EXPIRATION),

                pool_size: ReaderGetter::get_u8(&conf, "memcached",
                "pool_size", defaults::MEMCACHED_POOL_SIZE),

                reconnect: ReaderGetter::get_u16(&conf, "memcached",
                "reconnect", defaults::MEMCACHED_RECONNECT),

                timeout: ReaderGetter::get_u16(&conf, "memcached",
                "timeout", defaults::MEMCACHED_TIMEOUT)
            }
        }
    }
}

impl ReaderGetter {
    fn get_inet(
        conf: &Ini, group: &'static str, key: &'static str,
        key_host: &'static str, key_port: &'static str,
        default_host: &'static str, default_port: &'static str
    ) -> SocketAddr {
        let value_host = (*conf).get_from_or(Some(group), key_host,
            default_host).parse::<IpAddr>().unwrap();

        let value_port = (*conf).get_from_or(Some(group), key_port,
            default_port).parse::<u16>().unwrap();

        let value_inet = SocketAddr::new(value_host, value_port);

        debug!("[{}] parsed @{}:{} <inet> => {}", MODULE, group, key,
            value_inet);

        value_inet
    }

    fn get_u8(
        conf: &Ini, group: &'static str, key: &'static str,
        default: &'static str
    ) -> u8 {
        let value_u8 = (*conf).get_from_or(Some(group), key,
            default).parse::<u8>().unwrap();

        debug!("[{}] parsed @{}:{} <u8> => {}", MODULE, group, key, value_u8);

        value_u8
    }

    fn get_u16(
        conf: &Ini, group: &'static str, key: &'static str,
        default: &'static str
    ) -> u16 {
        let value_u16 = (*conf).get_from_or(Some(group), key,
            default).parse::<u16>().unwrap();

        debug!("[{}] parsed @{}:{} <u16> => {}", MODULE, group, key, value_u16);

        value_u16
    }

    fn get_u32(
        conf: &Ini, group: &'static str, key: &'static str,
        default: &'static str
    ) -> u32 {
        let value_u32 = (*conf).get_from_or(Some(group), key,
            default).parse::<u32>().unwrap();

        debug!("[{}] parsed @{}:{} <u32> => {}", MODULE, group, key, value_u32);

        value_u32
    }
}
<|endoftext|>


<commit_before>use std::fmt::{Debug, Formatter, Error};
use std::path::Path;

use config::reader::from_file;
use config::types::Config as Cfg;
use cli::CliConfig;

pub struct Configuration {
    pub rtp         : String,
    pub store_sub   : String,
    pub verbose     : bool,
    pub debugging   : bool,
    pub editor      : Option<String>,
    pub editor_opts : String,
}

impl Configuration {

    pub fn new(config: &CliConfig) -> Configuration {
        let rtp = rtp_path(config).or(default_path());

        let mut verbose     = false;
        let mut debugging   = false;
        let mut store_sub   = String::from("/store");
        let mut editor      = None;
        let mut editor_opts = String::from("");

        if let Some(cfg) = fetch_config(rtp.clone()) {
            if let Some(v) = cfg.lookup_boolean("verbose") {
                verbose = v;
            }
            if let Some(d) = cfg.lookup_boolean("debug") {
                debugging = d;
            }
            if let Some(s) = cfg.lookup_str("store") {
                store_sub = String::from(s);
            }
            if let Some(s) = cfg.lookup_str("editor") {
                editor = Some(String::from(s));
            }
            if let Some(s) = cfg.lookup_str("editor-opts") {
                editor_opts = String::from(s);
            }
        }

        let runtimepath = rtp.unwrap_or(String::from("/tmp/"));

        debug!("Building configuration");
        debug!("  - verbose    : {}", verbose);
        debug!("  - debugging  : {}", debugging);
        debug!("  - store sub  : {}", store_sub);
        debug!("  - runtimepath: {}", runtimepath);
        debug!("  - editor     : {}", editor.unwrap_or(String::from("")));
        debug!("  - editor-opts: {}", editor_opts);

        Configuration {
            verbose: verbose,
            debugging: debugging,
            store_sub: store_sub,
            rtp: runtimepath,
            editor: editor,
            editor_opts: editor_opts,
        }
    }

    pub fn is_verbose(&self) -> bool {
        self.verbose
    }

    pub fn is_debugging(&self) -> bool {
        self.debugging
    }

    pub fn store_path(&self) -> String {
        format!("{}{}", self.rtp, self.store_sub)
    }

    pub fn get_rtp(&self) -> String {
        self.rtp.clone()
    }

    pub fn editor(&self) -> Option<String> {
        self.editor
    }

    pub fn editor_opts(&self) -> &String {
        &self.editor_opts
    }

}

fn rtp_path(config: &CliConfig) -> Option<String> {
    config.cli_matches.value_of("rtp")
                      .and_then(|s| Some(String::from(s)))
}

fn fetch_config(rtp: Option<String>) -> Option<Cfg> {
    rtp.and_then(|r| from_file(Path::new(&(r.clone() + "/config"))).ok())
}

fn default_path() -> Option<String> {
    use std::env::home_dir;

    home_dir().and_then(|mut buf| {
        buf.push("/.imag");
        buf.to_str().map(|s| String::from(s))
    })

}

impl Debug for Configuration {

    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "Configuration (verbose: {}, debugging: {}, rtp: {}, store path: {})",
            self.is_verbose(),
            self.is_debugging(),
            self.get_rtp(),
            self.store_path()
            )
    }

}

<commit_msg>configuration fixup editor<commit_after>use std::fmt::{Debug, Formatter, Error};
use std::path::Path;

use config::reader::from_file;
use config::types::Config as Cfg;
use cli::CliConfig;

pub struct Configuration {
    pub rtp         : String,
    pub store_sub   : String,
    pub verbose     : bool,
    pub debugging   : bool,
    pub editor      : Option<String>,
    pub editor_opts : String,
}

impl Configuration {

    pub fn new(config: &CliConfig) -> Configuration {
        let rtp = rtp_path(config).or(default_path());

        let mut verbose     = false;
        let mut debugging   = false;
        let mut store_sub   = String::from("/store");
        let mut editor      = None;
        let mut editor_opts = String::from("");

        if let Some(cfg) = fetch_config(rtp.clone()) {
            if let Some(v) = cfg.lookup_boolean("verbose") {
                verbose = v;
            }
            if let Some(d) = cfg.lookup_boolean("debug") {
                debugging = d;
            }
            if let Some(s) = cfg.lookup_str("store") {
                store_sub = String::from(s);
            }
            if let Some(s) = cfg.lookup_str("editor") {
                editor = Some(String::from(s));
            }
            if let Some(s) = cfg.lookup_str("editor-opts") {
                editor_opts = String::from(s);
            }
        }

        let runtimepath = rtp.unwrap_or(String::from("/tmp/"));

        debug!("Building configuration");
        debug!("  - verbose    : {}", verbose);
        debug!("  - debugging  : {}", debugging);
        debug!("  - store sub  : {}", store_sub);
        debug!("  - runtimepath: {}", runtimepath);
        debug!("  - editor     : {:?}", editor);
        debug!("  - editor-opts: {}", editor_opts);

        Configuration {
            verbose: verbose,
            debugging: debugging,
            store_sub: store_sub,
            rtp: runtimepath,
            editor: editor,
            editor_opts: editor_opts,
        }
    }

    pub fn is_verbose(&self) -> bool {
        self.verbose
    }

    pub fn is_debugging(&self) -> bool {
        self.debugging
    }

    pub fn store_path(&self) -> String {
        format!("{}{}", self.rtp, self.store_sub)
    }

    pub fn get_rtp(&self) -> String {
        self.rtp.clone()
    }

    pub fn editor(&self) -> Option<String> {
        self.editor.clone()
    }

    pub fn editor_opts(&self) -> &String {
        &self.editor_opts
    }

}

fn rtp_path(config: &CliConfig) -> Option<String> {
    config.cli_matches.value_of("rtp")
                      .and_then(|s| Some(String::from(s)))
}

fn fetch_config(rtp: Option<String>) -> Option<Cfg> {
    rtp.and_then(|r| from_file(Path::new(&(r.clone() + "/config"))).ok())
}

fn default_path() -> Option<String> {
    use std::env::home_dir;

    home_dir().and_then(|mut buf| {
        buf.push("/.imag");
        buf.to_str().map(|s| String::from(s))
    })

}

impl Debug for Configuration {

    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "Configuration (verbose: {}, debugging: {}, rtp: {}, store path: {})",
            self.is_verbose(),
            self.is_debugging(),
            self.get_rtp(),
            self.store_path()
            )
    }

}

<|endoftext|>


<commit_before>extern crate gtk;
extern crate glib;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::mpsc::{channel, Receiver, Sender};

use gtk::prelude::*;
use gtk::{ApplicationWindow, HeaderBar, Button,
          FileChooserDialog, ResponseType, FileChooserAction};

use ::media::{Context, ContextMessage};
use ::media::ContextMessage::*;

use super::{AudioController, InfoController, MediaHandler, VideoController};

pub struct MainController {
    window: ApplicationWindow,
    header_bar: HeaderBar,
    info_ctrl: InfoController,
    video_ctrl: VideoController,
    audio_ctrl: Rc<RefCell<AudioController>>,

    ctx: Option<Context>,

    self_weak: Option<Weak<RefCell<MainController>>>,
    listener_src: Option<glib::SourceId>
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            info_ctrl: InfoController::new(&builder),
            video_ctrl: VideoController::new(&builder),
            audio_ctrl: AudioController::new(&builder),
            ctx: None,
            self_weak: None,
            listener_src: None,
        }));

        {
            // TODO: stop any playing context or pending_ctx
            let mut this_mut = this.borrow_mut();
            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_weak = Rc::downgrade(&this);
            this_mut.self_weak = Some(this_weak);
        }

        let open_btn: Button = builder.get_object("open-btn").unwrap();
        let this_weak = Rc::downgrade(&this);
        open_btn.connect_clicked(move |_| {
            let this = this_weak.upgrade()
                .expect("Main controller is no longer available for select_media");
            this.borrow_mut().select_media();
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn stop(&mut self) {
        if let Some(context) = self.ctx.as_ref() {
            context.stop();
            if let Some(source_id) = self.listener_src {
                // remove listerner in order to avoid conflict on borrowing of self
                glib::source_remove(source_id);
            }
            self.listener_src = None;
        }
    }

    fn process_message(&mut self,
        message: ContextMessage,
        ui_tx_opt: Option<&Sender<ContextMessage>>
    ) -> bool
    {
        let mut keep_going = true;
        match message {
            AsyncDone => {
                println!("Received AsyncDone");
            },
            InitDone => {
                println!("Received InitDone");

                let ref context = self.ctx.as_ref()
                    .expect("Received InitDone, but context is not available");

                self.info_ctrl.new_media(context);
                self.video_ctrl.new_media(context);
                self.audio_ctrl.borrow_mut().new_media(context);

                self.header_bar.set_subtitle(Some(context.file_name.as_str()));
            },
            Eos => {
                println!("Received Eos");
                keep_going = false;
            },
            FailedToOpenMedia => {
                eprintln!("ERROR: failed to open media");
                self.ctx = None;
                // TODO: clear UI
                keep_going = false;
            },
            HaveAudioBuffer(audio_buffer) => {
                self.audio_ctrl.borrow_mut().have_buffer(audio_buffer);
                //println!("Received HaveAudioBuffer");
                /*println!("Received AudioBuffer with offset: {}, pts: {:?}, dts: {:?}, duration: {:?}",
                    buffer.get_offset(), buffer.get_pts(),
                    buffer.get_dts(), buffer.get_duration()
                );*/
            },
            HaveVideoWidget(video_widget) => {
                //println!("Received HaveVideoWidget");
                let ui_tx = ui_tx_opt.as_ref()
                    .expect("Received HaveVideoWidget, but no ui_tx is defined");
                self.video_ctrl.have_widget(video_widget);
                ui_tx.send(GotVideoWidget)
                    .expect("Failed to send GotVideoWidget to context");
            },
            _ => {
                eprintln!("Received an unexpected message => will quit listner");
                keep_going = false;
            },
        };

        if !keep_going {
            self.listener_src = None;
        }

        keep_going
    }

    fn select_media(&mut self) {
        self.stop();

        let file_dlg = FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", ResponseType::Ok.into());

        let result = file_dlg.run();

        // Note: couldn't find a way to coerce ResponseType to i32 in a match statement
        if result == ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }
        else { () }

        file_dlg.close();
    }

    fn register_listener(&mut self,
        ui_rx: Receiver<ContextMessage>,
        timeout: u32,
        ui_tx_opt: Option<Sender<ContextMessage>>,
    )
    {
        let self_weak = self.self_weak.as_ref()
            .expect("Failed to get ref on MainController's weak Rc for register_listener")
            .clone();

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;
            let mut msg_iter = ui_rx.try_iter();

            let first_msg_opt = msg_iter.next();
            if let Some(first_msg) = first_msg_opt {
                // only get this as mut if a message is received
                let this = self_weak.upgrade()
                    .expect("Main controller is no longer available for ctx channel listener");
                let mut this_mut = this.borrow_mut();
                keep_going = this_mut.process_message(first_msg, ui_tx_opt.as_ref());
                if keep_going {
                    // process remaining messages
                    for msg in msg_iter {
                        keep_going = this_mut.process_message(msg, ui_tx_opt.as_ref());
                        if !keep_going { break; }
                    }
                }
            };

            if !keep_going { println!("Exiting listener"); }
            glib::Continue(keep_going)
        }));
    }

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

        self.audio_ctrl.borrow_mut().clear();

        let (ctx_tx, ui_rx) = channel();
        let (ui_tx, ctx_rx) = channel();

        self.register_listener(ui_rx, 100, Some(ui_tx));

        match Context::open_media_path(filepath, ctx_tx, ctx_rx) {
            Ok(ctx) => self.ctx = Some(ctx),
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<commit_msg>Minor cleanups (#7)<commit_after>extern crate gtk;
extern crate glib;

use std::rc::{Rc, Weak};
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::mpsc::{channel, Receiver, Sender};

use gtk::prelude::*;
use gtk::{ApplicationWindow, HeaderBar, Button,
          FileChooserDialog, ResponseType, FileChooserAction};

use ::media::{Context, ContextMessage};
use ::media::ContextMessage::*;

use super::{AudioController, InfoController, MediaHandler, VideoController};

pub struct MainController {
    window: ApplicationWindow,
    header_bar: HeaderBar,
    info_ctrl: InfoController,
    video_ctrl: VideoController,
    audio_ctrl: Rc<RefCell<AudioController>>,

    ctx: Option<Context>,

    self_weak: Option<Weak<RefCell<MainController>>>,
    listener_src: Option<glib::SourceId>
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            info_ctrl: InfoController::new(&builder),
            video_ctrl: VideoController::new(&builder),
            audio_ctrl: AudioController::new(&builder),
            ctx: None,
            self_weak: None,
            listener_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();
            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_weak = Rc::downgrade(&this);
            this_mut.self_weak = Some(this_weak);
        }

        let open_btn: Button = builder.get_object("open-btn").unwrap();
        let this_weak = Rc::downgrade(&this);
        open_btn.connect_clicked(move |_| {
            let this = this_weak.upgrade()
                .expect("Main controller is no longer available for select_media");
            this.borrow_mut().select_media();
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn stop(&mut self) {
        if let Some(context) = self.ctx.as_ref() {
            context.stop();
            if let Some(source_id) = self.listener_src {
                // remove listerner in order to avoid conflict on borrowing of self
                glib::source_remove(source_id);
            }
            self.listener_src = None;
        }
    }

    fn process_message(&mut self,
        message: ContextMessage,
        ui_tx_opt: Option<&Sender<ContextMessage>>
    ) -> bool
    {
        let mut keep_going = true;
        match message {
            AsyncDone => {
                println!("Received AsyncDone");
            },
            InitDone => {
                println!("Received InitDone");

                let ref context = self.ctx.as_ref()
                    .expect("Received InitDone, but context is not available");

                self.info_ctrl.new_media(context);
                self.video_ctrl.new_media(context);
                self.audio_ctrl.borrow_mut().new_media(context);

                self.header_bar.set_subtitle(Some(context.file_name.as_str()));
            },
            Eos => {
                println!("Received Eos");
                keep_going = false;
            },
            FailedToOpenMedia => {
                eprintln!("ERROR: failed to open media");
                self.ctx = None;
                keep_going = false;
            },
            HaveAudioBuffer(audio_buffer) => {
                self.audio_ctrl.borrow_mut().have_buffer(audio_buffer);
            },
            HaveVideoWidget(video_widget) => {
                //println!("Received HaveVideoWidget");
                let ui_tx = ui_tx_opt.as_ref()
                    .expect("Received HaveVideoWidget, but no ui_tx is defined");
                self.video_ctrl.have_widget(video_widget);
                ui_tx.send(GotVideoWidget)
                    .expect("Failed to send GotVideoWidget to context");
            },
            _ => {
                eprintln!("Received an unexpected message => will quit listner");
                keep_going = false;
            },
        };

        if !keep_going {
            self.listener_src = None;
        }

        keep_going
    }

    fn select_media(&mut self) {
        self.stop();

        let file_dlg = FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", ResponseType::Ok.into());

        let result = file_dlg.run();

        if result == ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }
        else { () }

        file_dlg.close();
    }

    fn register_listener(&mut self,
        ui_rx: Receiver<ContextMessage>,
        timeout: u32,
        ui_tx_opt: Option<Sender<ContextMessage>>,
    )
    {
        let this_weak = self.self_weak.as_ref()
            .expect("Failed to get ref on MainController's weak Rc for register_listener")
            .clone();

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;
            let mut msg_iter = ui_rx.try_iter();

            let first_msg_opt = msg_iter.next();
            if let Some(first_msg) = first_msg_opt {
                // only get this as mut if a message is received
                let this = this_weak.upgrade()
                    .expect("Main controller is no longer available for ctx channel listener");
                let mut this_mut = this.borrow_mut();
                keep_going = this_mut.process_message(first_msg, ui_tx_opt.as_ref());
                if keep_going {
                    // process remaining messages
                    for msg in msg_iter {
                        keep_going = this_mut.process_message(msg, ui_tx_opt.as_ref());
                        if !keep_going { break; }
                    }
                }
            };

            if !keep_going { println!("Exiting listener"); }
            glib::Continue(keep_going)
        }));
    }

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

        self.audio_ctrl.borrow_mut().clear();

        let (ctx_tx, ui_rx) = channel();
        let (ui_tx, ctx_rx) = channel();

        self.register_listener(ui_rx, 100, Some(ui_tx));

        match Context::open_media_path(filepath, ctx_tx, ctx_rx) {
            Ok(ctx) => self.ctx = Some(ctx),
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<|endoftext|>


<commit_before>use crate::{convert, ops};

/// Used to tell an operation whether it should exit early or go on as usual.
///
/// This is used when exposing things (like graph traversals or visitors) where
/// you want the user to be able to choose whether to exit early.
/// Having the enum makes it clearer -- no more wondering "wait, what did `false`
/// mean again?" -- and allows including a value.
///
/// # Examples
///
/// Early-exiting from [`Iterator::try_for_each`]:
/// ```
/// #![feature(control_flow_enum)]
/// use std::ops::ControlFlow;
///
/// let r = (2..100).try_for_each(|x| {
///     if 403 % x == 0 {
///         return ControlFlow::Break(x)
///     }
///
///     ControlFlow::Continue(())
/// });
/// assert_eq!(r, ControlFlow::Break(13));
/// ```
///
/// A basic tree traversal:
/// ```no_run
/// #![feature(control_flow_enum)]
/// use std::ops::ControlFlow;
///
/// pub struct TreeNode<T> {
///     value: T,
///     left: Option<Box<TreeNode<T>>>,
///     right: Option<Box<TreeNode<T>>>,
/// }
///
/// impl<T> TreeNode<T> {
///     pub fn traverse_inorder<B>(&self, mut f: impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
///         if let Some(left) = &self.left {
///             left.traverse_inorder(&mut f)?;
///         }
///         f(&self.value)?;
///         if let Some(right) = &self.right {
///             right.traverse_inorder(&mut f)?;
///         }
///         ControlFlow::Continue(())
///     }
/// }
/// ```
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow<B, C = ()> {
    /// Move on to the next phase of the operation as normal.
    #[cfg_attr(not(bootstrap), lang = "Continue")]
    Continue(C),
    /// Exit the operation without running subsequent phases.
    #[cfg_attr(not(bootstrap), lang = "Break")]
    Break(B),
    // Yes, the order of the variants doesn't match the type parameters.
    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
    // is a no-op conversion in the `Try` implementation.
}

#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
impl<B, C> ops::TryV1 for ControlFlow<B, C> {
    type Output = C;
    type Error = B;
    #[inline]
    fn into_result(self) -> Result<Self::Output, Self::Error> {
        match self {
            ControlFlow::Continue(y) => Ok(y),
            ControlFlow::Break(x) => Err(x),
        }
    }
    #[inline]
    fn from_error(v: Self::Error) -> Self {
        ControlFlow::Break(v)
    }
    #[inline]
    fn from_ok(v: Self::Output) -> Self {
        ControlFlow::Continue(v)
    }
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::TryV2 for ControlFlow<B, C> {
    type Output = C;
    type Residual = ControlFlow<B, convert::Infallible>;

    #[inline]
    fn from_output(output: Self::Output) -> Self {
        ControlFlow::Continue(output)
    }

    #[inline]
    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        match self {
            ControlFlow::Continue(c) => ControlFlow::Continue(c),
            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
        }
    }
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::FromResidual for ControlFlow<B, C> {
    #[inline]
    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
        match residual {
            ControlFlow::Break(b) => ControlFlow::Break(b),
        }
    }
}

impl<B, C> ControlFlow<B, C> {
    /// Returns `true` if this is a `Break` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
    /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_break(&self) -> bool {
        matches!(*self, ControlFlow::Break(_))
    }

    /// Returns `true` if this is a `Continue` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
    /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_continue(&self) -> bool {
        matches!(*self, ControlFlow::Continue(_))
    }

    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
    /// `ControlFlow` was `Break` and `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
    /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn break_value(self) -> Option<B> {
        match self {
            ControlFlow::Continue(..) => None,
            ControlFlow::Break(x) => Some(x),
        }
    }

    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
    /// to the break value in case it exists.
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
    where
        F: FnOnce(B) -> T,
    {
        match self {
            ControlFlow::Continue(x) => ControlFlow::Continue(x),
            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
        }
    }
}

#[cfg(bootstrap)]
impl<R: ops::TryV1> ControlFlow<R, R::Output> {
    /// Create a `ControlFlow` from any type implementing `Try`.
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    #[inline]
    pub fn from_try(r: R) -> Self {
        match R::into_result(r) {
            Ok(v) => ControlFlow::Continue(v),
            Err(v) => ControlFlow::Break(R::from_error(v)),
        }
    }

    /// Convert a `ControlFlow` into any type implementing `Try`;
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    #[inline]
    pub fn into_try(self) -> R {
        match self {
            ControlFlow::Continue(v) => R::from_ok(v),
            ControlFlow::Break(v) => v,
        }
    }
}

#[cfg(not(bootstrap))]
impl<R: ops::TryV2> ControlFlow<R, R::Output> {
    /// Create a `ControlFlow` from any type implementing `Try`.
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    #[inline]
    pub fn from_try(r: R) -> Self {
        match R::branch(r) {
            ControlFlow::Continue(v) => ControlFlow::Continue(v),
            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
        }
    }

    /// Convert a `ControlFlow` into any type implementing `Try`;
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    #[inline]
    pub fn into_try(self) -> R {
        match self {
            ControlFlow::Continue(v) => R::from_output(v),
            ControlFlow::Break(v) => v,
        }
    }
}

impl<B> ControlFlow<B, ()> {
    /// It's frequently the case that there's no value needed with `Continue`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
    ///     partial_sum += x;
    ///     if partial_sum > 100 { ControlFlow::Break(x) }
    ///     else { ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(last_used.break_value(), Some(22));
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const CONTINUE: Self = ControlFlow::Continue(());
}

impl<C> ControlFlow<(), C> {
    /// APIs like `try_for_each` don't need values with `Break`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// (1..10).chain(20..25).try_for_each(|x| {
    ///     if partial_sum > 100 { ControlFlow::BREAK }
    ///     else { partial_sum += x; ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(partial_sum, 108);
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const BREAK: Self = ControlFlow::Break(());
}
<commit_msg>Demote ControlFlow::{from|into}_try to pub(crate)<commit_after>use crate::{convert, ops};

/// Used to tell an operation whether it should exit early or go on as usual.
///
/// This is used when exposing things (like graph traversals or visitors) where
/// you want the user to be able to choose whether to exit early.
/// Having the enum makes it clearer -- no more wondering "wait, what did `false`
/// mean again?" -- and allows including a value.
///
/// # Examples
///
/// Early-exiting from [`Iterator::try_for_each`]:
/// ```
/// #![feature(control_flow_enum)]
/// use std::ops::ControlFlow;
///
/// let r = (2..100).try_for_each(|x| {
///     if 403 % x == 0 {
///         return ControlFlow::Break(x)
///     }
///
///     ControlFlow::Continue(())
/// });
/// assert_eq!(r, ControlFlow::Break(13));
/// ```
///
/// A basic tree traversal:
/// ```no_run
/// #![feature(control_flow_enum)]
/// use std::ops::ControlFlow;
///
/// pub struct TreeNode<T> {
///     value: T,
///     left: Option<Box<TreeNode<T>>>,
///     right: Option<Box<TreeNode<T>>>,
/// }
///
/// impl<T> TreeNode<T> {
///     pub fn traverse_inorder<B>(&self, mut f: impl FnMut(&T) -> ControlFlow<B>) -> ControlFlow<B> {
///         if let Some(left) = &self.left {
///             left.traverse_inorder(&mut f)?;
///         }
///         f(&self.value)?;
///         if let Some(right) = &self.right {
///             right.traverse_inorder(&mut f)?;
///         }
///         ControlFlow::Continue(())
///     }
/// }
/// ```
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow<B, C = ()> {
    /// Move on to the next phase of the operation as normal.
    #[cfg_attr(not(bootstrap), lang = "Continue")]
    Continue(C),
    /// Exit the operation without running subsequent phases.
    #[cfg_attr(not(bootstrap), lang = "Break")]
    Break(B),
    // Yes, the order of the variants doesn't match the type parameters.
    // They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
    // is a no-op conversion in the `Try` implementation.
}

#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
impl<B, C> ops::TryV1 for ControlFlow<B, C> {
    type Output = C;
    type Error = B;
    #[inline]
    fn into_result(self) -> Result<Self::Output, Self::Error> {
        match self {
            ControlFlow::Continue(y) => Ok(y),
            ControlFlow::Break(x) => Err(x),
        }
    }
    #[inline]
    fn from_error(v: Self::Error) -> Self {
        ControlFlow::Break(v)
    }
    #[inline]
    fn from_ok(v: Self::Output) -> Self {
        ControlFlow::Continue(v)
    }
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::TryV2 for ControlFlow<B, C> {
    type Output = C;
    type Residual = ControlFlow<B, convert::Infallible>;

    #[inline]
    fn from_output(output: Self::Output) -> Self {
        ControlFlow::Continue(output)
    }

    #[inline]
    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
        match self {
            ControlFlow::Continue(c) => ControlFlow::Continue(c),
            ControlFlow::Break(b) => ControlFlow::Break(ControlFlow::Break(b)),
        }
    }
}

#[unstable(feature = "try_trait_v2", issue = "84277")]
impl<B, C> ops::FromResidual for ControlFlow<B, C> {
    #[inline]
    fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
        match residual {
            ControlFlow::Break(b) => ControlFlow::Break(b),
        }
    }
}

impl<B, C> ControlFlow<B, C> {
    /// Returns `true` if this is a `Break` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(ControlFlow::<i32, String>::Break(3).is_break());
    /// assert!(!ControlFlow::<String, i32>::Continue(3).is_break());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_break(&self) -> bool {
        matches!(*self, ControlFlow::Break(_))
    }

    /// Returns `true` if this is a `Continue` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert!(!ControlFlow::<i32, String>::Break(3).is_continue());
    /// assert!(ControlFlow::<String, i32>::Continue(3).is_continue());
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn is_continue(&self) -> bool {
        matches!(*self, ControlFlow::Continue(_))
    }

    /// Converts the `ControlFlow` into an `Option` which is `Some` if the
    /// `ControlFlow` was `Break` and `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// assert_eq!(ControlFlow::<i32, String>::Break(3).break_value(), Some(3));
    /// assert_eq!(ControlFlow::<String, i32>::Continue(3).break_value(), None);
    /// ```
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn break_value(self) -> Option<B> {
        match self {
            ControlFlow::Continue(..) => None,
            ControlFlow::Break(x) => Some(x),
        }
    }

    /// Maps `ControlFlow<B, C>` to `ControlFlow<T, C>` by applying a function
    /// to the break value in case it exists.
    #[inline]
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub fn map_break<T, F>(self, f: F) -> ControlFlow<T, C>
    where
        F: FnOnce(B) -> T,
    {
        match self {
            ControlFlow::Continue(x) => ControlFlow::Continue(x),
            ControlFlow::Break(x) => ControlFlow::Break(f(x)),
        }
    }
}

#[cfg(bootstrap)]
impl<R: ops::TryV1> ControlFlow<R, R::Output> {
    /// Create a `ControlFlow` from any type implementing `Try`.
    #[inline]
    pub(crate) fn from_try(r: R) -> Self {
        match R::into_result(r) {
            Ok(v) => ControlFlow::Continue(v),
            Err(v) => ControlFlow::Break(R::from_error(v)),
        }
    }

    /// Convert a `ControlFlow` into any type implementing `Try`;
    #[inline]
    pub(crate) fn into_try(self) -> R {
        match self {
            ControlFlow::Continue(v) => R::from_ok(v),
            ControlFlow::Break(v) => v,
        }
    }
}

/// These are used only as part of implementing the iterator adapters.
/// They have mediocre names and non-obvious semantics, so aren't
/// currently on a path to potential stabilization.
#[cfg(not(bootstrap))]
impl<R: ops::TryV2> ControlFlow<R, R::Output> {
    /// Create a `ControlFlow` from any type implementing `Try`.
    #[inline]
    pub(crate) fn from_try(r: R) -> Self {
        match R::branch(r) {
            ControlFlow::Continue(v) => ControlFlow::Continue(v),
            ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
        }
    }

    /// Convert a `ControlFlow` into any type implementing `Try`;
    #[inline]
    pub(crate) fn into_try(self) -> R {
        match self {
            ControlFlow::Continue(v) => R::from_output(v),
            ControlFlow::Break(v) => v,
        }
    }
}

impl<B> ControlFlow<B, ()> {
    /// It's frequently the case that there's no value needed with `Continue`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
    ///     partial_sum += x;
    ///     if partial_sum > 100 { ControlFlow::Break(x) }
    ///     else { ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(last_used.break_value(), Some(22));
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const CONTINUE: Self = ControlFlow::Continue(());
}

impl<C> ControlFlow<(), C> {
    /// APIs like `try_for_each` don't need values with `Break`,
    /// so this provides a way to avoid typing `(())`, if you prefer it.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(control_flow_enum)]
    /// use std::ops::ControlFlow;
    ///
    /// let mut partial_sum = 0;
    /// (1..10).chain(20..25).try_for_each(|x| {
    ///     if partial_sum > 100 { ControlFlow::BREAK }
    ///     else { partial_sum += x; ControlFlow::CONTINUE }
    /// });
    /// assert_eq!(partial_sum, 108);
    /// ```
    #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
    pub const BREAK: Self = ControlFlow::Break(());
}
<|endoftext|>