<commit_before>use std::io::{Result, Write};

use analysis;
use analysis::namespaces::MAIN;
use case::CaseExt;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use nameutil;
use regex::{Captures, Regex};
use self::format::reformat_doc;
use stripper_lib::Type as SType;
use stripper_lib::{TypeStruct, write_file_name, write_item_doc};
use traits::*;

mod format;

trait ToStripperType {
    fn to_stripper_type(&self) -> TypeStruct;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn to_stripper_type(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }
        }
    }
}

impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Class, Struct);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("docs.md");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(write_file_name(w, None));

    for (tid, type_) in env.library.namespace_types(MAIN) {
        let obj = env.config.objects.get(&tid.full_name(&env.library))
            .and_then(|obj| if obj.status.ignored() { None } else { Some(obj) });
        match *type_ {
            LType::Class(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env, &analysis::object::class(env, obj).unwrap()));
                }
            }
            LType::Interface(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env,
                                           &analysis::object::interface(env, obj).unwrap()));
                }
            }
            LType::Record(..) => {
                if let Some(obj) = obj {
                    try!(create_record_doc(w, env, &analysis::record::new(env, obj).unwrap()));
                }
            }
            _ => {
                try!(handle_type(&mut w, env, type_));
            }
        }
    }

    Ok(())
}

fn handle_type(w: &mut Write, env: &Env, ty: &LType) -> Result<()> {
    match *ty {
        LType::Enumeration(ref e) => create_enum_doc(w, env, &e),
        _ => Ok(()),
    }
}

fn create_object_doc(w: &mut Write, env: &Env, info: &analysis::object::Info) -> Result<()> {
    let symbols = env.symbols.borrow();
    let ty = TypeStruct::new(SType::Struct, &info.name);
    let ty_ext = TypeStruct::new(SType::Trait, &format!("{}Ext", info.name));
    let has_trait = info.has_children;
    let doc;
    let functions: &[Function];

    match *env.library.type_(info.type_id) {
        Type::Class(ref cl) => {
            doc = cl.doc.as_ref();
            functions = &cl.functions;
        }
        Type::Interface(ref iface) => {
            doc = iface.doc.as_ref();
            functions = &iface.functions;
        }
        _ => unreachable!(),
    }

    try!(write_item_doc(w, &ty, |w| {
        if let Some(ver) = info.deprecated_version {
            try!(write!(w, "`[Deprecated since {}]` ", ver));
        }
        if let Some(doc) = doc {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        } else {
            try!(writeln!(w, ""));
        }
        if let Some(version) = info.version {
            try!(writeln!(w, "\nSince: {}", version));
        }

        let impl_self = if has_trait { Some(info.type_id) } else { None };
        let implements = impl_self.iter()
            .chain(env.class_hierarchy.supertypes(info.type_id))
            .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
            .map(|&tid| format!("[`{name}Ext`](trait.{name}Ext.html)",
                                name = env.library.type_(tid).get_name()))
            .collect::<Vec<_>>();
        if !implements.is_empty() {
            try!(writeln!(w, "\n# Implements\n"));
            try!(writeln!(w, "{}", &implements.join(", ")));
        }
        Ok(())
    }));

    if has_trait {
        try!(write_item_doc(w, &ty_ext, |w| {
            if let Some(ver) = info.deprecated_version {
                try!(write!(w, "`[Deprecated since {}]` ", ver));
            }
            try!(writeln!(w, "Trait containing all `{}` methods.", ty.name));

            if let Some(version) = info.version {
                try!(writeln!(w, "\nSince: {}", version));
            }

            let mut implementors = Some(info.type_id).into_iter()
                .chain(env.class_hierarchy.subtypes(info.type_id))
                .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
                .map(|tid| format!("[`{name}`](struct.{name}.html)",
                                    name = env.library.type_(tid).get_name()))
                .collect::<Vec<_>>();
            implementors.sort();

            try!(writeln!(w, "\n# Implementors\n"));
            try!(writeln!(w, "{}", implementors.join(", ")));
            Ok(())
        }));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in functions {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, env, &function, Some(Box::new(ty))));
    }
    Ok(())
}

fn create_record_doc(w: &mut Write, env: &Env, info: &analysis::record::Info) -> Result<()> {
    let record: &Record = env.library.type_(info.type_id).to_ref_as();
    let ty = record.to_stripper_type();
    let symbols = env.symbols.borrow();

    try!(write_item_doc(w, &ty, |w| {
        if let Some(ref doc) = record.doc {
            if let Some(ver) = info.deprecated_version {
                try!(write!(w, "`[Deprecated since {}]` ", ver));
            }
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        if let Some(ver) = info.deprecated_version {
            try!(writeln!(w, "\n# Deprecated since {}\n", ver));
        } else if record.doc_deprecated.is_some() {
            try!(writeln!(w, "\n# Deprecated\n"));
        }
        if let Some(ref doc) = record.doc_deprecated {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        if let Some(version) = info.version {
            try!(writeln!(w, "\nSince: {}", version));
        }
        Ok(())
    }));

    let ty = TypeStruct { ty: SType::Impl, ..ty };
    for function in &record.functions {
        try!(create_fn_doc(w, env, &function, Some(Box::new(ty.clone()))));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, env: &Env, enum_: &Enumeration) -> Result<()> {
    let ty = enum_.to_stripper_type();
    let symbols = env.symbols.borrow();

    try!(write_item_doc(w, &ty, |w| {
        if let Some(ref doc) = enum_.doc {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        if let Some(ver) = enum_.deprecated_version {
            try!(writeln!(w, "\n# Deprecated since {}\n", ver));
        } else if enum_.doc_deprecated.is_some() {
            try!(writeln!(w, "\n# Deprecated\n"));
        }
        if let Some(ref doc) = enum_.doc_deprecated {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        Ok(())
    }));

    for member in enum_.members.iter() {
        let mut sub_ty = TypeStruct { name: member.name.to_camel(), ..member.to_stripper_type()};

        if member.doc.is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(write_item_doc(w, &sub_ty, |w| {
                if let Some(ref doc) = member.doc {
                    try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
                }
                Ok(())
            }));
        }
    }

    if let Some(version) = enum_.version {
        if version > env.config.min_cfg_version {
            try!(writeln!(w, "\nSince: {}\n", version));
        }
    }
    Ok(())
}

lazy_static! {
    static ref PARAM_NAME: Regex = Regex::new(r"@(\w+)\b").unwrap();
}

fn fix_param_names(doc: &str, self_name: &Option<String>) -> String {
    PARAM_NAME.replace_all(doc, |caps: &Captures| {
        if let Some(ref self_name) = *self_name {
            if &caps[1] == self_name {
                return "@self".into()
            }
        }
        format!("@{}", nameutil::mangle_keywords(&caps[1]))
    })
}

fn create_fn_doc(w: &mut Write, env: &Env, fn_: &Function, parent: Option<Box<TypeStruct>>)
        -> Result<()> {
    if fn_.doc.is_none() && fn_.doc_deprecated.is_none() && fn_.ret.doc.is_none() {
        if fn_.parameters.iter().all(|p| {
            p.doc.is_none()
        }) {
            return Ok(());
        }
    }

    let symbols = env.symbols.borrow();
    let ty = TypeStruct { parent: parent, ..fn_.to_stripper_type() };

    let self_name: Option<String> = fn_.parameters.iter()
        .find(|p| p.instance_parameter)
        .map(|p| p.name.clone());

    write_item_doc(w, &ty, |w| {
        if let Some(ref doc) = fn_.doc {
            if let Some(ver) = fn_.deprecated_version {
                try!(write!(w, "`[Deprecated since {}]` ", ver));
            }
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
        }
        for parameter in fn_.parameters.iter() {
            if parameter.instance_parameter || parameter.name.is_empty() {
                continue
            }
            if let Some(ref doc) = parameter.doc {
                try!(writeln!(w, "## `{}`", nameutil::mangle_keywords(&parameter.name[..])));
                try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
            }
        }

        if let Some(ref doc) = fn_.ret.doc {
            try!(writeln!(w, "\n# Returns\n"));
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
        }

        if let Some(version) = fn_.version {
            if version > env.config.min_cfg_version {
                try!(writeln!(w, "\nSince: {}\n", version));
            }
        }
        if let Some(ver) = fn_.deprecated_version {
            try!(writeln!(w, "\n# Deprecated since {}\n", ver));
        } else if fn_.doc_deprecated.is_some() {
            try!(writeln!(w, "\n# Deprecated\n"));
        }
        if let Some(ref doc) = fn_.doc_deprecated {
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
        }
        Ok(())
    })
}
<commit_msg>Move version notices above parameters in function docs<commit_after>use std::io::{Result, Write};

use analysis;
use analysis::namespaces::MAIN;
use case::CaseExt;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use nameutil;
use regex::{Captures, Regex};
use self::format::reformat_doc;
use stripper_lib::Type as SType;
use stripper_lib::{TypeStruct, write_file_name, write_item_doc};
use traits::*;

mod format;

trait ToStripperType {
    fn to_stripper_type(&self) -> TypeStruct;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn to_stripper_type(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }
        }
    }
}

impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Class, Struct);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("docs.md");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(write_file_name(w, None));

    for (tid, type_) in env.library.namespace_types(MAIN) {
        let obj = env.config.objects.get(&tid.full_name(&env.library))
            .and_then(|obj| if obj.status.ignored() { None } else { Some(obj) });
        match *type_ {
            LType::Class(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env, &analysis::object::class(env, obj).unwrap()));
                }
            }
            LType::Interface(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env,
                                           &analysis::object::interface(env, obj).unwrap()));
                }
            }
            LType::Record(..) => {
                if let Some(obj) = obj {
                    try!(create_record_doc(w, env, &analysis::record::new(env, obj).unwrap()));
                }
            }
            _ => {
                try!(handle_type(&mut w, env, type_));
            }
        }
    }

    Ok(())
}

fn handle_type(w: &mut Write, env: &Env, ty: &LType) -> Result<()> {
    match *ty {
        LType::Enumeration(ref e) => create_enum_doc(w, env, &e),
        _ => Ok(()),
    }
}

fn create_object_doc(w: &mut Write, env: &Env, info: &analysis::object::Info) -> Result<()> {
    let symbols = env.symbols.borrow();
    let ty = TypeStruct::new(SType::Struct, &info.name);
    let ty_ext = TypeStruct::new(SType::Trait, &format!("{}Ext", info.name));
    let has_trait = info.has_children;
    let doc;
    let functions: &[Function];

    match *env.library.type_(info.type_id) {
        Type::Class(ref cl) => {
            doc = cl.doc.as_ref();
            functions = &cl.functions;
        }
        Type::Interface(ref iface) => {
            doc = iface.doc.as_ref();
            functions = &iface.functions;
        }
        _ => unreachable!(),
    }

    try!(write_item_doc(w, &ty, |w| {
        if let Some(ver) = info.deprecated_version {
            try!(write!(w, "`[Deprecated since {}]` ", ver));
        }
        if let Some(doc) = doc {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        } else {
            try!(writeln!(w, ""));
        }
        if let Some(version) = info.version {
            try!(writeln!(w, "\nSince: {}", version));
        }

        let impl_self = if has_trait { Some(info.type_id) } else { None };
        let implements = impl_self.iter()
            .chain(env.class_hierarchy.supertypes(info.type_id))
            .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
            .map(|&tid| format!("[`{name}Ext`](trait.{name}Ext.html)",
                                name = env.library.type_(tid).get_name()))
            .collect::<Vec<_>>();
        if !implements.is_empty() {
            try!(writeln!(w, "\n# Implements\n"));
            try!(writeln!(w, "{}", &implements.join(", ")));
        }
        Ok(())
    }));

    if has_trait {
        try!(write_item_doc(w, &ty_ext, |w| {
            if let Some(ver) = info.deprecated_version {
                try!(write!(w, "`[Deprecated since {}]` ", ver));
            }
            try!(writeln!(w, "Trait containing all `{}` methods.", ty.name));

            if let Some(version) = info.version {
                try!(writeln!(w, "\nSince: {}", version));
            }

            let mut implementors = Some(info.type_id).into_iter()
                .chain(env.class_hierarchy.subtypes(info.type_id))
                .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
                .map(|tid| format!("[`{name}`](struct.{name}.html)",
                                    name = env.library.type_(tid).get_name()))
                .collect::<Vec<_>>();
            implementors.sort();

            try!(writeln!(w, "\n# Implementors\n"));
            try!(writeln!(w, "{}", implementors.join(", ")));
            Ok(())
        }));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in functions {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, env, &function, Some(Box::new(ty))));
    }
    Ok(())
}

fn create_record_doc(w: &mut Write, env: &Env, info: &analysis::record::Info) -> Result<()> {
    let record: &Record = env.library.type_(info.type_id).to_ref_as();
    let ty = record.to_stripper_type();
    let symbols = env.symbols.borrow();

    try!(write_item_doc(w, &ty, |w| {
        if let Some(ref doc) = record.doc {
            if let Some(ver) = info.deprecated_version {
                try!(write!(w, "`[Deprecated since {}]` ", ver));
            }
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        if let Some(ver) = info.deprecated_version {
            try!(writeln!(w, "\n# Deprecated since {}\n", ver));
        } else if record.doc_deprecated.is_some() {
            try!(writeln!(w, "\n# Deprecated\n"));
        }
        if let Some(ref doc) = record.doc_deprecated {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        if let Some(version) = info.version {
            try!(writeln!(w, "\nSince: {}", version));
        }
        Ok(())
    }));

    let ty = TypeStruct { ty: SType::Impl, ..ty };
    for function in &record.functions {
        try!(create_fn_doc(w, env, &function, Some(Box::new(ty.clone()))));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, env: &Env, enum_: &Enumeration) -> Result<()> {
    let ty = enum_.to_stripper_type();
    let symbols = env.symbols.borrow();

    try!(write_item_doc(w, &ty, |w| {
        if let Some(ref doc) = enum_.doc {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        if let Some(ver) = enum_.deprecated_version {
            try!(writeln!(w, "\n# Deprecated since {}\n", ver));
        } else if enum_.doc_deprecated.is_some() {
            try!(writeln!(w, "\n# Deprecated\n"));
        }
        if let Some(ref doc) = enum_.doc_deprecated {
            try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
        }
        Ok(())
    }));

    for member in enum_.members.iter() {
        let mut sub_ty = TypeStruct { name: member.name.to_camel(), ..member.to_stripper_type()};

        if member.doc.is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(write_item_doc(w, &sub_ty, |w| {
                if let Some(ref doc) = member.doc {
                    try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
                }
                Ok(())
            }));
        }
    }

    if let Some(version) = enum_.version {
        if version > env.config.min_cfg_version {
            try!(writeln!(w, "\nSince: {}\n", version));
        }
    }
    Ok(())
}

lazy_static! {
    static ref PARAM_NAME: Regex = Regex::new(r"@(\w+)\b").unwrap();
}

fn fix_param_names(doc: &str, self_name: &Option<String>) -> String {
    PARAM_NAME.replace_all(doc, |caps: &Captures| {
        if let Some(ref self_name) = *self_name {
            if &caps[1] == self_name {
                return "@self".into()
            }
        }
        format!("@{}", nameutil::mangle_keywords(&caps[1]))
    })
}

fn create_fn_doc(w: &mut Write, env: &Env, fn_: &Function, parent: Option<Box<TypeStruct>>)
        -> Result<()> {
    if fn_.doc.is_none() && fn_.doc_deprecated.is_none() && fn_.ret.doc.is_none() {
        if fn_.parameters.iter().all(|p| {
            p.doc.is_none()
        }) {
            return Ok(());
        }
    }

    let symbols = env.symbols.borrow();
    let ty = TypeStruct { parent: parent, ..fn_.to_stripper_type() };

    let self_name: Option<String> = fn_.parameters.iter()
        .find(|p| p.instance_parameter)
        .map(|p| p.name.clone());

    write_item_doc(w, &ty, |w| {
        if let Some(ref doc) = fn_.doc {
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
        }
        if let Some(version) = fn_.version {
            if version > env.config.min_cfg_version {
                try!(writeln!(w, "\nSince: {}\n", version));
            }
        }
        if let Some(ver) = fn_.deprecated_version {
            try!(writeln!(w, "\n# Deprecated since {}\n", ver));
        } else if fn_.doc_deprecated.is_some() {
            try!(writeln!(w, "\n# Deprecated\n"));
        }
        if let Some(ref doc) = fn_.doc_deprecated {
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
        }

        for parameter in fn_.parameters.iter() {
            if parameter.instance_parameter || parameter.name.is_empty() {
                continue
            }
            if let Some(ref doc) = parameter.doc {
                try!(writeln!(w, "## `{}`", nameutil::mangle_keywords(&parameter.name[..])));
                try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
            }
        }

        if let Some(ref doc) = fn_.ret.doc {
            try!(writeln!(w, "\n# Returns\n"));
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), &symbols)));
        }
        Ok(())
    })
}
<|endoftext|>


<commit_before>use iron::prelude::*;
use iron::middleware::Handler;
use iron::status::Status;
use std::io::Read;

use super::super::SharedStore;
use super::super::metrics::Metric;
use super::super::parsers::log_line::LogLineReader;

/// Accepts HTTP requests and reads lines from the body. Each line will be
/// passed to its set of `readers` and any metrics collected by those readers
/// will be recorded in the `store`.
pub struct LogDrainHandler {
    store: SharedStore,
    readers: Vec<Box<LogLineReader>>,
}

impl LogDrainHandler {
    /// Create a new handler with the given vector of readers. Each reader
    /// will be called on every line the handler receives.
    pub fn new(store: SharedStore, readers: Vec<Box<LogLineReader>>) -> LogDrainHandler {
        LogDrainHandler {
            store: store,
            readers: readers,
        }
    }
}

impl Handler for LogDrainHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut body = String::new();
        match req.body.read_to_string(&mut body) {
            Ok(_) => {},
            Err(error) => {
                println!("{:?}", error);
                return Err(IronError::new(error, Status::InternalServerError))
            },
        }

        let ref readers = self.readers;
        let mut metrics: Vec<Metric> = vec![];

        for line in body.lines() {
            for reader in readers {
                metrics.extend(reader.read(line))
            }
        }

        self.store.record(metrics);

        Ok(Response::with((Status::Created)))
    }
}
<commit_msg>Explicitly mark trait objects with `dyn`<commit_after>use iron::prelude::*;
use iron::middleware::Handler;
use iron::status::Status;
use std::io::Read;

use super::super::SharedStore;
use super::super::metrics::Metric;
use super::super::parsers::log_line::LogLineReader;

/// Accepts HTTP requests and reads lines from the body. Each line will be
/// passed to its set of `readers` and any metrics collected by those readers
/// will be recorded in the `store`.
pub struct LogDrainHandler {
    store: SharedStore,
    readers: Vec<Box<dyn LogLineReader>>,
}

impl LogDrainHandler {
    /// Create a new handler with the given vector of readers. Each reader
    /// will be called on every line the handler receives.
    pub fn new(store: SharedStore, readers: Vec<Box<dyn LogLineReader>>) -> LogDrainHandler {
        LogDrainHandler {
            store,
            readers,
        }
    }
}

impl Handler for LogDrainHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut body = String::new();
        match req.body.read_to_string(&mut body) {
            Ok(_) => {},
            Err(error) => {
                println!("{:?}", error);
                return Err(IronError::new(error, Status::InternalServerError))
            },
        }

        let ref readers = self.readers;
        let mut metrics: Vec<Metric> = vec![];

        for line in body.lines() {
            for reader in readers {
                metrics.extend(reader.read(line))
            }
        }

        self.store.record(metrics);

        Ok(Response::with(Status::Created))
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rooster Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::super::getopts;
use super::super::password;
use std::io::Write;

pub fn callback_help() {
    println!("Usage:");
    println!("    rooster delete -h");
    println!("    rooster delete <app_name>");
    println!("");
    println!("Example:");
    println!("    rooster delete youtube");
}

pub fn callback_exec(matches: &getopts::Matches, store: &mut password::v2::PasswordStore) -> Result<(), i32> {
    if matches.free.len() < 2 {
        println_err!("Woops, seems like the app name is missing here. For help, try:");
        println_err!("    rooster delete -h");
        return Err(1);
    }

    let app_name = &matches.free[1];

    match store.delete_password(app_name) {
        Ok(_) => {
            println_ok!("Done! I've deleted the password for {}.", app_name);
            Ok(())
        },
        Err(err) => {
            println_err!("Woops, I couldn't find a password for this app ({:?}). Make sure you didn't make a typo.", err);
            println_err!("You can use 'rooster list' to see a list of available passwords.");
            Err(1)
        }
    }
}
<commit_msg>command/delete: allow deleting multiple passwords at once<commit_after>// Copyright 2014 The Rooster Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::super::getopts;
use super::super::password;
use std::io::Write;

pub fn callback_help() {
    println!("Usage:");
    println!("    rooster delete -h");
    println!("    rooster delete <app_name> ...");
    println!("");
    println!("Example:");
    println!("    rooster delete youtube");
}

pub fn callback_exec(matches: &getopts::Matches, store: &mut password::v2::PasswordStore) -> Result<(), i32> {
    if matches.free.len() < 2 {
        println_err!("Woops, seems like the app name is missing here. For help, try:");
        println_err!("    rooster delete -h");
        return Err(1);
    }

    let mut has_error = false;

    for app_name in &matches.free[1..] {
        match store.delete_password(app_name) {
            Ok(_) => {
                println_ok!("Done! I've deleted the password for {}.", app_name);
            },
            Err(err) => {
                println_err!("Woops! I couldn't find a password for the app `{}` (error: {:?}).", app_name, err);
                has_error = true;
            }
        }
    }

    if has_error {
        Err(1)
    } else {
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::ops;
use std::time;

const BILLION: i64 = 1_000_000_000;

#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub struct Time {
    pub sec: u32,
    pub nsec: u32,
}

impl Time {
    #[inline]
    pub fn new() -> Time {
        Self::default()
    }

    #[inline]
    pub fn from_nanos(t: i64) -> Time {
        Time {
            sec: (t / BILLION) as u32,
            nsec: (t % BILLION) as u32,
        }
    }

    #[inline]
    pub fn nanos(&self) -> i64 {
        i64::from(self.sec) * BILLION + i64::from(self.nsec)
    }
}

impl cmp::PartialEq for Time {
    fn eq(&self, other: &Self) -> bool {
        self.nanos() == other.nanos()
    }
}

impl cmp::PartialOrd for Time {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.nanos().partial_cmp(&other.nanos())
    }
}

impl cmp::Eq for Time {}

impl cmp::Ord for Time {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.nanos().cmp(&other.nanos())
    }
}

#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub struct Duration {
    pub sec: i32,
    pub nsec: i32,
}

impl Duration {
    #[inline]
    pub fn new() -> Duration {
        Self::default()
    }

    #[inline]
    pub fn from_nanos(t: i64) -> Duration {
        Duration {
            sec: (t / BILLION) as i32,
            nsec: (t % BILLION) as i32,
        }
    }

    #[inline]
    fn nanos(&self) -> i64 {
        i64::from(self.sec) * BILLION + i64::from(self.nsec)
    }
}

impl cmp::PartialEq for Duration {
    fn eq(&self, other: &Self) -> bool {
        self.nanos() == other.nanos()
    }
}

impl cmp::PartialOrd for Duration {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.nanos().partial_cmp(&other.nanos())
    }
}

impl cmp::Eq for Duration {}

impl cmp::Ord for Duration {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.nanos().cmp(&other.nanos())
    }
}

impl ops::Add<Duration> for Time {
    type Output = Time;
    fn add(self, rhs: Duration) -> Self::Output {
        Time::from_nanos(self.nanos() + rhs.nanos())
    }
}

impl ops::Add<Duration> for Duration {
    type Output = Duration;
    fn add(self, rhs: Duration) -> Self::Output {
        Duration::from_nanos(self.nanos() + rhs.nanos())
    }
}

impl ops::Sub<Time> for Time {
    type Output = Duration;
    fn sub(self, rhs: Time) -> Self::Output {
        Duration::from_nanos(self.nanos() - rhs.nanos())
    }
}

impl ops::Sub<Duration> for Time {
    type Output = Time;
    fn sub(self, rhs: Duration) -> Self::Output {
        Time::from_nanos(self.nanos() - rhs.nanos())
    }
}

impl ops::Sub<Duration> for Duration {
    type Output = Duration;
    fn sub(self, rhs: Duration) -> Self::Output {
        Duration::from_nanos(self.nanos() - rhs.nanos())
    }
}

impl ops::Neg for Duration {
    type Output = Duration;
    fn neg(self) -> Self::Output {
        Duration {
            sec: -self.sec,
            nsec: -self.nsec,
        }
    }
}

impl From<time::Duration> for Duration {
    fn from(std_duration: time::Duration) -> Self {
        Duration {
            sec: std_duration.as_secs() as i32,
            nsec: std_duration.subsec_nanos() as i32,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Duration, Time};
    use std::time;

    #[test]
    fn from_nanos_works() {
        let time = Time::from_nanos(123456789987654321);
        assert_eq!(time.sec, 123456789);
        assert_eq!(time.nsec, 987654321);
        let time = Duration::from_nanos(123456789987654321);
        assert_eq!(time.sec, 123456789);
        assert_eq!(time.nsec, 987654321);
    }

    #[test]
    fn nanos_works() {
        let time = Time {
            sec: 123456789,
            nsec: 987654321,
        };
        assert_eq!(time.nanos(), 123456789987654321);
        let time = Duration {
            sec: 123456789,
            nsec: 987654321,
        };
        assert_eq!(time.nanos(), 123456789987654321);
    }

    #[test]
    fn works_with_negative() {
        let time = Time::from_nanos(-123456789987654321);
        assert_eq!(time.sec, -123456789);
        assert_eq!(time.nsec, -987654321);
        assert_eq!(time.nanos(), -123456789987654321);
        let time = Duration::from_nanos(-123456789987654321);
        assert_eq!(time.sec, -123456789);
        assert_eq!(time.nsec, -987654321);
        assert_eq!(time.nanos(), -123456789987654321);
    }

    #[test]
    fn convert_works() {
        let std_duration = time::Duration::new(123, 456);
        let msg_duration = Duration::from(std_duration);
        assert_eq!(msg_duration.sec, 123);
        assert_eq!(msg_duration.nsec, 456);

        let std_duration2 = time::Duration::new(9876, 54321);
        let msg_duration2: Duration = std_duration2.into();
        assert_eq!(msg_duration2.sec, 9876);
        assert_eq!(msg_duration2.nsec, 54321);
    }
}
<commit_msg>Fix tests<commit_after>use std::cmp;
use std::ops;
use std::time;

const BILLION: i64 = 1_000_000_000;

#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub struct Time {
    pub sec: u32,
    pub nsec: u32,
}

impl Time {
    #[inline]
    pub fn new() -> Time {
        Self::default()
    }

    #[inline]
    pub fn from_nanos(t: i64) -> Time {
        Time {
            sec: (t / BILLION) as u32,
            nsec: (t % BILLION) as u32,
        }
    }

    #[inline]
    pub fn nanos(&self) -> i64 {
        i64::from(self.sec) * BILLION + i64::from(self.nsec)
    }
}

impl cmp::PartialEq for Time {
    fn eq(&self, other: &Self) -> bool {
        self.nanos() == other.nanos()
    }
}

impl cmp::PartialOrd for Time {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.nanos().partial_cmp(&other.nanos())
    }
}

impl cmp::Eq for Time {}

impl cmp::Ord for Time {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.nanos().cmp(&other.nanos())
    }
}

#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub struct Duration {
    pub sec: i32,
    pub nsec: i32,
}

impl Duration {
    #[inline]
    pub fn new() -> Duration {
        Self::default()
    }

    #[inline]
    pub fn from_nanos(t: i64) -> Duration {
        Duration {
            sec: (t / BILLION) as i32,
            nsec: (t % BILLION) as i32,
        }
    }

    #[inline]
    fn nanos(&self) -> i64 {
        i64::from(self.sec) * BILLION + i64::from(self.nsec)
    }
}

impl cmp::PartialEq for Duration {
    fn eq(&self, other: &Self) -> bool {
        self.nanos() == other.nanos()
    }
}

impl cmp::PartialOrd for Duration {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.nanos().partial_cmp(&other.nanos())
    }
}

impl cmp::Eq for Duration {}

impl cmp::Ord for Duration {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.nanos().cmp(&other.nanos())
    }
}

impl ops::Add<Duration> for Time {
    type Output = Time;
    fn add(self, rhs: Duration) -> Self::Output {
        Time::from_nanos(self.nanos() + rhs.nanos())
    }
}

impl ops::Add<Duration> for Duration {
    type Output = Duration;
    fn add(self, rhs: Duration) -> Self::Output {
        Duration::from_nanos(self.nanos() + rhs.nanos())
    }
}

impl ops::Sub<Time> for Time {
    type Output = Duration;
    fn sub(self, rhs: Time) -> Self::Output {
        Duration::from_nanos(self.nanos() - rhs.nanos())
    }
}

impl ops::Sub<Duration> for Time {
    type Output = Time;
    fn sub(self, rhs: Duration) -> Self::Output {
        Time::from_nanos(self.nanos() - rhs.nanos())
    }
}

impl ops::Sub<Duration> for Duration {
    type Output = Duration;
    fn sub(self, rhs: Duration) -> Self::Output {
        Duration::from_nanos(self.nanos() - rhs.nanos())
    }
}

impl ops::Neg for Duration {
    type Output = Duration;
    fn neg(self) -> Self::Output {
        Duration {
            sec: -self.sec,
            nsec: -self.nsec,
        }
    }
}

impl From<time::Duration> for Duration {
    fn from(std_duration: time::Duration) -> Self {
        Duration {
            sec: std_duration.as_secs() as i32,
            nsec: std_duration.subsec_nanos() as i32,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Duration, Time};
    use std::time;

    #[test]
    fn from_nanos_works() {
        let time = Time::from_nanos(123456789987654321);
        assert_eq!(time.sec, 123456789);
        assert_eq!(time.nsec, 987654321);
        let time = Duration::from_nanos(123456789987654321);
        assert_eq!(time.sec, 123456789);
        assert_eq!(time.nsec, 987654321);
    }

    #[test]
    fn nanos_works() {
        let time = Time {
            sec: 123456789,
            nsec: 987654321,
        };
        assert_eq!(time.nanos(), 123456789987654321);
        let time = Duration {
            sec: 123456789,
            nsec: 987654321,
        };
        assert_eq!(time.nanos(), 123456789987654321);
    }

    #[test]
    fn duration_works_with_negative() {
        let time = Duration::from_nanos(-123456789987654321);
        assert_eq!(time.sec, -123456789);
        assert_eq!(time.nsec, -987654321);
        assert_eq!(time.nanos(), -123456789987654321);
    }

    #[test]
    fn convert_works() {
        let std_duration = time::Duration::new(123, 456);
        let msg_duration = Duration::from(std_duration);
        assert_eq!(msg_duration.sec, 123);
        assert_eq!(msg_duration.nsec, 456);

        let std_duration2 = time::Duration::new(9876, 54321);
        let msg_duration2: Duration = std_duration2.into();
        assert_eq!(msg_duration2.sec, 9876);
        assert_eq!(msg_duration2.nsec, 54321);
    }
}
<|endoftext|>


<commit_before>use crate::error::IoError;

/// Provides IO error as an associated type.
///
/// Must be implemented for all types that also implement at least one of the following traits: `Read`, `Write`,
/// `Seek`.
pub trait IoBase {
    /// Type of errors returned by input/output operations.
    type Error: IoError;
}

/// The `Read` trait allows for reading bytes from a source.
///
/// It is based on the `std::io::Read` trait.
pub trait Read: IoBase {
    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
    ///
    /// This function does not provide any guarantees about whether it blocks waiting for data, but if an object needs
    /// to block for a read and cannot, it will typically signal this via an Err return value.
    ///
    /// If the return value of this method is `Ok(n)`, then it must be guaranteed that `0 <= n <= buf.len()`. A nonzero
    /// `n` value indicates that the buffer buf has been filled in with n bytes of data from this source. If `n` is
    /// `0`, then it can indicate one of two scenarios:
    ///
    /// 1. This reader has reached its "end of file" and will likely no longer be able to produce bytes. Note that this
    ///    does not mean that the reader will always no longer be able to produce bytes.
    /// 2. The buffer specified was 0 bytes in length.
    ///
    /// It is not an error if the returned value `n` is smaller than the buffer size, even when the reader is not at
    /// the end of the stream yet. This may happen for example because fewer bytes are actually available right now
    /// (e. g. being close to end-of-file) or because read() was interrupted by a signal.
    ///
    /// # Errors
    ///
    /// If this function encounters any form of I/O or other error, an error will be returned. If an error is returned
    /// then it must be guaranteed that no bytes were read.
    /// An error for which `IoError::is_interrupted` returns true is non-fatal and the read operation should be retried
    /// if there is nothing else to do.
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;

    /// Read the exact number of bytes required to fill `buf`.
    ///
    /// This function reads as many bytes as necessary to completely fill the specified buffer `buf`.
    ///
    /// # Errors
    ///
    /// If this function encounters an error for which `IoError::is_interrupted` returns true then the error is ignored
    /// and the operation will continue.
    ///
    /// If this function encounters an end of file before completely filling the buffer, it returns an error
    /// instantiated by a call to `IoError::new_unexpected_eof_error`. The contents of `buf` are unspecified in this
    /// case.
    ///
    /// If this function returns an error, it is unspecified how many bytes it has read, but it will never read more
    /// than would be necessary to completely fill the buffer.
    fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
        while !buf.is_empty() {
            match self.read(buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                }
                Err(ref e) if e.is_interrupted() => {}
                Err(e) => return Err(e),
            }
        }
        if buf.is_empty() {
            Ok(())
        } else {
            debug!("failed to fill whole buffer in read_exact");
            Err(Self::Error::new_unexpected_eof_error())
        }
    }
}

/// The `Write` trait allows for writing bytes into the sink.
///
/// It is based on the `std::io::Write` trait.
pub trait Write: IoBase {
    /// Write a buffer into this writer, returning how many bytes were written.
    ///
    /// # Errors
    ///
    /// Each call to write may generate an I/O error indicating that the operation could not be completed. If an error
    /// is returned then no bytes in the buffer were written to this writer.
    /// It is not considered an error if the entire buffer could not be written to this writer.
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;

    /// Attempts to write an entire buffer into this writer.
    ///
    /// This method will continuously call `write` until there is no more data to be written or an error is returned.
    /// Errors for which `IoError::is_interrupted` method returns true are being skipped. This method will not return
    /// until the entire buffer has been successfully written or such an error occurs.
    /// If `write` returns 0 before the entire buffer has been written this method will return an error instantiated by
    /// a call to `IoError::new_write_zero_error`.
    ///
    /// # Errors
    ///
    /// This function will return the first error for which `IoError::is_interrupted` method returns false that `write`
    /// returns.
    fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::Error> {
        while !buf.is_empty() {
            match self.write(buf) {
                Ok(0) => {
                    debug!("failed to write whole buffer in write_all");
                    return Err(Self::Error::new_write_zero_error());
                }
                Ok(n) => buf = &buf[n..],
                Err(ref e) if e.is_interrupted() => {}
                Err(e) => return Err(e),
            }
        }
        Ok(())
    }

    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
    ///
    /// # Errors
    ///
    /// It is considered an error if not all bytes could be written due to I/O errors or EOF being reached.
    fn flush(&mut self) -> Result<(), Self::Error>;
}

/// Enumeration of possible methods to seek within an I/O object.
///
/// It is based on the `std::io::SeekFrom` enum.
pub enum SeekFrom {
    /// Sets the offset to the provided number of bytes.
    Start(u64),
    /// Sets the offset to the size of this object plus the specified number of bytes.
    End(i64),
    /// Sets the offset to the current position plus the specified number of bytes.
    Current(i64),
}

/// The `Seek` trait provides a cursor which can be moved within a stream of bytes.
///
/// It is based on the `std::io::Seek` trait.
pub trait Seek: IoBase {
    /// Seek to an offset, in bytes, in a stream.
    ///
    /// A seek beyond the end of a stream or to a negative position is not allowed.
    ///
    /// If the seek operation completed successfully, this method returns the new position from the start of the
    /// stream. That position can be used later with `SeekFrom::Start`.
    ///
    /// # Errors
    /// Seeking to a negative offset is considered an error.
    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error>;
}

#[cfg(feature = "std")]
impl From<SeekFrom> for std::io::SeekFrom {
    fn from(from: SeekFrom) -> Self {
        match from {
            SeekFrom::Start(n) => std::io::SeekFrom::Start(n),
            SeekFrom::End(n) => std::io::SeekFrom::End(n),
            SeekFrom::Current(n) => std::io::SeekFrom::Current(n),
        }
    }
}

#[cfg(feature = "std")]
impl From<std::io::SeekFrom> for SeekFrom {
    fn from(from: std::io::SeekFrom) -> Self {
        match from {
            std::io::SeekFrom::Start(n) => SeekFrom::Start(n),
            std::io::SeekFrom::End(n) => SeekFrom::End(n),
            std::io::SeekFrom::Current(n) => SeekFrom::Current(n),
        }
    }
}

/// A wrapper struct for types that have implementations for `std::io` traits.
///
/// `Read`, `Write`, `Seek` traits from this crate are implemented for this type if
/// corresponding types from `std::io` are implemented by the inner instance.
#[cfg(feature = "std")]
pub struct StdIoWrapper<T> {
    inner: T,
}

#[cfg(feature = "std")]
impl<T> StdIoWrapper<T> {
    /// Creates a new `StdIoWrapper` instance that wraps the provided `inner` instance.
    pub fn new(inner: T) -> Self {
        Self { inner }
    }
}

#[cfg(feature = "std")]
impl<T> IoBase for StdIoWrapper<T> {
    type Error = std::io::Error;
}

#[cfg(feature = "std")]
impl<T: std::io::Read> Read for StdIoWrapper<T> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        self.inner.read(buf)
    }
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
        self.inner.read_exact(buf)
    }
}

#[cfg(feature = "std")]
impl<T: std::io::Write> Write for StdIoWrapper<T> {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
        self.inner.write(buf)
    }

    fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        self.inner.write_all(buf)
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        self.inner.flush()
    }
}

#[cfg(feature = "std")]
impl<T: std::io::Seek> Seek for StdIoWrapper<T> {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
        self.inner.seek(pos.into())
    }
}

#[cfg(feature = "std")]
impl<T> From<T> for StdIoWrapper<T> {
    fn from(from: T) -> Self {
        Self::new(from)
    }
}

pub(crate) trait ReadLeExt {
    type Error;
    fn read_u8(&mut self) -> Result<u8, Self::Error>;
    fn read_u16_le(&mut self) -> Result<u16, Self::Error>;
    fn read_u32_le(&mut self) -> Result<u32, Self::Error>;
}

impl<T: Read> ReadLeExt for T {
    type Error = <Self as IoBase>::Error;

    fn read_u8(&mut self) -> Result<u8, Self::Error> {
        let mut buf = [0_u8; 1];
        self.read_exact(&mut buf)?;
        Ok(buf[0])
    }

    fn read_u16_le(&mut self) -> Result<u16, Self::Error> {
        let mut buf = [0_u8; 2];
        self.read_exact(&mut buf)?;
        Ok(u16::from_le_bytes(buf))
    }

    fn read_u32_le(&mut self) -> Result<u32, Self::Error> {
        let mut buf = [0_u8; 4];
        self.read_exact(&mut buf)?;
        Ok(u32::from_le_bytes(buf))
    }
}

pub(crate) trait WriteLeExt {
    type Error;
    fn write_u8(&mut self, n: u8) -> Result<(), Self::Error>;
    fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error>;
    fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error>;
}

impl<T: Write> WriteLeExt for T {
    type Error = <Self as IoBase>::Error;

    fn write_u8(&mut self, n: u8) -> Result<(), Self::Error> {
        self.write_all(&[n])
    }

    fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error> {
        self.write_all(&n.to_le_bytes())
    }

    fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error> {
        self.write_all(&n.to_le_bytes())
    }
}
<commit_msg>Add into_inner function in StdIoWrapper<commit_after>use crate::error::IoError;

/// Provides IO error as an associated type.
///
/// Must be implemented for all types that also implement at least one of the following traits: `Read`, `Write`,
/// `Seek`.
pub trait IoBase {
    /// Type of errors returned by input/output operations.
    type Error: IoError;
}

/// The `Read` trait allows for reading bytes from a source.
///
/// It is based on the `std::io::Read` trait.
pub trait Read: IoBase {
    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
    ///
    /// This function does not provide any guarantees about whether it blocks waiting for data, but if an object needs
    /// to block for a read and cannot, it will typically signal this via an Err return value.
    ///
    /// If the return value of this method is `Ok(n)`, then it must be guaranteed that `0 <= n <= buf.len()`. A nonzero
    /// `n` value indicates that the buffer buf has been filled in with n bytes of data from this source. If `n` is
    /// `0`, then it can indicate one of two scenarios:
    ///
    /// 1. This reader has reached its "end of file" and will likely no longer be able to produce bytes. Note that this
    ///    does not mean that the reader will always no longer be able to produce bytes.
    /// 2. The buffer specified was 0 bytes in length.
    ///
    /// It is not an error if the returned value `n` is smaller than the buffer size, even when the reader is not at
    /// the end of the stream yet. This may happen for example because fewer bytes are actually available right now
    /// (e. g. being close to end-of-file) or because read() was interrupted by a signal.
    ///
    /// # Errors
    ///
    /// If this function encounters any form of I/O or other error, an error will be returned. If an error is returned
    /// then it must be guaranteed that no bytes were read.
    /// An error for which `IoError::is_interrupted` returns true is non-fatal and the read operation should be retried
    /// if there is nothing else to do.
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;

    /// Read the exact number of bytes required to fill `buf`.
    ///
    /// This function reads as many bytes as necessary to completely fill the specified buffer `buf`.
    ///
    /// # Errors
    ///
    /// If this function encounters an error for which `IoError::is_interrupted` returns true then the error is ignored
    /// and the operation will continue.
    ///
    /// If this function encounters an end of file before completely filling the buffer, it returns an error
    /// instantiated by a call to `IoError::new_unexpected_eof_error`. The contents of `buf` are unspecified in this
    /// case.
    ///
    /// If this function returns an error, it is unspecified how many bytes it has read, but it will never read more
    /// than would be necessary to completely fill the buffer.
    fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
        while !buf.is_empty() {
            match self.read(buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                }
                Err(ref e) if e.is_interrupted() => {}
                Err(e) => return Err(e),
            }
        }
        if buf.is_empty() {
            Ok(())
        } else {
            debug!("failed to fill whole buffer in read_exact");
            Err(Self::Error::new_unexpected_eof_error())
        }
    }
}

/// The `Write` trait allows for writing bytes into the sink.
///
/// It is based on the `std::io::Write` trait.
pub trait Write: IoBase {
    /// Write a buffer into this writer, returning how many bytes were written.
    ///
    /// # Errors
    ///
    /// Each call to write may generate an I/O error indicating that the operation could not be completed. If an error
    /// is returned then no bytes in the buffer were written to this writer.
    /// It is not considered an error if the entire buffer could not be written to this writer.
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;

    /// Attempts to write an entire buffer into this writer.
    ///
    /// This method will continuously call `write` until there is no more data to be written or an error is returned.
    /// Errors for which `IoError::is_interrupted` method returns true are being skipped. This method will not return
    /// until the entire buffer has been successfully written or such an error occurs.
    /// If `write` returns 0 before the entire buffer has been written this method will return an error instantiated by
    /// a call to `IoError::new_write_zero_error`.
    ///
    /// # Errors
    ///
    /// This function will return the first error for which `IoError::is_interrupted` method returns false that `write`
    /// returns.
    fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::Error> {
        while !buf.is_empty() {
            match self.write(buf) {
                Ok(0) => {
                    debug!("failed to write whole buffer in write_all");
                    return Err(Self::Error::new_write_zero_error());
                }
                Ok(n) => buf = &buf[n..],
                Err(ref e) if e.is_interrupted() => {}
                Err(e) => return Err(e),
            }
        }
        Ok(())
    }

    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
    ///
    /// # Errors
    ///
    /// It is considered an error if not all bytes could be written due to I/O errors or EOF being reached.
    fn flush(&mut self) -> Result<(), Self::Error>;
}

/// Enumeration of possible methods to seek within an I/O object.
///
/// It is based on the `std::io::SeekFrom` enum.
pub enum SeekFrom {
    /// Sets the offset to the provided number of bytes.
    Start(u64),
    /// Sets the offset to the size of this object plus the specified number of bytes.
    End(i64),
    /// Sets the offset to the current position plus the specified number of bytes.
    Current(i64),
}

/// The `Seek` trait provides a cursor which can be moved within a stream of bytes.
///
/// It is based on the `std::io::Seek` trait.
pub trait Seek: IoBase {
    /// Seek to an offset, in bytes, in a stream.
    ///
    /// A seek beyond the end of a stream or to a negative position is not allowed.
    ///
    /// If the seek operation completed successfully, this method returns the new position from the start of the
    /// stream. That position can be used later with `SeekFrom::Start`.
    ///
    /// # Errors
    /// Seeking to a negative offset is considered an error.
    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error>;
}

#[cfg(feature = "std")]
impl From<SeekFrom> for std::io::SeekFrom {
    fn from(from: SeekFrom) -> Self {
        match from {
            SeekFrom::Start(n) => std::io::SeekFrom::Start(n),
            SeekFrom::End(n) => std::io::SeekFrom::End(n),
            SeekFrom::Current(n) => std::io::SeekFrom::Current(n),
        }
    }
}

#[cfg(feature = "std")]
impl From<std::io::SeekFrom> for SeekFrom {
    fn from(from: std::io::SeekFrom) -> Self {
        match from {
            std::io::SeekFrom::Start(n) => SeekFrom::Start(n),
            std::io::SeekFrom::End(n) => SeekFrom::End(n),
            std::io::SeekFrom::Current(n) => SeekFrom::Current(n),
        }
    }
}

/// A wrapper struct for types that have implementations for `std::io` traits.
///
/// `Read`, `Write`, `Seek` traits from this crate are implemented for this type if
/// corresponding types from `std::io` are implemented by the inner instance.
#[cfg(feature = "std")]
pub struct StdIoWrapper<T> {
    inner: T,
}

#[cfg(feature = "std")]
impl<T> StdIoWrapper<T> {
    /// Creates a new `StdIoWrapper` instance that wraps the provided `inner` instance.
    pub fn new(inner: T) -> Self {
        Self { inner }
    }

    /// Returns inner struct
    pub fn into_inner(self) -> T {
        self.inner
    }
}

#[cfg(feature = "std")]
impl<T> IoBase for StdIoWrapper<T> {
    type Error = std::io::Error;
}

#[cfg(feature = "std")]
impl<T: std::io::Read> Read for StdIoWrapper<T> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        self.inner.read(buf)
    }
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
        self.inner.read_exact(buf)
    }
}

#[cfg(feature = "std")]
impl<T: std::io::Write> Write for StdIoWrapper<T> {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
        self.inner.write(buf)
    }

    fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        self.inner.write_all(buf)
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        self.inner.flush()
    }
}

#[cfg(feature = "std")]
impl<T: std::io::Seek> Seek for StdIoWrapper<T> {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
        self.inner.seek(pos.into())
    }
}

#[cfg(feature = "std")]
impl<T> From<T> for StdIoWrapper<T> {
    fn from(from: T) -> Self {
        Self::new(from)
    }
}

pub(crate) trait ReadLeExt {
    type Error;
    fn read_u8(&mut self) -> Result<u8, Self::Error>;
    fn read_u16_le(&mut self) -> Result<u16, Self::Error>;
    fn read_u32_le(&mut self) -> Result<u32, Self::Error>;
}

impl<T: Read> ReadLeExt for T {
    type Error = <Self as IoBase>::Error;

    fn read_u8(&mut self) -> Result<u8, Self::Error> {
        let mut buf = [0_u8; 1];
        self.read_exact(&mut buf)?;
        Ok(buf[0])
    }

    fn read_u16_le(&mut self) -> Result<u16, Self::Error> {
        let mut buf = [0_u8; 2];
        self.read_exact(&mut buf)?;
        Ok(u16::from_le_bytes(buf))
    }

    fn read_u32_le(&mut self) -> Result<u32, Self::Error> {
        let mut buf = [0_u8; 4];
        self.read_exact(&mut buf)?;
        Ok(u32::from_le_bytes(buf))
    }
}

pub(crate) trait WriteLeExt {
    type Error;
    fn write_u8(&mut self, n: u8) -> Result<(), Self::Error>;
    fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error>;
    fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error>;
}

impl<T: Write> WriteLeExt for T {
    type Error = <Self as IoBase>::Error;

    fn write_u8(&mut self, n: u8) -> Result<(), Self::Error> {
        self.write_all(&[n])
    }

    fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error> {
        self.write_all(&n.to_le_bytes())
    }

    fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error> {
        self.write_all(&n.to_le_bytes())
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::cell::RefCell;
use std::iter::repeat;

use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::progress::ProgressState;
use crate::utils::{expand_template, pad_str};

#[cfg(feature = "improved_unicode")]
use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars.
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Cow<'static, str>,
    // how unicode-big each char in progress_chars is
    char_width: usize,
}

#[cfg(feature = "improved_unicode")]
fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

#[cfg(not(feature = "improved_unicode"))]
fn segment(s: &str) -> Vec<Box<str>> {
    s.chars().map(|x| x.to_string().into()).collect()
}

#[cfg(feature = "improved_unicode")]
fn measure(s: &str) -> usize {
    unicode_width::UnicodeWidthStr::width(s)
}

#[cfg(not(feature = "improved_unicode"))]
fn measure(s: &str) -> usize {
    s.chars().count()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
    c.iter()
        .map(|s| measure(s.as_ref()))
        .fold(None, |acc, new| {
            match acc {
                None => return Some(new),
                Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
            }
            acc
        })
        .unwrap()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars.
    pub fn default_bar() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{wide_bar} {pos}/{len}"),
        }
    }

    /// Returns the default progress bar style for spinners.
    pub fn default_spinner() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{spinner} {msg}"),
        }
    }

    /// Sets the tick character sequence for spinners.
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        self
    }

    /// Sets the tick string sequence for spinners.
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        self
    }

    /// Sets the progress characters `(filled, current, to do)`.
    /// You can pass more then three for a more detailed display.
    /// All passed grapheme clusters need to be of equal width.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        self.char_width = width(&self.progress_chars);
        self
    }

    /// Sets the template string for the progress bar.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = Cow::Owned(s.into());
        self
    }

    /// Returns the tick char for a given number.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number.
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state.
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    pub(crate) fn format_bar(
        &self,
        state: &ProgressState,
        width: usize,
        alt_style: Option<&Style>,
    ) -> String {
        // todo: this code could really use some comments
        let width = width / state.style.char_width;
        let pct = state.fraction();
        let fill = pct * width as f32;
        let fill = fill as usize;
        let head = if pct > 0.0 && fill < width { 1 } else { 0 };

        let pb: String = repeat(&state.style.progress_chars[0])
            .take(fill)
            .map(|b| b.as_ref())
            .collect();

        let cur = if head == 1 {
            let n = state.style.progress_chars.len().saturating_sub(2);
            let cur_char = if n == 0 {
                1
            } else {
                n.saturating_sub((fill * n) % n)
            };
            state.style.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };
        let bg = width.saturating_sub(fill).saturating_sub(head);
        let rest: String = repeat(state.style.progress_chars.last().unwrap())
            .take(bg)
            .map(|b| b.as_ref())
            .collect();
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let wide_element = RefCell::new(None);

            let s = expand_template(line, |var| {
                let key = var.key;

                match key {
                    "wide_bar" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("bar"));
                        "\x00".into()
                    }
                    "bar" => {
                        self.format_bar(state, var.width.unwrap_or(20), var.alt_style.as_ref())
                    }
                    "spinner" => state.current_tick_str().to_string(),
                    "wide_msg" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("msg"));
                        "\x00".into()
                    }
                    "msg" => state.message().to_string(),
                    "prefix" => state.prefix().to_string(),
                    "pos" => pos.to_string(),
                    "len" => len.to_string(),
                    "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                    "bytes" => format!("{}", HumanBytes(state.pos)),
                    "total_bytes" => format!("{}", HumanBytes(state.len)),
                    "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                    "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                    "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                    "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                    "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                    "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                    "per_sec" => format!("{}/s", state.per_sec()),
                    "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                    "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                    "eta" => format!("{:#}", HumanDuration(state.eta())),
                    _ => "".into(),
                }
            });

            rv.push(if let Some(ref var) = *wide_element.borrow() {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state, bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}
<commit_msg>Fix fine-grained progress bars (#233)<commit_after>use std::borrow::Cow;
use std::cell::RefCell;

use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::progress::ProgressState;
use crate::utils::{expand_template, pad_str};

#[cfg(feature = "improved_unicode")]
use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars.
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Cow<'static, str>,
    // how unicode-big each char in progress_chars is
    char_width: usize,
}

#[cfg(feature = "improved_unicode")]
fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

#[cfg(not(feature = "improved_unicode"))]
fn segment(s: &str) -> Vec<Box<str>> {
    s.chars().map(|x| x.to_string().into()).collect()
}

#[cfg(feature = "improved_unicode")]
fn measure(s: &str) -> usize {
    unicode_width::UnicodeWidthStr::width(s)
}

#[cfg(not(feature = "improved_unicode"))]
fn measure(s: &str) -> usize {
    s.chars().count()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
    c.iter()
        .map(|s| measure(s.as_ref()))
        .fold(None, |acc, new| {
            match acc {
                None => return Some(new),
                Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
            }
            acc
        })
        .unwrap()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars.
    pub fn default_bar() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{wide_bar} {pos}/{len}"),
        }
    }

    /// Returns the default progress bar style for spinners.
    pub fn default_spinner() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{spinner} {msg}"),
        }
    }

    /// Sets the tick character sequence for spinners.
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        self
    }

    /// Sets the tick string sequence for spinners.
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        self
    }

    /// Sets the progress characters `(filled, current, to do)`.
    /// You can pass more then three for a more detailed display.
    /// All passed grapheme clusters need to be of equal width.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        self.char_width = width(&self.progress_chars);
        self
    }

    /// Sets the template string for the progress bar.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = Cow::Owned(s.into());
        self
    }

    /// Returns the tick char for a given number.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number.
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state.
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    pub(crate) fn format_bar(
        &self,
        state: &ProgressState,
        width: usize,
        alt_style: Option<&Style>,
    ) -> String {
        // The number of clusters from progress_chars to write (rounding down).
        let width = width / state.style.char_width;
        // The number of full clusters (including a fractional component for a partially-full one).
        let fill = state.fraction() * width as f32;
        // The number of entirely full clusters (by truncating `fill`).
        let entirely_filled = fill as usize;
        // 1 if the bar is not entirely empty or full (meaning we need to draw the "current"
        // character between the filled and "to do" segment), 0 otherwise.
        let head = if fill > 0.0 && entirely_filled < width {
            1
        } else {
            0
        };

        let pb = state.style.progress_chars[0].repeat(entirely_filled);

        let cur = if head == 1 {
            // Number of fine-grained progress entries in progress_chars.
            let n = state.style.progress_chars.len().saturating_sub(2);
            let cur_char = if n <= 1 {
                // No fine-grained entries. 1 is the single "current" entry if we have one, the "to
                // do" entry if not.
                1
            } else {
                // Pick a fine-grained entry, ranging from the last one (n) if the fractional part
                // of fill is 0 to the first one (1) if the fractional part of fill is almost 1.
                n.saturating_sub((fill.fract() * n as f32) as usize)
            };
            state.style.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };

        // Number of entirely empty clusters needed to fill the bar up to `width`.
        let bg = width.saturating_sub(entirely_filled).saturating_sub(head);
        let rest = state.style.progress_chars.last().unwrap().repeat(bg);
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let wide_element = RefCell::new(None);

            let s = expand_template(line, |var| {
                let key = var.key;

                match key {
                    "wide_bar" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("bar"));
                        "\x00".into()
                    }
                    "bar" => {
                        self.format_bar(state, var.width.unwrap_or(20), var.alt_style.as_ref())
                    }
                    "spinner" => state.current_tick_str().to_string(),
                    "wide_msg" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("msg"));
                        "\x00".into()
                    }
                    "msg" => state.message().to_string(),
                    "prefix" => state.prefix().to_string(),
                    "pos" => pos.to_string(),
                    "len" => len.to_string(),
                    "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                    "bytes" => format!("{}", HumanBytes(state.pos)),
                    "total_bytes" => format!("{}", HumanBytes(state.len)),
                    "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                    "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                    "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                    "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                    "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                    "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                    "per_sec" => format!("{}/s", state.per_sec()),
                    "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                    "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                    "eta" => format!("{:#}", HumanDuration(state.eta())),
                    _ => "".into(),
                }
            });

            rv.push(if let Some(ref var) = *wide_element.borrow() {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state, bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}
<|endoftext|>