<commit_before>#![allow(dead_code)]

//! Diagnostics API.

// Right now this is implemented on top of codespan, but fixes a few things I
// don't like about the API.

use codespan;
use codespan_reporting::diagnostic as imp;
use codespan_reporting::term;
use std::{
    cell::RefCell,
    fmt::{self, Debug},
    hash::Hash,
    iter::{FromIterator, IntoIterator},
    rc::Rc,
};
use termcolor::{self, ColorChoice};

pub mod db {
    use super::*;
    use codespan_reporting::files::{Line, SimpleFile};
    use salsa::{self, InternKey};
    use std::sync::Arc;

    intern_key!(FileId);
    impl FileId {
        pub fn lookup(&self, db: &impl FileInterner) -> crate::File {
            db.lookup_intern_file(*self)
        }
    }
    impl PartialOrd for FileId {
        fn partial_cmp(&self, other: &FileId) -> Option<std::cmp::Ordering> {
            Some(self.cmp(other))
        }
    }
    impl Ord for FileId {
        fn cmp(&self, other: &FileId) -> std::cmp::Ordering {
            self.as_intern_id()
                .as_usize()
                .cmp(&other.as_intern_id().as_usize())
        }
    }

    #[salsa::query_group(FileInternerStorage)]
    pub trait FileInterner
    where
        // This code should stay agnostic to the actual file type.
        crate::File: File,
    {
        #[salsa::interned]
        fn intern_file(&self, file: crate::File) -> FileId;
    }

    /// Since the Files trait (and libclang) copy the entire file contents every
    /// time we request them, we need a way of caching those contents. We then
    /// wrap them in SimpleFile, which creates an index of the start of every
    /// line.
    #[derive(Debug, Clone)]
    pub struct BasicFile(SimpleFile<Arc<str>, Arc<str>>);
    impl PartialEq for BasicFile {
        fn eq(&self, other: &BasicFile) -> bool {
            self.0.origin() == other.0.origin() && self.0.source() == other.0.source()
        }
    }
    impl Eq for BasicFile {}

    /// Cache for [`BasicFile`]. Should not be used outside of the `diagnostics` module.
    #[salsa::query_group(BasicFileCacheStorage)]
    pub trait BasicFileCache: FileInterner {
        fn basic_file(&self, id: FileId) -> Arc<BasicFile>;
    }
    fn basic_file(db: &impl FileInterner, id: FileId) -> Arc<BasicFile> {
        let file = id.lookup(db);
        Arc::new(BasicFile(SimpleFile::new(
            file.name().into(),
            file.contents().into(),
        )))
    }

    /// Adapter between salsa, BasicFile, and the Files trait.
    pub(super) struct FilesWrapper<'db, DB: BasicFileCache>(pub(super) &'db DB);
    impl<'a, DB: BasicFileCache> codespan_reporting::files::Files<'a> for FilesWrapper<'a, DB> {
        type FileId = FileId;
        type Origin = Arc<str>;
        type LineSource = String;

        fn origin(&self, id: FileId) -> Option<Self::Origin> {
            Some(self.0.basic_file(id).0.origin().clone())
        }

        fn line(&self, id: FileId, line_idx: usize) -> Option<Line<String>> {
            self.0.basic_file(id).0.line((), line_idx).map(|line| Line {
                start: line.start,
                number: line.number,
                source: line.source.to_owned(),
            })
        }

        fn line_index(&self, id: FileId, byte_idx: usize) -> Option<usize> {
            self.0.basic_file(id).0.line_index((), byte_idx)
        }
    }
}

//pub use codespan::FileId;

pub use db::FileId;

/// The source code associated with an object.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Span {
    file_id: FileId,
    span: codespan::Span,
}

impl Span {
    pub fn new(file_id: FileId, start_offset: u32, end_offset: u32) -> Self {
        Span {
            file_id,
            span: codespan::Span::new(start_offset, end_offset),
        }
    }

    pub fn label(&self, message: impl Into<String>) -> Label {
        let range = self.span.start().to_usize()..self.span.end().to_usize();
        Label(imp::Label::primary(self.file_id, range).with_message(message))
    }
}

/// A message associated with a Span.
pub struct Label(imp::Label<FileId>);

/// Creates diagnostics and keeps track of statistics for a compile session.
pub struct DiagnosticsCtx(Rc<RefCell<CtxInner>>);

struct CtxInner {
    counts: Counts,
    mode: Mode,
}

enum Mode {
    Term { writer: termcolor::StandardStream },
    Test { errs: Vec<String> },
}

type Count = u32;
#[derive(Default)]
struct Counts {
    bugs: Count,
    errors: Count,
    warns: Count,
    infos: Count,
    helps: Count,
}

impl DiagnosticsCtx {
    pub fn new() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Term {
                writer: termcolor::StandardStream::stderr(ColorChoice::Auto),
            },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn test() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Test { errs: vec![] },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn get_test(&self) -> Vec<String> {
        match &self.0.borrow().mode {
            Mode::Test { errs } => errs.clone(),
            _ => panic!("expected test mode"),
        }
    }

    pub fn has_bugs(&self) -> bool {
        // TODO: should we just panic instead?
        self.0.borrow().counts.errors > 0
    }

    pub fn has_errors(&self) -> bool {
        self.0.borrow().counts.errors > 0
    }

    fn clone(&self) -> Self {
        DiagnosticsCtx(Rc::clone(&self.0))
    }
}

/// A representation of a source file to be used with `SourceFileMap`.
pub trait File: Clone + Eq + Hash + Debug {
    /// Returns the name of the file (often, the path to it). This will be used
    /// in diagnostics messages.
    fn name(&self) -> String;

    /// Returns the contents of the file.
    fn contents(&self) -> String;
}

#[must_use]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic(imp::Diagnostic<db::FileId>);

impl fmt::Debug for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // TODO
        write!(f, "Diagnostic({})", self.message())
    }
}

impl Diagnostic {
    pub fn bug(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::bug()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn error(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::error()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn warn(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::warning()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn info(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::note()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn help(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::help()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn emit(self, db: &'_ impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        let CtxInner { mode, counts, .. } = &mut *ctx.0.borrow_mut();
        match self.0.severity {
            imp::Severity::Bug => counts.bugs += 1,
            imp::Severity::Error => counts.errors += 1,
            imp::Severity::Warning => counts.warns += 1,
            imp::Severity::Note => (),
            imp::Severity::Help => counts.helps += 1,
        }
        match mode {
            Mode::Term { writer } => {
                term::emit(writer, &Default::default(), &db::FilesWrapper(db), &self.0)
                    .expect("failed to emit diagnostic")
            }
            Mode::Test { errs } => errs.push(self.0.message),
        }
    }

    pub fn with_note(mut self, note: impl Into<String>) -> Self {
        self.0.notes.push(note.into());
        self
    }

    pub fn with_notes(mut self, notes: impl IntoIterator<Item = String>) -> Diagnostic {
        self.0.notes.extend(notes);
        self
    }

    pub fn with_label(mut self, secondary_label: Label) -> Diagnostic {
        self.0.labels.push(secondary_label.0);
        self
    }

    pub fn with_labels(mut self, seconadry_labels: impl IntoIterator<Item = Label>) -> Diagnostic {
        self.0
            .labels
            .extend(seconadry_labels.into_iter().map(|l| l.0));
        self
    }

    pub fn message(&self) -> &str {
        &self.0.message
    }
}

/// An ordered list of diagnostics.
// TODO: panic if a Diagnostic[s] is dropped without ever being emitted
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Diagnostics {
    val: Vec<Diagnostic>,
}
impl Diagnostics {
    fn new() -> Diagnostics {
        Diagnostics { val: vec![] }
    }

    pub fn build(f: impl FnOnce(&mut Diagnostics)) -> Diagnostics {
        let mut diags = Diagnostics::new();
        f(&mut diags);
        diags
    }

    /// Adds a diagnostic.
    pub fn add(&mut self, diag: Diagnostic) {
        self.val.push(diag);
    }

    /// Combines diagnostics from `self` and `second` in order.
    ///
    /// This is useful when combining diagnostics from a query result with those
    /// from another operation, for example.
    ///
    /// The conventional way to call this method is using UFCS, e.g.
    ///
    /// ```
    /// Diagnostics::merge(&first, &second)
    /// ```
    ///
    /// but it can be useful to call as a method when chaining multiple merges.
    pub fn merge(&self, second: &Diagnostics) -> Diagnostics {
        // One day we can optimize the data structure for this case, instead of
        // cloning. We'll probably use Arcs, but optimize for the empty case.
        Diagnostics {
            val: self.val.iter().chain(second.val.iter()).cloned().collect(),
        }
    }

    /// Consumes `other`, adding all diagnostics to `self`.
    pub fn append(&mut self, mut other: Diagnostics) {
        self.val.append(&mut other.val);
    }

    pub fn emit(self, db: &impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        for diag in self.val {
            diag.emit(db, ctx);
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
        self.val.iter()
    }
}
impl From<Diagnostic> for Diagnostics {
    fn from(err: Diagnostic) -> Self {
        Diagnostics { val: vec![err] }
    }
}
impl From<Diagnostics> for Vec<Diagnostic> {
    fn from(diags: Diagnostics) -> Self {
        diags.val
    }
}

/// A value, plus any diagnostics that occurred while computing the value.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Outcome<T> {
    val: T,
    err: Diagnostics,
}
impl<T> Outcome<T> {
    pub fn from_ok(val: T) -> Outcome<T> {
        Outcome {
            val,
            err: Diagnostics::new(),
        }
    }

    pub fn from_err(val: T, err: Diagnostic) -> Outcome<T> {
        Outcome {
            val,
            err: err.into(),
        }
    }

    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn as_ref(&self) -> Outcome<&T> {
        Outcome {
            val: &self.val,
            err: self.err.clone(),
        }
    }

    pub fn val(self) -> Result<T, Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> T {
        self.val
    }

    pub fn errs(self) -> Diagnostics {
        self.err
    }

    pub fn split(self) -> (T, Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }
}

pub fn ok<T>(val: T) -> Outcome<T> {
    Outcome::from_ok(val)
}

pub fn err<T>(val: T, err: Diagnostic) -> Outcome<T> {
    Outcome::from_err(val, err)
}

impl<A, B> FromIterator<Outcome<A>> for Outcome<B>
where
    B: FromIterator<A>,
{
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = Outcome<A>>,
    {
        let mut errs = Diagnostics::new();
        let vals = iter.into_iter().map(|oc| {
            errs.append(oc.err);
            oc.val
        });
        Outcome {
            val: B::from_iter(vals),
            err: errs,
        }
    }
}

impl<T> From<(T, Diagnostics)> for Outcome<T> {
    fn from(x: (T, Diagnostics)) -> Self {
        Outcome { val: x.0, err: x.1 }
    }
}

impl From<Diagnostics> for Outcome<()> {
    fn from(err: Diagnostics) -> Self {
        Outcome { val: (), err }
    }
}
<commit_msg>Add RefOutcome with non-destructive semantics<commit_after>#![allow(dead_code)]

//! Diagnostics API.

// Right now this is implemented on top of codespan, but fixes a few things I
// don't like about the API.

use codespan;
use codespan_reporting::diagnostic as imp;
use codespan_reporting::term;
use std::{
    cell::RefCell,
    fmt::{self, Debug},
    hash::Hash,
    iter::{FromIterator, IntoIterator},
    rc::Rc,
};
use termcolor::{self, ColorChoice};

pub mod db {
    use super::*;
    use codespan_reporting::files::{Line, SimpleFile};
    use salsa::{self, InternKey};
    use std::sync::Arc;

    intern_key!(FileId);
    impl FileId {
        pub fn lookup(&self, db: &impl FileInterner) -> crate::File {
            db.lookup_intern_file(*self)
        }
    }
    impl PartialOrd for FileId {
        fn partial_cmp(&self, other: &FileId) -> Option<std::cmp::Ordering> {
            Some(self.cmp(other))
        }
    }
    impl Ord for FileId {
        fn cmp(&self, other: &FileId) -> std::cmp::Ordering {
            self.as_intern_id()
                .as_usize()
                .cmp(&other.as_intern_id().as_usize())
        }
    }

    #[salsa::query_group(FileInternerStorage)]
    pub trait FileInterner
    where
        // This code should stay agnostic to the actual file type.
        crate::File: File,
    {
        #[salsa::interned]
        fn intern_file(&self, file: crate::File) -> FileId;
    }

    /// Since the Files trait (and libclang) copy the entire file contents every
    /// time we request them, we need a way of caching those contents. We then
    /// wrap them in SimpleFile, which creates an index of the start of every
    /// line.
    #[derive(Debug, Clone)]
    pub struct BasicFile(SimpleFile<Arc<str>, Arc<str>>);
    impl PartialEq for BasicFile {
        fn eq(&self, other: &BasicFile) -> bool {
            self.0.origin() == other.0.origin() && self.0.source() == other.0.source()
        }
    }
    impl Eq for BasicFile {}

    /// Cache for [`BasicFile`]. Should not be used outside of the `diagnostics` module.
    #[salsa::query_group(BasicFileCacheStorage)]
    pub trait BasicFileCache: FileInterner {
        fn basic_file(&self, id: FileId) -> Arc<BasicFile>;
    }
    fn basic_file(db: &impl FileInterner, id: FileId) -> Arc<BasicFile> {
        let file = id.lookup(db);
        Arc::new(BasicFile(SimpleFile::new(
            file.name().into(),
            file.contents().into(),
        )))
    }

    /// Adapter between salsa, BasicFile, and the Files trait.
    pub(super) struct FilesWrapper<'db, DB: BasicFileCache>(pub(super) &'db DB);
    impl<'a, DB: BasicFileCache> codespan_reporting::files::Files<'a> for FilesWrapper<'a, DB> {
        type FileId = FileId;
        type Origin = Arc<str>;
        type LineSource = String;

        fn origin(&self, id: FileId) -> Option<Self::Origin> {
            Some(self.0.basic_file(id).0.origin().clone())
        }

        fn line(&self, id: FileId, line_idx: usize) -> Option<Line<String>> {
            self.0.basic_file(id).0.line((), line_idx).map(|line| Line {
                start: line.start,
                number: line.number,
                source: line.source.to_owned(),
            })
        }

        fn line_index(&self, id: FileId, byte_idx: usize) -> Option<usize> {
            self.0.basic_file(id).0.line_index((), byte_idx)
        }
    }
}

//pub use codespan::FileId;

pub use db::FileId;

/// The source code associated with an object.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Span {
    file_id: FileId,
    span: codespan::Span,
}

impl Span {
    pub fn new(file_id: FileId, start_offset: u32, end_offset: u32) -> Self {
        Span {
            file_id,
            span: codespan::Span::new(start_offset, end_offset),
        }
    }

    pub fn label(&self, message: impl Into<String>) -> Label {
        let range = self.span.start().to_usize()..self.span.end().to_usize();
        Label(imp::Label::primary(self.file_id, range).with_message(message))
    }
}

/// A message associated with a Span.
pub struct Label(imp::Label<FileId>);

/// Creates diagnostics and keeps track of statistics for a compile session.
pub struct DiagnosticsCtx(Rc<RefCell<CtxInner>>);

struct CtxInner {
    counts: Counts,
    mode: Mode,
}

enum Mode {
    Term { writer: termcolor::StandardStream },
    Test { errs: Vec<String> },
}

type Count = u32;
#[derive(Default)]
struct Counts {
    bugs: Count,
    errors: Count,
    warns: Count,
    infos: Count,
    helps: Count,
}

impl DiagnosticsCtx {
    pub fn new() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Term {
                writer: termcolor::StandardStream::stderr(ColorChoice::Auto),
            },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn test() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Test { errs: vec![] },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn get_test(&self) -> Vec<String> {
        match &self.0.borrow().mode {
            Mode::Test { errs } => errs.clone(),
            _ => panic!("expected test mode"),
        }
    }

    pub fn has_bugs(&self) -> bool {
        // TODO: should we just panic instead?
        self.0.borrow().counts.errors > 0
    }

    pub fn has_errors(&self) -> bool {
        self.0.borrow().counts.errors > 0
    }

    fn clone(&self) -> Self {
        DiagnosticsCtx(Rc::clone(&self.0))
    }
}

/// A representation of a source file to be used with `SourceFileMap`.
pub trait File: Clone + Eq + Hash + Debug {
    /// Returns the name of the file (often, the path to it). This will be used
    /// in diagnostics messages.
    fn name(&self) -> String;

    /// Returns the contents of the file.
    fn contents(&self) -> String;
}

#[must_use]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic(imp::Diagnostic<db::FileId>);

impl fmt::Debug for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // TODO
        write!(f, "Diagnostic({})", self.message())
    }
}

impl Diagnostic {
    pub fn bug(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::bug()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn error(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::error()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn warn(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::warning()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn info(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::note()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn help(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::help()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn emit(self, db: &'_ impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        let CtxInner { mode, counts, .. } = &mut *ctx.0.borrow_mut();
        match self.0.severity {
            imp::Severity::Bug => counts.bugs += 1,
            imp::Severity::Error => counts.errors += 1,
            imp::Severity::Warning => counts.warns += 1,
            imp::Severity::Note => (),
            imp::Severity::Help => counts.helps += 1,
        }
        match mode {
            Mode::Term { writer } => {
                term::emit(writer, &Default::default(), &db::FilesWrapper(db), &self.0)
                    .expect("failed to emit diagnostic")
            }
            Mode::Test { errs } => errs.push(self.0.message),
        }
    }

    pub fn with_note(mut self, note: impl Into<String>) -> Self {
        self.0.notes.push(note.into());
        self
    }

    pub fn with_notes(mut self, notes: impl IntoIterator<Item = String>) -> Diagnostic {
        self.0.notes.extend(notes);
        self
    }

    pub fn with_label(mut self, secondary_label: Label) -> Diagnostic {
        self.0.labels.push(secondary_label.0);
        self
    }

    pub fn with_labels(mut self, seconadry_labels: impl IntoIterator<Item = Label>) -> Diagnostic {
        self.0
            .labels
            .extend(seconadry_labels.into_iter().map(|l| l.0));
        self
    }

    pub fn message(&self) -> &str {
        &self.0.message
    }
}

/// An ordered list of diagnostics.
// TODO: panic if a Diagnostic[s] is dropped without ever being emitted
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Diagnostics {
    val: Vec<Diagnostic>,
}
impl Diagnostics {
    fn new() -> Diagnostics {
        Diagnostics { val: vec![] }
    }

    pub fn build(f: impl FnOnce(&mut Diagnostics)) -> Diagnostics {
        let mut diags = Diagnostics::new();
        f(&mut diags);
        diags
    }

    /// Adds a diagnostic.
    pub fn add(&mut self, diag: Diagnostic) {
        self.val.push(diag);
    }

    /// Combines diagnostics from `self` and `second` in order.
    ///
    /// This is useful when combining diagnostics from a query result with those
    /// from another operation, for example.
    ///
    /// The conventional way to call this method is using UFCS, e.g.
    ///
    /// ```
    /// Diagnostics::merge(&first, &second)
    /// ```
    ///
    /// but it can be useful to call as a method when chaining multiple merges.
    pub fn merge(&self, second: &Diagnostics) -> Diagnostics {
        // One day we can optimize the data structure for this case, instead of
        // cloning. We'll probably use Arcs, but optimize for the empty case.
        Diagnostics {
            val: self.val.iter().chain(second.val.iter()).cloned().collect(),
        }
    }

    /// Consumes `other`, adding all diagnostics to `self`.
    pub fn append(&mut self, mut other: Diagnostics) {
        self.val.append(&mut other.val);
    }

    pub fn emit(self, db: &impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        for diag in self.val {
            diag.emit(db, ctx);
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
        self.val.iter()
    }
}
impl From<Diagnostic> for Diagnostics {
    fn from(err: Diagnostic) -> Self {
        Diagnostics { val: vec![err] }
    }
}
impl From<Diagnostics> for Vec<Diagnostic> {
    fn from(diags: Diagnostics) -> Self {
        diags.val
    }
}

/// A value, plus any diagnostics that occurred while computing the value.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Outcome<T> {
    val: T,
    err: Diagnostics,
}
impl<T> Outcome<T> {
    pub fn from_ok(val: T) -> Outcome<T> {
        Outcome {
            val,
            err: Diagnostics::new(),
        }
    }

    pub fn from_err(val: T, err: Diagnostic) -> Outcome<T> {
        Outcome {
            val,
            err: err.into(),
        }
    }

    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn as_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        RefOutcome {
            val: &self.val,
            err: &self.err,
        }
    }

    // Use this when you need to bypass Arc::as_ref.
    pub fn to_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        self.as_ref()
    }

    pub fn val(self) -> Result<T, Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> T {
        self.val
    }

    pub fn errs(self) -> Diagnostics {
        self.err
    }

    pub fn split(self) -> (T, Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }
}

pub fn ok<T>(val: T) -> Outcome<T> {
    Outcome::from_ok(val)
}

pub fn err<T>(val: T, err: Diagnostic) -> Outcome<T> {
    Outcome::from_err(val, err)
}

pub struct RefOutcome<'a, T> {
    val: &'a T,
    err: &'a Diagnostics,
}

impl<'a, T> RefOutcome<'a, T> {
    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn val(self) -> Result<&'a T, &'a Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> &'a T {
        self.val
    }

    pub fn errs(self) -> &'a Diagnostics {
        self.err
    }

    pub fn split(self) -> (&'a T, &'a Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(&'a T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }
}

impl<A, B> FromIterator<Outcome<A>> for Outcome<B>
where
    B: FromIterator<A>,
{
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = Outcome<A>>,
    {
        let mut errs = Diagnostics::new();
        let vals = iter.into_iter().map(|oc| {
            errs.append(oc.err);
            oc.val
        });
        Outcome {
            val: B::from_iter(vals),
            err: errs,
        }
    }
}

impl<T> From<(T, Diagnostics)> for Outcome<T> {
    fn from(x: (T, Diagnostics)) -> Self {
        Outcome { val: x.0, err: x.1 }
    }
}

impl From<Diagnostics> for Outcome<()> {
    fn from(err: Diagnostics) -> Self {
        Outcome { val: (), err }
    }
}
<|endoftext|>


<commit_before>/*!
 * This library provides an API client for Diffbot.
 *
 * Making API requests
 * -------------------
 *
 * There are a handful of different ways to make API calls:
 *
 * 1. The most basic way to make a request is with the ``call()`` function.
 *    Everything must be specified for each request.
 *
 * 2. Use the ``Diffbot`` struct to keep track of your token and API version
 *    and then use its ``.call()`` method to make API calls. This has the
 *    advantage that you can specify those things just once and they'll be
 *    retained.
 *
 * 3. Instead of making a request in one step, you can make it two steps with
 *    the ``prepare_request()`` function. This allows you to specify to Diffbot
 *    certain details of how *it* should make the request. That gives you a
 *    ``Request`` object.
 *
 * 4. In the same manner, if you have a ``Diffbot`` struct you can call the
 *    ``.prepare_request()`` method on it.
 *
 * Prepared requests
 * -----------------
 *
 * If you use the ``prepare_request()`` function or method, you can tweak the
 * request that will be sent to Diffbot. You can alter the User-Agent, Referer
 * or Cookie headers that it will send and then call ``.call()`` to make the
 * request, or you can call ``.post_body()`` to send the HTML yourself, if it
 * is not publicly available to the wider Internet.
 *
 * Getting data out of the result
 * ------------------------------
 *
 * At present, the successful return value of a request is simply a JSON object,
 * a tree map. This *will* make it moderately difficult to work with, but if
 * you're determined, it's possible. You'll end up with results like these:
 *
 *     // First of all, you must, of course, have a response to work on.
 *     let mut response: TreeMap<~str, Json>
 *                     = diffbot::call(..., "article", ...).unwrap();
 *
 *     // Get the title of the article
 *     let title = match response.pop(&~"title").unwrap() {
 *         json::String(s) => s,
 *         _ => unreachable!(),
 *     };
 *
 *     // Get the URL of each image
 *     let image_urls: ~[Url] = match response.pop(&~"images").unwrap() {
 *         json::List(images) => images.move_iter().map(|image| match image {
 *             json::Object(~mut o) => {
 *                 match o.pop(&~"url").unwrap() {
 *                     json::String(ref s) => from_str(s),
 *                     _ => unreachable!(),
 *                 }
 *             },
 *             _ => unreachable!(),
 *         }),
 *         _ => unreachable!(),
 *     }.collect();
 *
 * (Yep, I'll freely admit that these are clumsier than they might be in another
 * language, which might allow something like this:
 *
 *     let response = ...;
 *
 *     let title = response.title;
 *     let image_urls = [from_str(image.url) for image in response.images];
 *
 * In time we may get strongly typed interfaces which would be much nicer, but
 * for now, you'd need to do that yourself. It can be done with the tools in
 * ``extra::serialize``, by the way.)
 */
#[crate_id = "diffbot#1.0"];
#[crate_type = "dylib"];
#[crate_type = "rlib"];
#[doc(html_logo_url = "diffy-d.png",
      html_favicon_url = "http://www.diffbot.com/favicon.ico")];

extern mod extra = "extra#0.10-pre";
extern mod http = "http#0.1-pre";

use std::io::net::tcp::TcpStream;
use extra::json;
use extra::url::Url;
use http::client::RequestWriter;
use http::method::{Get, Post};
use http::headers::content_type::MediaType;

/// A convenience type which simply keeps track of a developer token and version
/// number.
///
/// There is no necessity to use this type; you can call ``call()`` directly
/// should you so desire.
#[deriving(Eq, Clone)]
pub struct Diffbot {
    /// The developer's token
    token: ~str,

    /// The API version number
    version: uint,
}

// Basic methods
impl Diffbot {
    /// Construct a new ``Diffbot`` instance from the passed parameters.
    pub fn new(token: ~str, version: uint) -> Diffbot {
        Diffbot {
            token: token,
            version: version,
        }
    }

    /// Make a call to any Diffbot API with the stored token and API version.
    ///
    /// See the ``call()`` function for an explanation of the parameters.
    pub fn call(&self, url: &Url, api: &str, fields: &[&str])
               -> Result<json::Object, Error> {
        call(url, self.token, api, fields, self.version)
    }

    /// Prepare a request to any Diffbot API with the stored token and API version.
    ///
    /// See the ``call()`` function for an explanation of the parameters.
    pub fn prepare_request(&self, url: &Url, api: &str, fields: &[&str])
               -> Request {
        prepare_request(url, self.token, api, fields, self.version)
    }
}

// Handy wrapper methods for specific diffbot APIs
impl Diffbot {
    /// Call the Diffbot Analyze API.
    ///
    /// See the ``call()`` function for an explanation of the parameters.
    pub fn analyze(&self, url: &Url, fields: &[&str])
                  -> Result<json::Object, Error> {
        self.call(url, "analyze", fields)
    }

    /// Call the Diffbot Article API.
    ///
    /// See the ``call()`` function for an explanation of the parameters.
    pub fn article(&self, url: &Url, fields: &[&str])
                  -> Result<json::Object, Error> {
        self.call(url, "article", fields)
    }
}

/// An in-progress Diffbot API call.
pub struct Request {
    priv request: RequestWriter<TcpStream>,
}

impl Request {
    /// Set the value for Diffbot to send as the ``User-Agent`` header when
    /// making your request.
    pub fn user_agent(&mut self, user_agent: ~str) {
        self.request.headers.extensions.insert(~"X-Forwarded-User-Agent",
                                               user_agent);
    }

    /// Set the value for Diffbot to send as the ``Referer`` header when
    /// making your request.
    pub fn referer(&mut self, referer: ~str) {
        self.request.headers.extensions.insert(~"X-Forwarded-Referer",
                                               referer);
    }

    /// Set the value for Diffbot to send as the ``Cookie`` header when
    /// making your request.
    pub fn cookie(&mut self, cookie: ~str) {
        self.request.headers.extensions.insert(~"X-Forwarded-Cookie",
                                               cookie);
    }

    /// Set Diffbot's timeout, in milliseconds. The default is five seconds.
    pub fn timeout(&mut self, milliseconds: u64) {
        self.request.url.query.push((~"timeout", milliseconds.to_str()));
    }

    /// Execute the request and get the results.
    pub fn call(self) -> Result<json::Object, Error> {
        let mut response = match self.request.read_response() {
            Ok(response) => response,
            Err(_request) => return Err(IoError),  // Request failed
        };
        let json = match json::from_reader(&mut response as &mut Reader) {
            Ok(json) => json,
            Err(error) => return Err(JsonError(error)),  // It... wasn't JSON!?
        };
        // Now let's see if this is an API error or not.
        // API errors are of the form {"error":"Invalid API.","errorCode":500}
        match json {
            json::Object(~mut o) => {
                match o.pop(&~"errorCode") {
                    Some(json::Number(num)) => {
                        let num = num as uint;
                        let msg = match o.pop(&~"error")
                               .expect("JSON had errorCode but not error") {
                            json::String(s) => s,
                            uh_oh => fail!("error was {} instead of a string", uh_oh.to_str()),
                        };
                        Err(ApiError(msg, num))
                    },
                    Some(uh_oh) => fail!("errorCode was {} instead of a number", uh_oh.to_str()),
                    None => Ok(o),
                }
            },
            // All API responses must be objects.
            // If it's not, there's something screwy going on.
            _ => fail!("API return value wasn't a JSON object"),
        }
    }

    /// Execute the request as a POST request, sending it through with the given
    /// text/html entity body.
    ///
    /// This has the effect that Diffbot will skip requesting the URL and will
    /// instead take the passed body as the HTML it is to check. This is mainly
    /// useful for non-public websites.
    pub fn post_body(mut self, body: &[u8]) -> Result<json::Object, Error> {
        self.request.method = Post;
        self.request.headers.content_type = Some(MediaType(~"text", ~"html", ~[]));
        self.request.headers.content_length = Some(body.len());
        // Calling write_headers is an extra and unnecessary safety guard which
        // will cause the task to fail if the request has already started to be
        // sent (which would render the three statements above ineffectual)
        self.request.write_headers();
        self.request.write(body);
        self.call()
    }
}

/// Error code: "unauthorized token"
pub static UNAUTHORIZED_TOKEN: uint = 401;

/// Error code: "requested page not found"
pub static REQUESTED_PAGE_NOT_FOUND: uint = 404;

/// Error code: "your token has exceeded the allowed number of calls, or has
/// otherwise been throttled for API abuse."
pub static TOKEN_EXCEEDED_OR_THROTTLED: uint = 429;

/// Error code: "error processing the page. Specific information will be
/// returned in the JSON response."
pub static ERROR_PROCESSING: uint = 500;

/// Something went wrong with the Diffbot API call.
#[deriving(Eq)]
pub enum Error {
    /// An error code returned by the Diffbot API, with message and code.
    /// Refer to http://www.diffbot.com/dev/docs/error/ for an explanation of
    /// the error codes.
    ///
    /// When comparing the error code, you should use these constants:
    ///
    /// - ``UNAUTHORIZED_TOKEN``: "unauthorized token"
    /// - ``REQUESTED_PAGE_NOT_FOUND``: "requested page not found"
    /// - ``TOKEN_EXCEEDED_OR_THROTTLED``: "your token has exceeded the allowed
    ///   number of calls, or has otherwise been throttled for API abuse."
    /// - ``ERROR_PROCESSING``: "error processing the page. Specific information
    ///   will be returned in the JSON response."
    ApiError(~str, uint),

    /// The JSON was not valid. This is one of those ones that *should* never
    /// happen; you know...
    ///
    /// Actually, I can percieve that it might happen if a document returned
    /// included invalid UTF-8, but this case has not been tested.
    JsonError(json::Error),

    /// An I/O error occurred and the condition was trapped somewhere (by you).
    IoError,
}

impl ToStr for Error {
    fn to_str(&self) -> ~str {
        match *self {
            ApiError(ref msg, code) => format!("API error {}: {}", code, *msg),
            JsonError(ref error) => format!("JSON error: {}", error.to_str()),
            IoError => format!("I/O error (already handled)"),
        }
    }
}

/// Make a simple Diffbot API call.
///
/// For more complex requests, use ``Diffbot`` or ``prepare_request()``.
///
/// Arguments
/// =========
///
/// - ``url`` is the URL that you wish Diffbot to operate upon. If this is a
///   publicly-inaccessible URL, you should use ``post_body()`` on a prepared
///   request instead of ``call()``.
///
/// - ``token`` is the developer's token.
///
/// - ``api`` is the name of the API endpoint, e.g. "article", "product".
///
/// - ``fields`` is the set of fields you want the API call to return; it
///   follows the form specified by the Diffbot API and so should have values
///   like "*", "meta", "querystring", "images(*)".
///
/// - ``version`` is the Diffbot API version number.
pub fn call(url: &Url, token: &str, api: &str, fields: &[&str], version: uint)
           -> Result<json::Object, Error> {
    prepare_request(url, token, api, fields, version).call()
}

/// Prepare, but do not send, a request.
///
/// This allows you to use some of the more advanced features of the API like
/// setting certain headers for Diffbot to use, or uploading a private document
/// for it.
pub fn prepare_request(url: &Url, token: &str, api: &str, fields: &[&str],
                       version: uint)
                      -> Request {
    // First of all we must calculate the GET parameters.
    let mut query = ~[(~"token", token.to_owned()),
                      (~"url", url.to_str())];
    if fields.len() > 0 {
        query.push((~"fields", fields.connect(",")));
    }

    // Now that we've got that, we can figure out the complete URL.
    let url = Url::new(~"http",                          // scheme
                       None,                             // user
                       ~"api.diffbot.com",               // host
                       None,                             // port
                       format!("/v{}/{}", version, api),  // path
                       query,                            // query
                       None);                            // fragment

    // And with that, we can now make the request. Whee!
    Request {
        request: RequestWriter::new(Get, url)
    }
}
<commit_msg>Remove experimental wrapper methods for specific APIs.<commit_after>/*!
 * This library provides an API client for Diffbot.
 *
 * Making API requests
 * -------------------
 *
 * There are a handful of different ways to make API calls:
 *
 * 1. The most basic way to make a request is with the ``call()`` function.
 *    Everything must be specified for each request.
 *
 * 2. Use the ``Diffbot`` struct to keep track of your token and API version
 *    and then use its ``.call()`` method to make API calls. This has the
 *    advantage that you can specify those things just once and they'll be
 *    retained.
 *
 * 3. Instead of making a request in one step, you can make it two steps with
 *    the ``prepare_request()`` function. This allows you to specify to Diffbot
 *    certain details of how *it* should make the request. That gives you a
 *    ``Request`` object.
 *
 * 4. In the same manner, if you have a ``Diffbot`` struct you can call the
 *    ``.prepare_request()`` method on it.
 *
 * Prepared requests
 * -----------------
 *
 * If you use the ``prepare_request()`` function or method, you can tweak the
 * request that will be sent to Diffbot. You can alter the User-Agent, Referer
 * or Cookie headers that it will send and then call ``.call()`` to make the
 * request, or you can call ``.post_body()`` to send the HTML yourself, if it
 * is not publicly available to the wider Internet.
 *
 * Getting data out of the result
 * ------------------------------
 *
 * At present, the successful return value of a request is simply a JSON object,
 * a tree map. This *will* make it moderately difficult to work with, but if
 * you're determined, it's possible. You'll end up with results like these:
 *
 *     // First of all, you must, of course, have a response to work on.
 *     let mut response: TreeMap<~str, Json>
 *                     = diffbot::call(..., "article", ...).unwrap();
 *
 *     // Get the title of the article
 *     let title = match response.pop(&~"title").unwrap() {
 *         json::String(s) => s,
 *         _ => unreachable!(),
 *     };
 *
 *     // Get the URL of each image
 *     let image_urls: ~[Url] = match response.pop(&~"images").unwrap() {
 *         json::List(images) => images.move_iter().map(|image| match image {
 *             json::Object(~mut o) => {
 *                 match o.pop(&~"url").unwrap() {
 *                     json::String(ref s) => from_str(s),
 *                     _ => unreachable!(),
 *                 }
 *             },
 *             _ => unreachable!(),
 *         }),
 *         _ => unreachable!(),
 *     }.collect();
 *
 * (Yep, I'll freely admit that these are clumsier than they might be in another
 * language, which might allow something like this:
 *
 *     let response = ...;
 *
 *     let title = response.title;
 *     let image_urls = [from_str(image.url) for image in response.images];
 *
 * In time we may get strongly typed interfaces which would be much nicer, but
 * for now, you'd need to do that yourself. It can be done with the tools in
 * ``extra::serialize``, by the way.)
 */
#[crate_id = "diffbot#1.0"];
#[crate_type = "dylib"];
#[crate_type = "rlib"];
#[doc(html_logo_url = "diffy-d.png",
      html_favicon_url = "http://www.diffbot.com/favicon.ico")];

extern mod extra = "extra#0.10-pre";
extern mod http = "http#0.1-pre";

use std::io::net::tcp::TcpStream;
use extra::json;
use extra::url::Url;
use http::client::RequestWriter;
use http::method::{Get, Post};
use http::headers::content_type::MediaType;

/// A convenience type which simply keeps track of a developer token and version
/// number.
///
/// There is no necessity to use this type; you can call ``call()`` directly
/// should you so desire.
#[deriving(Eq, Clone)]
pub struct Diffbot {
    /// The developer's token
    token: ~str,

    /// The API version number
    version: uint,
}

// Basic methods
impl Diffbot {
    /// Construct a new ``Diffbot`` instance from the passed parameters.
    pub fn new(token: ~str, version: uint) -> Diffbot {
        Diffbot {
            token: token,
            version: version,
        }
    }

    /// Make a call to any Diffbot API with the stored token and API version.
    ///
    /// See the ``call()`` function for an explanation of the parameters.
    pub fn call(&self, url: &Url, api: &str, fields: &[&str])
               -> Result<json::Object, Error> {
        call(url, self.token, api, fields, self.version)
    }

    /// Prepare a request to any Diffbot API with the stored token and API version.
    ///
    /// See the ``call()`` function for an explanation of the parameters.
    pub fn prepare_request(&self, url: &Url, api: &str, fields: &[&str])
               -> Request {
        prepare_request(url, self.token, api, fields, self.version)
    }
}

/// An in-progress Diffbot API call.
pub struct Request {
    priv request: RequestWriter<TcpStream>,
}

impl Request {
    /// Set the value for Diffbot to send as the ``User-Agent`` header when
    /// making your request.
    pub fn user_agent(&mut self, user_agent: ~str) {
        self.request.headers.extensions.insert(~"X-Forwarded-User-Agent",
                                               user_agent);
    }

    /// Set the value for Diffbot to send as the ``Referer`` header when
    /// making your request.
    pub fn referer(&mut self, referer: ~str) {
        self.request.headers.extensions.insert(~"X-Forwarded-Referer",
                                               referer);
    }

    /// Set the value for Diffbot to send as the ``Cookie`` header when
    /// making your request.
    pub fn cookie(&mut self, cookie: ~str) {
        self.request.headers.extensions.insert(~"X-Forwarded-Cookie",
                                               cookie);
    }

    /// Set Diffbot's timeout, in milliseconds. The default is five seconds.
    pub fn timeout(&mut self, milliseconds: u64) {
        self.request.url.query.push((~"timeout", milliseconds.to_str()));
    }

    /// Execute the request and get the results.
    pub fn call(self) -> Result<json::Object, Error> {
        let mut response = match self.request.read_response() {
            Ok(response) => response,
            Err(_request) => return Err(IoError),  // Request failed
        };
        let json = match json::from_reader(&mut response as &mut Reader) {
            Ok(json) => json,
            Err(error) => return Err(JsonError(error)),  // It... wasn't JSON!?
        };
        // Now let's see if this is an API error or not.
        // API errors are of the form {"error":"Invalid API.","errorCode":500}
        match json {
            json::Object(~mut o) => {
                match o.pop(&~"errorCode") {
                    Some(json::Number(num)) => {
                        let num = num as uint;
                        let msg = match o.pop(&~"error")
                               .expect("JSON had errorCode but not error") {
                            json::String(s) => s,
                            uh_oh => fail!("error was {} instead of a string", uh_oh.to_str()),
                        };
                        Err(ApiError(msg, num))
                    },
                    Some(uh_oh) => fail!("errorCode was {} instead of a number", uh_oh.to_str()),
                    None => Ok(o),
                }
            },
            // All API responses must be objects.
            // If it's not, there's something screwy going on.
            _ => fail!("API return value wasn't a JSON object"),
        }
    }

    /// Execute the request as a POST request, sending it through with the given
    /// text/html entity body.
    ///
    /// This has the effect that Diffbot will skip requesting the URL and will
    /// instead take the passed body as the HTML it is to check. This is mainly
    /// useful for non-public websites.
    pub fn post_body(mut self, body: &[u8]) -> Result<json::Object, Error> {
        self.request.method = Post;
        self.request.headers.content_type = Some(MediaType(~"text", ~"html", ~[]));
        self.request.headers.content_length = Some(body.len());
        // Calling write_headers is an extra and unnecessary safety guard which
        // will cause the task to fail if the request has already started to be
        // sent (which would render the three statements above ineffectual)
        self.request.write_headers();
        self.request.write(body);
        self.call()
    }
}

/// Error code: "unauthorized token"
pub static UNAUTHORIZED_TOKEN: uint = 401;

/// Error code: "requested page not found"
pub static REQUESTED_PAGE_NOT_FOUND: uint = 404;

/// Error code: "your token has exceeded the allowed number of calls, or has
/// otherwise been throttled for API abuse."
pub static TOKEN_EXCEEDED_OR_THROTTLED: uint = 429;

/// Error code: "error processing the page. Specific information will be
/// returned in the JSON response."
pub static ERROR_PROCESSING: uint = 500;

/// Something went wrong with the Diffbot API call.
#[deriving(Eq)]
pub enum Error {
    /// An error code returned by the Diffbot API, with message and code.
    /// Refer to http://www.diffbot.com/dev/docs/error/ for an explanation of
    /// the error codes.
    ///
    /// When comparing the error code, you should use these constants:
    ///
    /// - ``UNAUTHORIZED_TOKEN``: "unauthorized token"
    /// - ``REQUESTED_PAGE_NOT_FOUND``: "requested page not found"
    /// - ``TOKEN_EXCEEDED_OR_THROTTLED``: "your token has exceeded the allowed
    ///   number of calls, or has otherwise been throttled for API abuse."
    /// - ``ERROR_PROCESSING``: "error processing the page. Specific information
    ///   will be returned in the JSON response."
    ApiError(~str, uint),

    /// The JSON was not valid. This is one of those ones that *should* never
    /// happen; you know...
    ///
    /// Actually, I can percieve that it might happen if a document returned
    /// included invalid UTF-8, but this case has not been tested.
    JsonError(json::Error),

    /// An I/O error occurred and the condition was trapped somewhere (by you).
    IoError,
}

impl ToStr for Error {
    fn to_str(&self) -> ~str {
        match *self {
            ApiError(ref msg, code) => format!("API error {}: {}", code, *msg),
            JsonError(ref error) => format!("JSON error: {}", error.to_str()),
            IoError => format!("I/O error (already handled)"),
        }
    }
}

/// Make a simple Diffbot API call.
///
/// For more complex requests, use ``Diffbot`` or ``prepare_request()``.
///
/// Arguments
/// =========
///
/// - ``url`` is the URL that you wish Diffbot to operate upon. If this is a
///   publicly-inaccessible URL, you should use ``post_body()`` on a prepared
///   request instead of ``call()``.
///
/// - ``token`` is the developer's token.
///
/// - ``api`` is the name of the API endpoint, e.g. "article", "product".
///
/// - ``fields`` is the set of fields you want the API call to return; it
///   follows the form specified by the Diffbot API and so should have values
///   like "*", "meta", "querystring", "images(*)".
///
/// - ``version`` is the Diffbot API version number.
pub fn call(url: &Url, token: &str, api: &str, fields: &[&str], version: uint)
           -> Result<json::Object, Error> {
    prepare_request(url, token, api, fields, version).call()
}

/// Prepare, but do not send, a request.
///
/// This allows you to use some of the more advanced features of the API like
/// setting certain headers for Diffbot to use, or uploading a private document
/// for it.
pub fn prepare_request(url: &Url, token: &str, api: &str, fields: &[&str],
                       version: uint)
                      -> Request {
    // First of all we must calculate the GET parameters.
    let mut query = ~[(~"token", token.to_owned()),
                      (~"url", url.to_str())];
    if fields.len() > 0 {
        query.push((~"fields", fields.connect(",")));
    }

    // Now that we've got that, we can figure out the complete URL.
    let url = Url::new(~"http",                          // scheme
                       None,                             // user
                       ~"api.diffbot.com",               // host
                       None,                             // port
                       format!("/v{}/{}", version, api),  // path
                       query,                            // query
                       None);                            // fragment

    // And with that, we can now make the request. Whee!
    Request {
        request: RequestWriter::new(Get, url)
    }
}
<|endoftext|>


<commit_before>use std::io::{Cursor, Read, Seek, SeekFrom};

/// Convert num bytes to a u64
fn read_int_from_bytes(bytes: &mut Cursor<&Vec<u8>>, num: usize) -> Result<u64, &'static str> {
    let mut x: Vec<u8> = vec![0; num];
    if bytes.read(&mut x).unwrap() != num {
        return Err("Couldn't read all bytes");
    }
    println!("read_int_from_bytes pos: {:?}", bytes.position());
    let mut result: u64 = 0;
    for i in (0..num).rev() {
        result += (x[num - 1 - i] as u64) << (i * 8);
    }
    Ok(result)
}

/// Read an integer and return it as u64.
fn read_int(bytes: &mut Cursor<&Vec<u8>>) -> Result<u64, &'static str> {
    let first_value = bytes.get_ref()[0] & 0x1F;
    bytes.seek(SeekFrom::Current(1)).unwrap();
    match first_value {
        24 => {
            let result = bytes.get_ref()[1] as u64;
            // Manually advance cursor.
            bytes.seek(SeekFrom::Current(1)).unwrap();
            Ok(result)
        }
        25 => return Ok(read_int_from_bytes(bytes, 2).unwrap()),
        26 => return Ok(read_int_from_bytes(bytes, 4).unwrap()),
        27 => return Ok(read_int_from_bytes(bytes, 8).unwrap()),
        28...31 => return Err("Not well formed"),
        _ => return Ok(first_value as u64),
    }
}

/// Read a signed integer and return it as i64.
fn read_signed_int(bytes: &mut Cursor<&Vec<u8>>) -> Result<i64, &'static str> {
    let uint = read_int(bytes).unwrap();
    if uint > i64::max_value() as u64 {
        return Err("Signed integer doesn't fit in a i64 (too large)");
    }
    let result: i64 = -1 - uint as i64;
    Ok(result)
}

fn to_hex_string(bytes: &[u8]) -> String {
    let strs: Vec<String> = bytes.iter().map(|b| format!("{:02X}", b)).collect();
    strs.join("")
}

/// Read a byte string and return it as hex string.
fn read_byte_string(bytes: &mut Cursor<&Vec<u8>>) -> Result<String, &'static str> {
    let length = read_int(bytes).unwrap();
    if length > usize::max_value() as u64 {
        return Err("Byte array is too large to allocate.");
    }
    let length = length as usize;
    let mut byte_string: Vec<u8> = Vec::with_capacity(length);
    unsafe {
        byte_string.set_len(length);
    } // with_capacity doesn't set the size :(
    if bytes.read(&mut byte_string).unwrap() != length {
        return Err("Couldn't read enough data for this byte string");
    }
    Ok(to_hex_string(&byte_string))
}

// #[allow(unused_variables)]
// fn read_utf8_string(bytes: &Vec<u8>) -> Result<String, &'static str> {
//     Err("not implemented")
// }

// #[allow(unused_variables)]
// fn read_array(bytes: &Vec<u8>) -> Result<String, &'static str> {
//     Err("not implemented")
// }

// #[allow(unused_variables)]
// fn read_map(bytes: &Vec<u8>) -> Result<String, &'static str> {
//     Err("not implemented")
// }

/// Read the CBOR structure in bytes and return it as a string.
pub fn decode_element(bytes: &Vec<u8>) -> Result<String, &'static str> {
    let major_type = bytes[0] >> 5;
    let mut byte_cursor = Cursor::new(bytes);
    match major_type {
        0 => return Ok(read_int(&mut byte_cursor).unwrap().to_string()),
        1 => return Ok(read_signed_int(&mut byte_cursor).unwrap().to_string()),
        2 => return read_byte_string(&mut byte_cursor),
        // 3 => return read_utf8_string(&bytes),
        // 4 => return read_array(&bytes),
        // 5 => return read_map(&bytes),
        6 => return Err("semantic tags are not implemented"),
        7 => return Err("major type 7 is not implemented"),
        _ => return Err("malformed first byte"),
    }
}
<commit_msg>add utf-8 strings<commit_after>use std::io::{Cursor, Read, Seek, SeekFrom};

/// Convert num bytes to a u64
fn read_int_from_bytes(bytes: &mut Cursor<&Vec<u8>>, num: usize) -> Result<u64, &'static str> {
    let mut x: Vec<u8> = vec![0; num];
    if bytes.read(&mut x).unwrap() != num {
        return Err("Couldn't read all bytes");
    }
    println!("read_int_from_bytes pos: {:?}", bytes.position());
    let mut result: u64 = 0;
    for i in (0..num).rev() {
        result += (x[num - 1 - i] as u64) << (i * 8);
    }
    Ok(result)
}

/// Read an integer and return it as u64.
fn read_int(bytes: &mut Cursor<&Vec<u8>>) -> Result<u64, &'static str> {
    let first_value = bytes.get_ref()[0] & 0x1F;
    bytes.seek(SeekFrom::Current(1)).unwrap();
    match first_value {
        24 => {
            let result = bytes.get_ref()[1] as u64;
            // Manually advance cursor.
            bytes.seek(SeekFrom::Current(1)).unwrap();
            Ok(result)
        }
        25 => return Ok(read_int_from_bytes(bytes, 2).unwrap()),
        26 => return Ok(read_int_from_bytes(bytes, 4).unwrap()),
        27 => return Ok(read_int_from_bytes(bytes, 8).unwrap()),
        28...31 => return Err("Not well formed"),
        _ => return Ok(first_value as u64),
    }
}

/// Read a signed integer and return it as i64.
fn read_signed_int(bytes: &mut Cursor<&Vec<u8>>) -> Result<i64, &'static str> {
    let uint = read_int(bytes).unwrap();
    if uint > i64::max_value() as u64 {
        return Err("Signed integer doesn't fit in a i64 (too large)");
    }
    let result: i64 = -1 - uint as i64;
    Ok(result)
}

/// Read a byte string and return it as hex string.
fn read_byte_string(bytes: &mut Cursor<&Vec<u8>>) -> Result<Vec<u8>, &'static str> {
    let length = read_int(bytes).unwrap();
    if length > usize::max_value() as u64 {
        return Err("Byte array is too large to allocate.");
    }
    let length = length as usize;
    let mut byte_string: Vec<u8> = Vec::with_capacity(length);
    unsafe {
        byte_string.set_len(length);
    } // with_capacity doesn't set the size :(
    if bytes.read(&mut byte_string).unwrap() != length {
        return Err("Couldn't read enough data for this byte string");
    }
    Ok(byte_string)
}

fn read_utf8_string(bytes: &mut Cursor<&Vec<u8>>) -> Result<String, &'static str> {
    read_byte_string(bytes);
    Err("not implemented")
}

// #[allow(unused_variables)]
// fn read_array(bytes: &Vec<u8>) -> Result<String, &'static str> {
//     Err("not implemented")
// }

// #[allow(unused_variables)]
// fn read_map(bytes: &Vec<u8>) -> Result<String, &'static str> {
//     Err("not implemented")
// }

fn to_hex_string(bytes: &[u8]) -> String {
    let strs: Vec<String> = bytes.iter().map(|b| format!("{:02X}", b)).collect();
    strs.join("")
}

/// Read the CBOR structure in bytes and return it as a string.
pub fn decode_element(bytes: &Vec<u8>) -> Result<String, &'static str> {
    let major_type = bytes[0] >> 5;
    let mut byte_cursor = Cursor::new(bytes);
    match major_type {
        0 => return Ok(read_int(&mut byte_cursor).unwrap().to_string()),
        1 => return Ok(read_signed_int(&mut byte_cursor).unwrap().to_string()),
        2 => return Ok(to_hex_string(&read_byte_string(&mut byte_cursor).unwrap())),
        3 => return read_utf8_string(&mut byte_cursor),
        // 4 => return read_array(&bytes),
        // 5 => return read_map(&bytes),
        6 => return Err("semantic tags are not implemented"),
        7 => return Err("major type 7 is not implemented"),
        _ => return Err("malformed first byte"),
    }
}
<|endoftext|>


<commit_before>use common::*;
use arch::paging::{BASE_PAGE_LENGTH};
use util::{MemoryObject, UniqueReadGuard, UniqueWriteGuard, RwLock};
use util::managed_arc::{ManagedWeakPool1Arc};
use core::marker::{PhantomData};
use core::any::{Any};
use super::{PageDescriptor, PageCap};
use cap::{UntypedDescriptor, SetDefault};

pub const PAGE_LENGTH: usize = BASE_PAGE_LENGTH;

impl<T: SetDefault + Any> PageCap<T> {
    pub fn retype_from(untyped: &mut UntypedDescriptor) -> Self {
        let mut arc: Option<Self> = None;

        let start_paddr = unsafe { untyped.allocate(BASE_PAGE_LENGTH, BASE_PAGE_LENGTH) };

        let mapped_weak_pool = unsafe { ManagedWeakPool1Arc::create(
            untyped.allocate(ManagedWeakPool1Arc::inner_length(),
                             ManagedWeakPool1Arc::inner_alignment())) };

        unsafe {
            untyped.derive(Self::inner_length(), Self::inner_alignment(), |paddr, next_child| {
                let mut desc = PageDescriptor::<T> {
                    mapped_weak_pool: mapped_weak_pool,
                    start_paddr: start_paddr,
                    next: next_child,
                    _marker: PhantomData
                };

                desc.write().set_default();

                arc = Some(unsafe {
                    Self::new(paddr, RwLock::new(desc))
                });

                arc.clone().unwrap().into()
            });
        }

        arc.unwrap()
    }

    pub const fn length() -> usize {
        BASE_PAGE_LENGTH
    }
}

impl<T: SetDefault + Any> PageDescriptor<T> {
    pub fn start_paddr(&self) -> PAddr {
        self.start_paddr
    }

    pub fn length(&self) -> usize {
        BASE_PAGE_LENGTH
    }

    fn page_object(&self) -> MemoryObject<T> {
        unsafe { MemoryObject::new(self.start_paddr) }
    }

    pub fn read(&self) -> UniqueReadGuard<T> {
        unsafe { UniqueReadGuard::new(self.page_object()) }
    }

    pub fn write(&mut self) -> UniqueWriteGuard<T> {
        unsafe { UniqueWriteGuard::new(self.page_object()) }
    }
}
<commit_msg>Assert PageCap's T's size is valid<commit_after>use common::*;
use arch::paging::{BASE_PAGE_LENGTH};
use util::{MemoryObject, UniqueReadGuard, UniqueWriteGuard, RwLock};
use util::managed_arc::{ManagedWeakPool1Arc};
use core::marker::{PhantomData};
use core::any::{Any};
use core::mem;
use super::{PageDescriptor, PageCap};
use cap::{UntypedDescriptor, SetDefault};

pub const PAGE_LENGTH: usize = BASE_PAGE_LENGTH;

impl<T: SetDefault + Any> PageCap<T> {
    pub fn retype_from(untyped: &mut UntypedDescriptor) -> Self {
        assert!(mem::size_of::<T>() <= PAGE_LENGTH);

        let mut arc: Option<Self> = None;

        let start_paddr = unsafe { untyped.allocate(BASE_PAGE_LENGTH, BASE_PAGE_LENGTH) };

        let mapped_weak_pool = unsafe { ManagedWeakPool1Arc::create(
            untyped.allocate(ManagedWeakPool1Arc::inner_length(),
                             ManagedWeakPool1Arc::inner_alignment())) };

        unsafe {
            untyped.derive(Self::inner_length(), Self::inner_alignment(), |paddr, next_child| {
                let mut desc = PageDescriptor::<T> {
                    mapped_weak_pool: mapped_weak_pool,
                    start_paddr: start_paddr,
                    next: next_child,
                    _marker: PhantomData
                };

                desc.write().set_default();

                arc = Some(unsafe {
                    Self::new(paddr, RwLock::new(desc))
                });

                arc.clone().unwrap().into()
            });
        }

        arc.unwrap()
    }

    pub const fn length() -> usize {
        BASE_PAGE_LENGTH
    }
}

impl<T: SetDefault + Any> PageDescriptor<T> {
    pub fn start_paddr(&self) -> PAddr {
        self.start_paddr
    }

    pub fn length(&self) -> usize {
        BASE_PAGE_LENGTH
    }

    fn page_object(&self) -> MemoryObject<T> {
        unsafe { MemoryObject::new(self.start_paddr) }
    }

    pub fn read(&self) -> UniqueReadGuard<T> {
        unsafe { UniqueReadGuard::new(self.page_object()) }
    }

    pub fn write(&mut self) -> UniqueWriteGuard<T> {
        unsafe { UniqueWriteGuard::new(self.page_object()) }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::sync::{Arc, RwLock};
use std::time::Instant;

use console::Term;

/// Target for draw operations
///
/// This tells a progress bar or a multi progress object where to paint to.
/// The draw target is a stateful wrapper over a drawing destination and
/// internally optimizes how often the state is painted to the output
/// device.
#[derive(Debug)]
pub struct ProgressDrawTarget {
    kind: ProgressDrawTargetKind,
}

impl ProgressDrawTarget {
    /// Draw to a buffered stdout terminal at a max of 15 times a second.
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stdout() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stdout(), 15)
    }

    /// Draw to a buffered stderr terminal at a max of 15 times a second.
    ///
    /// This is the default draw target for progress bars.  For more
    /// information see `ProgressDrawTarget::to_term`.
    pub fn stderr() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stderr(), 15)
    }

    /// Draw to a buffered stdout terminal at a max of `refresh_rate` times a second.
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stdout_with_hz(refresh_rate: u64) -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stdout(), refresh_rate)
    }

    /// Draw to a buffered stderr terminal at a max of `refresh_rate` times a second.
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stderr_with_hz(refresh_rate: u64) -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stderr(), refresh_rate)
    }

    /// Draw to a buffered stdout terminal without max framerate.
    ///
    /// This is useful when data is known to come in very slowly and
    /// not rendering some updates would be a problem (for instance
    /// when messages are used extensively).
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stdout_nohz() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stdout(), None)
    }

    /// Draw to a buffered stderr terminal without max framerate.
    ///
    /// This is useful when data is known to come in very slowly and
    /// not rendering some updates would be a problem (for instance
    /// when messages are used extensively).
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stderr_nohz() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stderr(), None)
    }

    pub(crate) fn new_remote(state: Arc<RwLock<MultiProgressState>>, idx: usize) -> Self {
        Self {
            kind: ProgressDrawTargetKind::Remote { state, idx },
        }
    }

    /// Draw to a terminal, optionally with a specific refresh rate.
    ///
    /// Progress bars are by default drawn to terminals however if the
    /// terminal is not user attended the entire progress bar will be
    /// hidden.  This is done so that piping to a file will not produce
    /// useless escape codes in that file.
    ///
    /// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead.
    #[allow(clippy::wrong_self_convention)]
    #[deprecated(since = "0.16.0", note = "Use `ProgressDrawTarget::term` instead")]
    pub fn to_term(term: Term, refresh_rate: impl Into<Option<u64>>) -> ProgressDrawTarget {
        ProgressDrawTarget::term(term, refresh_rate)
    }

    /// Draw to a terminal, optionally with a specific refresh rate.
    ///
    /// Progress bars are by default drawn to terminals however if the
    /// terminal is not user attended the entire progress bar will be
    /// hidden.  This is done so that piping to a file will not produce
    /// useless escape codes in that file.
    ///
    /// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead.
    pub fn term(term: Term, refresh_rate: impl Into<Option<u64>>) -> ProgressDrawTarget {
        ProgressDrawTarget {
            kind: ProgressDrawTargetKind::Term {
                term,
                last_line_count: 0,
                leaky_bucket: refresh_rate.into().map(|rate| LeakyBucket {
                    bucket: MAX_GROUP_SIZE,
                    leak_rate: rate as f64,
                    last_update: Instant::now(),
                }),
            },
        }
    }

    /// A hidden draw target.
    ///
    /// This forces a progress bar to be not rendered at all.
    pub fn hidden() -> ProgressDrawTarget {
        ProgressDrawTarget {
            kind: ProgressDrawTargetKind::Hidden,
        }
    }

    /// Returns true if the draw target is hidden.
    ///
    /// This is internally used in progress bars to figure out if overhead
    /// from drawing can be prevented.
    pub fn is_hidden(&self) -> bool {
        match self.kind {
            ProgressDrawTargetKind::Hidden => true,
            ProgressDrawTargetKind::Term { ref term, .. } => !term.is_term(),
            _ => false,
        }
    }

    /// Returns the current width of the draw target.
    pub(crate) fn width(&self) -> usize {
        match self.kind {
            ProgressDrawTargetKind::Term { ref term, .. } => term.size().1 as usize,
            ProgressDrawTargetKind::Remote { ref state, .. } => state.read().unwrap().width(),
            ProgressDrawTargetKind::Hidden => 0,
        }
    }

    /// Apply the given draw state (draws it).
    pub(crate) fn apply_draw_state(&mut self, draw_state: ProgressDrawState) -> io::Result<()> {
        let (term, last_line_count) = match self.kind {
            ProgressDrawTargetKind::Term {
                ref term,
                ref mut last_line_count,
                leaky_bucket: None,
            } => (term, last_line_count),
            ProgressDrawTargetKind::Term {
                ref term,
                ref mut last_line_count,
                leaky_bucket: Some(ref mut leaky_bucket),
            } => {
                if draw_state.force_draw || leaky_bucket.try_add_work() {
                    (term, last_line_count)
                } else {
                    // rate limited
                    return Ok(());
                }
            }
            ProgressDrawTargetKind::Remote { idx, ref state, .. } => {
                return state
                    .write()
                    .unwrap()
                    .draw(idx, draw_state)
                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e));
            }
            // Hidden, finished, or no need to refresh yet
            _ => return Ok(()),
        };

        if !draw_state.lines.is_empty() && draw_state.move_cursor {
            term.move_cursor_up(*last_line_count)?;
        } else {
            clear_last_lines(term, *last_line_count)?;
        }

        let shift = match draw_state.alignment {
            MultiProgressAlignment::Bottom if draw_state.lines.len() < *last_line_count => {
                let shift = *last_line_count - draw_state.lines.len();
                for _ in 0..shift {
                    term.write_line("")?;
                }
                shift
            }
            _ => 0,
        };
        draw_state.draw_to_term(term)?;
        term.flush()?;
        *last_line_count = draw_state.lines.len() - draw_state.orphan_lines + shift;
        Ok(())
    }

    /// Properly disconnects from the draw target
    pub(crate) fn disconnect(&self) {
        match self.kind {
            ProgressDrawTargetKind::Term { .. } => {}
            ProgressDrawTargetKind::Remote { idx, ref state, .. } => {
                state
                    .write()
                    .unwrap()
                    .draw(
                        idx,
                        ProgressDrawState {
                            lines: vec![],
                            orphan_lines: 0,
                            force_draw: true,
                            move_cursor: false,
                            alignment: Default::default(),
                        },
                    )
                    .ok();
            }
            ProgressDrawTargetKind::Hidden => {}
        };
    }

    pub(crate) fn remote(&self) -> Option<(&Arc<RwLock<MultiProgressState>>, usize)> {
        match &self.kind {
            ProgressDrawTargetKind::Remote { state, idx } => Some((state, *idx)),
            _ => None,
        }
    }
}

#[derive(Debug)]
enum ProgressDrawTargetKind {
    Term {
        term: Term,
        last_line_count: usize,
        leaky_bucket: Option<LeakyBucket>,
    },
    Remote {
        state: Arc<RwLock<MultiProgressState>>,
        idx: usize,
    },
    Hidden,
}

#[derive(Debug)]
pub(crate) struct MultiProgressState {
    /// The collection of states corresponding to progress bars
    /// the state is None for bars that have not yet been drawn or have been removed
    pub(crate) draw_states: Vec<Option<ProgressDrawState>>,
    /// Set of removed bars, should have corresponding `None` elements in the `draw_states` vector
    pub(crate) free_set: Vec<usize>,
    /// Indices to the `draw_states` to maintain correct visual order
    pub(crate) ordering: Vec<usize>,
    /// Target for draw operation for MultiProgress
    pub(crate) draw_target: ProgressDrawTarget,
    /// Whether or not to just move cursor instead of clearing lines
    pub(crate) move_cursor: bool,
    /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top`
    pub(crate) alignment: MultiProgressAlignment,
}

impl MultiProgressState {
    fn width(&self) -> usize {
        self.draw_target.width()
    }

    pub(crate) fn draw(&mut self, idx: usize, draw_state: ProgressDrawState) -> io::Result<()> {
        let force_draw = draw_state.force_draw;
        let mut orphan_lines = vec![];

        // Split orphan lines out of the draw state, if any
        let lines = if draw_state.orphan_lines > 0 {
            let split = draw_state.lines.split_at(draw_state.orphan_lines);
            orphan_lines.extend_from_slice(split.0);
            split.1.to_vec()
        } else {
            draw_state.lines
        };

        let draw_state = ProgressDrawState {
            lines,
            orphan_lines: 0,
            ..draw_state
        };

        self.draw_states[idx] = Some(draw_state);

        // the rest from here is only drawing, we can skip it.
        if self.draw_target.is_hidden() {
            return Ok(());
        }

        let mut lines = vec![];

        // Make orphaned lines appear at the top, so they can be properly
        // forgotten.
        let orphan_lines_count = orphan_lines.len();
        lines.append(&mut orphan_lines);

        for index in self.ordering.iter() {
            let draw_state = &self.draw_states[*index];
            if let Some(ref draw_state) = draw_state {
                lines.extend_from_slice(&draw_state.lines[..]);
            }
        }

        self.draw_target.apply_draw_state(ProgressDrawState {
            lines,
            orphan_lines: orphan_lines_count,
            force_draw: force_draw || orphan_lines_count > 0,
            move_cursor: self.move_cursor,
            alignment: self.alignment,
        })
    }

    pub(crate) fn len(&self) -> usize {
        self.draw_states.len() - self.free_set.len()
    }

    pub(crate) fn remove_idx(&mut self, idx: usize) {
        if self.free_set.contains(&idx) {
            return;
        }

        self.draw_states[idx].take();
        self.free_set.push(idx);
        self.ordering.retain(|&x| x != idx);

        assert!(
            self.len() == self.ordering.len(),
            "Draw state is inconsistent"
        );
    }
}

#[derive(Debug)]
pub(crate) struct LeakyBucket {
    leak_rate: f64,
    last_update: Instant,
    bucket: f64,
}

/// Rate limit but allow occasional bursts above desired rate
impl LeakyBucket {
    /// try to add some work to the bucket
    /// return false if the bucket is already full and the work should be skipped
    fn try_add_work(&mut self) -> bool {
        self.leak();
        if self.bucket < MAX_GROUP_SIZE {
            self.bucket += 1.0;
            true
        } else {
            false
        }
    }

    fn leak(&mut self) {
        let ticks = self.last_update.elapsed().as_secs_f64() * self.leak_rate;
        self.bucket -= ticks;
        if self.bucket < 0.0 {
            self.bucket = 0.0;
        }
        self.last_update = Instant::now();
    }
}

const MAX_GROUP_SIZE: f64 = 32.0;

/// The drawn state of an element.
#[derive(Clone, Debug)]
pub(crate) struct ProgressDrawState {
    /// The lines to print (can contain ANSI codes)
    pub lines: Vec<String>,
    /// The number of lines that shouldn't be reaped by the next tick.
    pub orphan_lines: usize,
    /// True if drawing should be forced.
    pub force_draw: bool,
    /// True if we should move the cursor up when possible instead of clearing lines.
    pub move_cursor: bool,
    /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top`
    pub alignment: MultiProgressAlignment,
}

impl ProgressDrawState {
    pub(crate) fn new(lines: Vec<String>, force_draw: bool) -> Self {
        Self {
            lines,
            orphan_lines: 0,
            force_draw,
            move_cursor: false,
            alignment: Default::default(),
        }
    }

    pub fn draw_to_term(&self, term: &Term) -> io::Result<()> {
        let len = self.lines.len();
        for (idx, line) in self.lines.iter().enumerate() {
            if idx + 1 != len {
                term.write_line(line)?;
            } else {
                // Don't append a '\n' if this is the last line
                term.write_str(line)?;
            }
        }
        Ok(())
    }
}

/// Vertical alignment of a multi progress.
///
/// The alignment controls how the multi progress is aligned if some of its progress bars get removed.
/// E.g. `Top` alignment (default), when _progress bar 2_ is removed:
/// ```ignore
/// [0/100] progress bar 1        [0/100] progress bar 1
/// [0/100] progress bar 2   =>   [0/100] progress bar 3
/// [0/100] progress bar 3
/// ```
///
/// `Bottom` alignment
/// ```ignore
/// [0/100] progress bar 1
/// [0/100] progress bar 2   =>   [0/100] progress bar 1
/// [0/100] progress bar 3        [0/100] progress bar 3
/// ```
#[derive(Debug, Copy, Clone)]
pub enum MultiProgressAlignment {
    Top,
    Bottom,
}

impl Default for MultiProgressAlignment {
    fn default() -> Self {
        Self::Top
    }
}

/// Fork of console::clear_last_lines that assumes that the last line doesn't contain a '\n'
fn clear_last_lines(term: &Term, n: usize) -> io::Result<()> {
    term.move_cursor_up(n.saturating_sub(1))?;
    for i in 0..n {
        term.clear_line()?;
        if i + 1 != n {
            term.move_cursor_down(1)?;
        }
    }
    term.move_cursor_up(n.saturating_sub(1))?;
    Ok(())
}
<commit_msg>Add an extra space to the last progress line<commit_after>use std::io;
use std::sync::{Arc, RwLock};
use std::time::Instant;

use console::Term;

/// Target for draw operations
///
/// This tells a progress bar or a multi progress object where to paint to.
/// The draw target is a stateful wrapper over a drawing destination and
/// internally optimizes how often the state is painted to the output
/// device.
#[derive(Debug)]
pub struct ProgressDrawTarget {
    kind: ProgressDrawTargetKind,
}

impl ProgressDrawTarget {
    /// Draw to a buffered stdout terminal at a max of 15 times a second.
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stdout() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stdout(), 15)
    }

    /// Draw to a buffered stderr terminal at a max of 15 times a second.
    ///
    /// This is the default draw target for progress bars.  For more
    /// information see `ProgressDrawTarget::to_term`.
    pub fn stderr() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stderr(), 15)
    }

    /// Draw to a buffered stdout terminal at a max of `refresh_rate` times a second.
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stdout_with_hz(refresh_rate: u64) -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stdout(), refresh_rate)
    }

    /// Draw to a buffered stderr terminal at a max of `refresh_rate` times a second.
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stderr_with_hz(refresh_rate: u64) -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stderr(), refresh_rate)
    }

    /// Draw to a buffered stdout terminal without max framerate.
    ///
    /// This is useful when data is known to come in very slowly and
    /// not rendering some updates would be a problem (for instance
    /// when messages are used extensively).
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stdout_nohz() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stdout(), None)
    }

    /// Draw to a buffered stderr terminal without max framerate.
    ///
    /// This is useful when data is known to come in very slowly and
    /// not rendering some updates would be a problem (for instance
    /// when messages are used extensively).
    ///
    /// For more information see `ProgressDrawTarget::to_term`.
    pub fn stderr_nohz() -> ProgressDrawTarget {
        ProgressDrawTarget::term(Term::buffered_stderr(), None)
    }

    pub(crate) fn new_remote(state: Arc<RwLock<MultiProgressState>>, idx: usize) -> Self {
        Self {
            kind: ProgressDrawTargetKind::Remote { state, idx },
        }
    }

    /// Draw to a terminal, optionally with a specific refresh rate.
    ///
    /// Progress bars are by default drawn to terminals however if the
    /// terminal is not user attended the entire progress bar will be
    /// hidden.  This is done so that piping to a file will not produce
    /// useless escape codes in that file.
    ///
    /// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead.
    #[allow(clippy::wrong_self_convention)]
    #[deprecated(since = "0.16.0", note = "Use `ProgressDrawTarget::term` instead")]
    pub fn to_term(term: Term, refresh_rate: impl Into<Option<u64>>) -> ProgressDrawTarget {
        ProgressDrawTarget::term(term, refresh_rate)
    }

    /// Draw to a terminal, optionally with a specific refresh rate.
    ///
    /// Progress bars are by default drawn to terminals however if the
    /// terminal is not user attended the entire progress bar will be
    /// hidden.  This is done so that piping to a file will not produce
    /// useless escape codes in that file.
    ///
    /// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead.
    pub fn term(term: Term, refresh_rate: impl Into<Option<u64>>) -> ProgressDrawTarget {
        ProgressDrawTarget {
            kind: ProgressDrawTargetKind::Term {
                term,
                last_line_count: 0,
                leaky_bucket: refresh_rate.into().map(|rate| LeakyBucket {
                    bucket: MAX_GROUP_SIZE,
                    leak_rate: rate as f64,
                    last_update: Instant::now(),
                }),
            },
        }
    }

    /// A hidden draw target.
    ///
    /// This forces a progress bar to be not rendered at all.
    pub fn hidden() -> ProgressDrawTarget {
        ProgressDrawTarget {
            kind: ProgressDrawTargetKind::Hidden,
        }
    }

    /// Returns true if the draw target is hidden.
    ///
    /// This is internally used in progress bars to figure out if overhead
    /// from drawing can be prevented.
    pub fn is_hidden(&self) -> bool {
        match self.kind {
            ProgressDrawTargetKind::Hidden => true,
            ProgressDrawTargetKind::Term { ref term, .. } => !term.is_term(),
            _ => false,
        }
    }

    /// Returns the current width of the draw target.
    pub(crate) fn width(&self) -> usize {
        match self.kind {
            ProgressDrawTargetKind::Term { ref term, .. } => term.size().1 as usize,
            ProgressDrawTargetKind::Remote { ref state, .. } => state.read().unwrap().width(),
            ProgressDrawTargetKind::Hidden => 0,
        }
    }

    /// Apply the given draw state (draws it).
    pub(crate) fn apply_draw_state(&mut self, draw_state: ProgressDrawState) -> io::Result<()> {
        let (term, last_line_count) = match self.kind {
            ProgressDrawTargetKind::Term {
                ref term,
                ref mut last_line_count,
                leaky_bucket: None,
            } => (term, last_line_count),
            ProgressDrawTargetKind::Term {
                ref term,
                ref mut last_line_count,
                leaky_bucket: Some(ref mut leaky_bucket),
            } => {
                if draw_state.force_draw || leaky_bucket.try_add_work() {
                    (term, last_line_count)
                } else {
                    // rate limited
                    return Ok(());
                }
            }
            ProgressDrawTargetKind::Remote { idx, ref state, .. } => {
                return state
                    .write()
                    .unwrap()
                    .draw(idx, draw_state)
                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e));
            }
            // Hidden, finished, or no need to refresh yet
            _ => return Ok(()),
        };

        if !draw_state.lines.is_empty() && draw_state.move_cursor {
            term.move_cursor_up(*last_line_count)?;
        } else {
            clear_last_lines(term, *last_line_count)?;
        }

        let shift = match draw_state.alignment {
            MultiProgressAlignment::Bottom if draw_state.lines.len() < *last_line_count => {
                let shift = *last_line_count - draw_state.lines.len();
                for _ in 0..shift {
                    term.write_line("")?;
                }
                shift
            }
            _ => 0,
        };
        draw_state.draw_to_term(term)?;
        term.flush()?;
        *last_line_count = draw_state.lines.len() - draw_state.orphan_lines + shift;
        Ok(())
    }

    /// Properly disconnects from the draw target
    pub(crate) fn disconnect(&self) {
        match self.kind {
            ProgressDrawTargetKind::Term { .. } => {}
            ProgressDrawTargetKind::Remote { idx, ref state, .. } => {
                state
                    .write()
                    .unwrap()
                    .draw(
                        idx,
                        ProgressDrawState {
                            lines: vec![],
                            orphan_lines: 0,
                            force_draw: true,
                            move_cursor: false,
                            alignment: Default::default(),
                        },
                    )
                    .ok();
            }
            ProgressDrawTargetKind::Hidden => {}
        };
    }

    pub(crate) fn remote(&self) -> Option<(&Arc<RwLock<MultiProgressState>>, usize)> {
        match &self.kind {
            ProgressDrawTargetKind::Remote { state, idx } => Some((state, *idx)),
            _ => None,
        }
    }
}

#[derive(Debug)]
enum ProgressDrawTargetKind {
    Term {
        term: Term,
        last_line_count: usize,
        leaky_bucket: Option<LeakyBucket>,
    },
    Remote {
        state: Arc<RwLock<MultiProgressState>>,
        idx: usize,
    },
    Hidden,
}

#[derive(Debug)]
pub(crate) struct MultiProgressState {
    /// The collection of states corresponding to progress bars
    /// the state is None for bars that have not yet been drawn or have been removed
    pub(crate) draw_states: Vec<Option<ProgressDrawState>>,
    /// Set of removed bars, should have corresponding `None` elements in the `draw_states` vector
    pub(crate) free_set: Vec<usize>,
    /// Indices to the `draw_states` to maintain correct visual order
    pub(crate) ordering: Vec<usize>,
    /// Target for draw operation for MultiProgress
    pub(crate) draw_target: ProgressDrawTarget,
    /// Whether or not to just move cursor instead of clearing lines
    pub(crate) move_cursor: bool,
    /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top`
    pub(crate) alignment: MultiProgressAlignment,
}

impl MultiProgressState {
    fn width(&self) -> usize {
        self.draw_target.width()
    }

    pub(crate) fn draw(&mut self, idx: usize, draw_state: ProgressDrawState) -> io::Result<()> {
        let force_draw = draw_state.force_draw;
        let mut orphan_lines = vec![];

        // Split orphan lines out of the draw state, if any
        let lines = if draw_state.orphan_lines > 0 {
            let split = draw_state.lines.split_at(draw_state.orphan_lines);
            orphan_lines.extend_from_slice(split.0);
            split.1.to_vec()
        } else {
            draw_state.lines
        };

        let draw_state = ProgressDrawState {
            lines,
            orphan_lines: 0,
            ..draw_state
        };

        self.draw_states[idx] = Some(draw_state);

        // the rest from here is only drawing, we can skip it.
        if self.draw_target.is_hidden() {
            return Ok(());
        }

        let mut lines = vec![];

        // Make orphaned lines appear at the top, so they can be properly
        // forgotten.
        let orphan_lines_count = orphan_lines.len();
        lines.append(&mut orphan_lines);

        for index in self.ordering.iter() {
            let draw_state = &self.draw_states[*index];
            if let Some(ref draw_state) = draw_state {
                lines.extend_from_slice(&draw_state.lines[..]);
            }
        }

        self.draw_target.apply_draw_state(ProgressDrawState {
            lines,
            orphan_lines: orphan_lines_count,
            force_draw: force_draw || orphan_lines_count > 0,
            move_cursor: self.move_cursor,
            alignment: self.alignment,
        })
    }

    pub(crate) fn len(&self) -> usize {
        self.draw_states.len() - self.free_set.len()
    }

    pub(crate) fn remove_idx(&mut self, idx: usize) {
        if self.free_set.contains(&idx) {
            return;
        }

        self.draw_states[idx].take();
        self.free_set.push(idx);
        self.ordering.retain(|&x| x != idx);

        assert!(
            self.len() == self.ordering.len(),
            "Draw state is inconsistent"
        );
    }
}

#[derive(Debug)]
pub(crate) struct LeakyBucket {
    leak_rate: f64,
    last_update: Instant,
    bucket: f64,
}

/// Rate limit but allow occasional bursts above desired rate
impl LeakyBucket {
    /// try to add some work to the bucket
    /// return false if the bucket is already full and the work should be skipped
    fn try_add_work(&mut self) -> bool {
        self.leak();
        if self.bucket < MAX_GROUP_SIZE {
            self.bucket += 1.0;
            true
        } else {
            false
        }
    }

    fn leak(&mut self) {
        let ticks = self.last_update.elapsed().as_secs_f64() * self.leak_rate;
        self.bucket -= ticks;
        if self.bucket < 0.0 {
            self.bucket = 0.0;
        }
        self.last_update = Instant::now();
    }
}

const MAX_GROUP_SIZE: f64 = 32.0;

/// The drawn state of an element.
#[derive(Clone, Debug)]
pub(crate) struct ProgressDrawState {
    /// The lines to print (can contain ANSI codes)
    pub lines: Vec<String>,
    /// The number of lines that shouldn't be reaped by the next tick.
    pub orphan_lines: usize,
    /// True if drawing should be forced.
    pub force_draw: bool,
    /// True if we should move the cursor up when possible instead of clearing lines.
    pub move_cursor: bool,
    /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top`
    pub alignment: MultiProgressAlignment,
}

impl ProgressDrawState {
    pub(crate) fn new(lines: Vec<String>, force_draw: bool) -> Self {
        Self {
            lines,
            orphan_lines: 0,
            force_draw,
            move_cursor: false,
            alignment: Default::default(),
        }
    }

    pub fn draw_to_term(&self, term: &Term) -> io::Result<()> {
        let len = self.lines.len();
        for (idx, line) in self.lines.iter().enumerate() {
            if idx + 1 != len {
                term.write_line(line)?;
            } else {
                // Don't append a '\n' if this is the last line
                term.write_str(line)?;
                // Also append a ' ' to keep the original chars count
                // This is important if the line was meant to fill the entire width
                term.write_str(" ")?;
            }
        }
        Ok(())
    }
}

/// Vertical alignment of a multi progress.
///
/// The alignment controls how the multi progress is aligned if some of its progress bars get removed.
/// E.g. `Top` alignment (default), when _progress bar 2_ is removed:
/// ```ignore
/// [0/100] progress bar 1        [0/100] progress bar 1
/// [0/100] progress bar 2   =>   [0/100] progress bar 3
/// [0/100] progress bar 3
/// ```
///
/// `Bottom` alignment
/// ```ignore
/// [0/100] progress bar 1
/// [0/100] progress bar 2   =>   [0/100] progress bar 1
/// [0/100] progress bar 3        [0/100] progress bar 3
/// ```
#[derive(Debug, Copy, Clone)]
pub enum MultiProgressAlignment {
    Top,
    Bottom,
}

impl Default for MultiProgressAlignment {
    fn default() -> Self {
        Self::Top
    }
}

/// Fork of console::clear_last_lines that assumes that the last line doesn't contain a '\n'
fn clear_last_lines(term: &Term, n: usize) -> io::Result<()> {
    term.move_cursor_up(n.saturating_sub(1))?;
    for i in 0..n {
        term.clear_line()?;
        if i + 1 != n {
            term.move_cursor_down(1)?;
        }
    }
    term.move_cursor_up(n.saturating_sub(1))?;
    Ok(())
}
<|endoftext|>


<commit_before>use frame::SubFrame;

/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

/// Audio frame that contains one sample for each channel.
pub struct Frame {
  /// Information regarding the current audio frame.
  pub header: Header,
  pub subframes: [SubFrame; MAX_CHANNELS],
  /// CRC-16 of all frame bytes before this footer.
  pub footer: Footer,
}

/// Channel assignment order.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  /// Independent channels, from one up to eight.
  Independent,
  /// Left and side stereo.
  LeftSide,
  /// Right and side stereo.
  RightSide,
  /// Middle and side stereo.
  MiddleSide,
}

/// Numbering scheme used from the frame.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  /// Frame number of first sample in frame.
  Frame(u32),
  /// Sample number of first sample in frame.
  Sample(u64),
}

/// Information regarding the current audio frame.
#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  /// Number of samples per subframe.
  pub block_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels that also represent the number of subframes.
  pub channels: u8,
  /// Channel assignment order.
  pub channel_assignment: ChannelAssignment,
  /// Size, in bits, per sample.
  pub bits_per_sample: usize,
  /// Numbering scheme used from the frame.
  pub number: NumberType,
  /// CRC-8 of all header bytes before this crc.
  pub crc: u8,
}

/// End of the audio frame.
///
/// Contains a value that represents the CRC-16 of everything inside the
/// frame before the footer.
#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<commit_msg>Add documentation for `Subframe` inside of `Frame`<commit_after>use frame::SubFrame;

/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

/// Audio frame that contains one sample for each channel.
pub struct Frame {
  /// Information regarding the current audio frame.
  pub header: Header,
  /// Data for each audio channel.
  pub subframes: [SubFrame; MAX_CHANNELS],
  /// CRC-16 of all frame bytes before this footer.
  pub footer: Footer,
}

/// Channel assignment order.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  /// Independent channels, from one up to eight.
  Independent,
  /// Left and side stereo.
  LeftSide,
  /// Right and side stereo.
  RightSide,
  /// Middle and side stereo.
  MiddleSide,
}

/// Numbering scheme used from the frame.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  /// Frame number of first sample in frame.
  Frame(u32),
  /// Sample number of first sample in frame.
  Sample(u64),
}

/// Information regarding the current audio frame.
#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  /// Number of samples per subframe.
  pub block_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels that also represent the number of subframes.
  pub channels: u8,
  /// Channel assignment order.
  pub channel_assignment: ChannelAssignment,
  /// Size, in bits, per sample.
  pub bits_per_sample: usize,
  /// Numbering scheme used from the frame.
  pub number: NumberType,
  /// CRC-8 of all header bytes before this crc.
  pub crc: u8,
}

/// End of the audio frame.
///
/// Contains a value that represents the CRC-16 of everything inside the
/// frame before the footer.
#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<|endoftext|>