<commit_before>use std::ops::{Add, Sub};
use typenum::uint::{Unsigned, UInt};
use typenum::bit::Bit;
use typenum::consts::{U0, U1};
use typenum::{Cmp, Same, Greater};

/// This enum serves to represent the type of a tensor. A tensor can have any number of indices, and each one can be either
/// covariant (a lower index), or contravariant (an upper index). For example, a vector is a tensor with only one contravariant index.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum IndexType {
    Covariant,
    Contravariant,
}

/// Trait identifying a type as representing a tensor variance. It is implemented for
/// `CovariantIndex`, `ContravariantIndex` and tuples (Index, Variance).
pub trait Variance {
    type Rank: Unsigned + Add<U1>;
    fn rank() -> usize {
        Self::Rank::to_usize()
    }
    fn variance() -> Vec<IndexType>;
}

impl Variance for () {
    type Rank = U0;

    fn variance() -> Vec<IndexType> {
        vec![]
    }
}

/// Trait identifying a type as representing a tensor index. It is implemented
/// for `CovariantIndex` and `ContravariantIndex`.
pub trait TensorIndex: Variance {
    fn index_type() -> IndexType;
}

/// Type representing a contravariant (upper) tensor index.
pub struct ContravariantIndex;
impl TensorIndex for ContravariantIndex {
    fn index_type() -> IndexType {
        IndexType::Contravariant
    }
}

/// Type representing a covariant (lower) tensor index.
pub struct CovariantIndex;
impl TensorIndex for CovariantIndex {
    fn index_type() -> IndexType {
        IndexType::Covariant
    }
}

impl Variance for ContravariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Contravariant]
    }
}

impl Variance for CovariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Covariant]
    }
}

/// Trait representing the other index type
pub trait OtherIndex: TensorIndex {
    type Output: TensorIndex;
}

impl OtherIndex for CovariantIndex {
    type Output = ContravariantIndex;
}

impl OtherIndex for ContravariantIndex {
    type Output = CovariantIndex;
}

// Back to implementing Variance

impl<T, U> Variance for (T, U)
    where U: Variance,
          <U::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          T: TensorIndex
{
    type Rank = <U::Rank as Add<U1>>::Output;

    fn variance() -> Vec<IndexType> {
        let mut result = vec![T::index_type()];
        result.append(&mut U::variance());
        result
    }
}

/// Operator trait used for concatenating two variances.
pub trait Concat<T: Variance>: Variance {
    type Output: Variance;
}

impl<T, U> Concat<U> for T
    where T: TensorIndex,
          U: TensorIndex,
          <<U as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = (T, U);
}

impl<T, U, V> Concat<V> for (T, U)
    where T: TensorIndex,
          V: TensorIndex,
          U: Variance + Concat<V>,
          <U as Concat<V>>::Output: Variance,
          <<U as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          <<<U as Concat<V>>::Output as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = (T, <U as Concat<V>>::Output);
}

impl<T, U, V> Concat<(U, V)> for T
    where T: TensorIndex,
          U: TensorIndex,
          V: Variance,
          <<U as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          <<V as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          <<<V as Variance>::Rank as Add<U1>>::Output as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = (T, (U, V));
}

impl<T, U, V, W> Concat<(V, W)> for (T, U)
    where T: TensorIndex,
          U: Variance + Concat<(V, W)>,
          V: TensorIndex,
          W: Variance,
          <<U as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          <<W as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          <<<U as Concat<(V, W)>>::Output as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = (T, <U as Concat<(V, W)>>::Output);
}

/// Indexing operator trait: Output is equal to the index type at the given position
///
/// Warning: Indices are numbered starting from 0!
pub trait Index<T: Unsigned>: Variance {
    type Output: TensorIndex;
}

impl Index<U0> for CovariantIndex {
    type Output = CovariantIndex;
}

impl Index<U0> for ContravariantIndex {
    type Output = ContravariantIndex;
}

impl<T, V, U, B> Index<UInt<U, B>> for (V, T)
    where V: TensorIndex,
          U: Unsigned,
          B: Bit,
          UInt<U, B>: Sub<U1>,
          <UInt<U, B> as Sub<U1>>::Output: Unsigned,
          T: Variance + Index<<UInt<U, B> as Sub<U1>>::Output>,
          <<T as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = <T as Index<<UInt<U, B> as Sub<U1>>::Output>>::Output;
}

impl<T, V> Index<U0> for (V, T)
    where V: TensorIndex,
          T: Variance,
          <<T as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = V;
}

/// An operator trait, removing the indicated index from a variance
pub trait RemoveIndex<T: Unsigned>: Variance {
    type Output: Variance;
}

impl RemoveIndex<U0> for CovariantIndex {
    type Output = ();
}

impl RemoveIndex<U0> for ContravariantIndex {
    type Output = ();
}

impl<U, V> RemoveIndex<U0> for (U, V)
    where U: TensorIndex,
          V: Variance,
          <<V as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = V;
}

impl<T, B, U, V> RemoveIndex<UInt<T, B>> for (U, V)
    where T: Unsigned,
          B: Bit,
          U: TensorIndex,
          UInt<T, B>: Sub<U1>,
          <UInt<T, B> as Sub<U1>>::Output: Unsigned,
          V: Variance + RemoveIndex<<UInt<T, B> as Sub<U1>>::Output>,
          <<V as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          <<<V as RemoveIndex<<UInt<T, B> as Sub<U1>>::Output>>::Output as Variance>::Rank as Add<U1>>::Output: Unsigned + Add<U1>
{
    type Output = (U, <V as RemoveIndex<<UInt<T, B> as Sub<U1>>::Output>>::Output);
}

/// An operator trait representing tensor contraction
pub trait Contract<Ul: Unsigned, Uh: Unsigned>: Variance {
    type Output: Variance;
}

// this is quite possibly the worst impl I have ever written
impl<Ul, Uh, V> Contract<Ul, Uh> for V
    where Ul: Unsigned,
          Uh: Unsigned + Sub<U1> + Cmp<Ul>,
          <Uh as Sub<U1>>::Output: Unsigned,
          <Uh as Cmp<Ul>>::Output: Same<Greater>,
          V: Index<Ul> + Index<Uh> + RemoveIndex<Ul>,
          <V as Index<Ul>>::Output: OtherIndex,
          <V as Index<Uh>>::Output: TensorIndex + Same<<<V as Index<Ul>>::Output as OtherIndex>::Output>,
          <V as RemoveIndex<Ul>>::Output: RemoveIndex<<Uh as Sub<U1>>::Output>,
          <<V as RemoveIndex<Ul>>::Output as RemoveIndex<<Uh as Sub<U1>>::Output>>::Output: Variance
{
    type Output = <<V as RemoveIndex<Ul>>::Output as RemoveIndex<<Uh as Sub<U1>>::Output>>::Output;
}

#[cfg(test)]
mod test {
    use super::*;
    use typenum::consts::{U0, U1, U2};

    #[test]
    fn test_variance() {
        assert_eq!(<(CovariantIndex, ContravariantIndex) as Variance>::variance(), vec![IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_variance_concat() {
        assert_eq!(<<CovariantIndex as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(CovariantIndex, CovariantIndex) as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<CovariantIndex as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(ContravariantIndex, CovariantIndex) as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_index() {
        assert_eq!(<CovariantIndex as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U1>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U0>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U2>>::Output::index_type(),
            IndexType::Covariant);
    }

    #[test]
    fn test_remove() {
        assert_eq!(<CovariantIndex as RemoveIndex<U0>>::Output::variance(),
            vec![]);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as RemoveIndex<U0>>::Output::variance(),
            vec![IndexType::Contravariant]);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as RemoveIndex<U1>>::Output::variance(),
            vec![IndexType::Covariant]);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as RemoveIndex<U1>>::Output::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant]);
    }

    #[test]
    fn test_contract() {
        assert_eq!(<(CovariantIndex, ContravariantIndex) as Contract<U0, U1>>::Output::variance(),
            vec![]);

        assert_eq!(<(ContravariantIndex, CovariantIndex) as Contract<U0, U1>>::Output::variance(),
            vec![]);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Contract<U0, U1>>::Output::variance(),
            vec![IndexType::Covariant]);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Contract<U0, U2>>::Output::variance(),
            vec![IndexType::Covariant]);
    }
}<commit_msg>Added some type aliases, hopefully increasing readability<commit_after>use std::ops::{Add, Sub};
use typenum::uint::{Unsigned, UInt};
use typenum::bit::Bit;
use typenum::consts::{U0, U1};
use typenum::{Cmp, Same, Greater};

type Add1<T> = <T as Add<U1>>::Output;
type Sub1<T> = <T as Sub<U1>>::Output;

/// This enum serves to represent the type of a tensor. A tensor can have any number of indices, and each one can be either
/// covariant (a lower index), or contravariant (an upper index). For example, a vector is a tensor with only one contravariant index.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum IndexType {
    Covariant,
    Contravariant,
}

/// Trait identifying a type as representing a tensor variance. It is implemented for
/// `CovariantIndex`, `ContravariantIndex` and tuples (Index, Variance).
pub trait Variance {
    type Rank: Unsigned + Add<U1>;
    fn rank() -> usize {
        Self::Rank::to_usize()
    }
    fn variance() -> Vec<IndexType>;
}

impl Variance for () {
    type Rank = U0;

    fn variance() -> Vec<IndexType> {
        vec![]
    }
}

/// Trait identifying a type as representing a tensor index. It is implemented
/// for `CovariantIndex` and `ContravariantIndex`.
pub trait TensorIndex: Variance {
    fn index_type() -> IndexType;
}

/// Type representing a contravariant (upper) tensor index.
pub struct ContravariantIndex;
impl TensorIndex for ContravariantIndex {
    fn index_type() -> IndexType {
        IndexType::Contravariant
    }
}

/// Type representing a covariant (lower) tensor index.
pub struct CovariantIndex;
impl TensorIndex for CovariantIndex {
    fn index_type() -> IndexType {
        IndexType::Covariant
    }
}

impl Variance for ContravariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Contravariant]
    }
}

impl Variance for CovariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Covariant]
    }
}

/// Trait representing the other index type
pub trait OtherIndex: TensorIndex {
    type Output: TensorIndex;
}

impl OtherIndex for CovariantIndex {
    type Output = ContravariantIndex;
}

impl OtherIndex for ContravariantIndex {
    type Output = CovariantIndex;
}

// Back to implementing Variance

impl<T, U> Variance for (T, U)
    where U: Variance,
          Add1<U::Rank>: Unsigned + Add<U1>,
          T: TensorIndex
{
    type Rank = Add1<U::Rank>;

    fn variance() -> Vec<IndexType> {
        let mut result = vec![T::index_type()];
        result.append(&mut U::variance());
        result
    }
}

/// Operator trait used for concatenating two variances.
pub trait Concat<T: Variance>: Variance {
    type Output: Variance;
}

pub type Joined<T, U> = <T as Concat<U>>::Output;

impl<T, U> Concat<U> for T
    where T: TensorIndex,
          U: TensorIndex,
          Add1<<U as Variance>::Rank>: Unsigned + Add<U1>
{
    type Output = (T, U);
}

impl<T, U, V> Concat<V> for (T, U)
    where T: TensorIndex,
          V: TensorIndex,
          U: Variance + Concat<V>,
          <U as Concat<V>>::Output: Variance,
          Add1<<U as Variance>::Rank>: Unsigned + Add<U1>,
          Add1<<Joined<U, V> as Variance>::Rank>: Unsigned + Add<U1>
{
    type Output = (T, <U as Concat<V>>::Output);
}

impl<T, U, V> Concat<(U, V)> for T
    where T: TensorIndex,
          U: TensorIndex,
          V: Variance,
          Add1<<U as Variance>::Rank>: Unsigned + Add<U1>,
          Add1<<V as Variance>::Rank>: Unsigned + Add<U1>,
          Add1<Add1<<V as Variance>::Rank>>: Unsigned + Add<U1>
{
    type Output = (T, (U, V));
}

impl<T, U, V, W> Concat<(V, W)> for (T, U)
    where T: TensorIndex,
          U: Variance + Concat<(V, W)>,
          V: TensorIndex,
          W: Variance,
          Add1<<U as Variance>::Rank>: Unsigned + Add<U1>,
          Add1<<W as Variance>::Rank>: Unsigned + Add<U1>,
          Add1<<Joined<U, (V, W)> as Variance>::Rank>: Unsigned + Add<U1>
{
    type Output = (T, Joined<U, (V, W)>);
}

/// Indexing operator trait: Output is equal to the index type at the given position
///
/// Warning: Indices are numbered starting from 0!
pub trait Index<T: Unsigned>: Variance {
    type Output: TensorIndex;
}

pub type At<T, U> = <T as Index<U>>::Output;

impl Index<U0> for CovariantIndex {
    type Output = CovariantIndex;
}

impl Index<U0> for ContravariantIndex {
    type Output = ContravariantIndex;
}

impl<T, V, U, B> Index<UInt<U, B>> for (V, T)
    where V: TensorIndex,
          U: Unsigned,
          B: Bit,
          UInt<U, B>: Sub<U1>,
          Sub1<UInt<U, B>>: Unsigned,
          T: Variance + Index<Sub1<UInt<U, B>>>,
          Add1<<T as Variance>::Rank>: Unsigned + Add<U1>
{
    type Output = At<T, Sub1<UInt<U, B>>>;
}

impl<T, V> Index<U0> for (V, T)
    where V: TensorIndex,
          T: Variance,
          Add1<<T as Variance>::Rank>: Unsigned + Add<U1>
{
    type Output = V;
}

/// An operator trait, removing the indicated index from a variance
pub trait RemoveIndex<T: Unsigned>: Variance {
    type Output: Variance;
}

pub type Removed<T, U> = <T as RemoveIndex<U>>::Output;

impl RemoveIndex<U0> for CovariantIndex {
    type Output = ();
}

impl RemoveIndex<U0> for ContravariantIndex {
    type Output = ();
}

impl<U, V> RemoveIndex<U0> for (U, V)
    where U: TensorIndex,
          V: Variance,
          Add1<<V as Variance>::Rank>: Unsigned + Add<U1>
{
    type Output = V;
}

impl<T, B, U, V> RemoveIndex<UInt<T, B>> for (U, V)
    where T: Unsigned,
          B: Bit,
          U: TensorIndex,
          UInt<T, B>: Sub<U1>,
          Sub1<UInt<T, B>>: Unsigned,
          V: Variance + RemoveIndex<Sub1<UInt<T, B>>>,
          Add1<<V as Variance>::Rank>: Unsigned + Add<U1>,
          Add1<<Removed<V, Sub1<UInt<T, B>>> as Variance>::Rank>: Unsigned + Add<U1>
{
    type Output = (U, Removed<V, Sub1<UInt<T, B>>>);
}

/// An operator trait representing tensor contraction
pub trait Contract<Ul: Unsigned, Uh: Unsigned>: Variance {
    type Output: Variance;
}

pub type Contracted<V, Ul, Uh> = <V as Contract<Ul, Uh>>::Output;

// this is quite possibly the worst impl I have ever written
impl<Ul, Uh, V> Contract<Ul, Uh> for V
    where Ul: Unsigned,
          Uh: Unsigned + Sub<U1> + Cmp<Ul>,
          Sub1<Uh>: Unsigned,
          <Uh as Cmp<Ul>>::Output: Same<Greater>,
          V: Index<Ul> + Index<Uh> + RemoveIndex<Ul>,
          At<V, Ul>: OtherIndex,
          At<V, Uh>: Same<<At<V, Ul> as OtherIndex>::Output>,
          Removed<V, Ul>: RemoveIndex<Sub1<Uh>>,
          Removed<Removed<V, Ul>, Sub1<Uh>>: Variance
{
    type Output = Removed<Removed<V, Ul>, Sub1<Uh>>;
}

#[cfg(test)]
mod test {
    use super::*;
    use typenum::consts::{U0, U1, U2};

    #[test]
    fn test_variance() {
        assert_eq!(<(CovariantIndex, ContravariantIndex) as Variance>::variance(), vec![IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_variance_concat() {
        assert_eq!(<Joined<CovariantIndex, ContravariantIndex> as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<Joined<(CovariantIndex, CovariantIndex), ContravariantIndex> as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<Joined<CovariantIndex, (CovariantIndex, ContravariantIndex)> as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<Joined<(ContravariantIndex, CovariantIndex), (CovariantIndex, ContravariantIndex)> as Variance>::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_index() {
        assert_eq!(<At<CovariantIndex, U0> as TensorIndex>::index_type(),
            IndexType::Covariant);

        assert_eq!(<At<(CovariantIndex, ContravariantIndex), U0> as TensorIndex>::index_type(),
            IndexType::Covariant);

        assert_eq!(<At<(CovariantIndex, ContravariantIndex), U1> as TensorIndex>::index_type(),
            IndexType::Contravariant);

        assert_eq!(<At<(ContravariantIndex, (CovariantIndex, CovariantIndex)), U0> as TensorIndex>::index_type(),
            IndexType::Contravariant);

        assert_eq!(<At<(ContravariantIndex, (CovariantIndex, CovariantIndex)), U2> as TensorIndex>::index_type(),
            IndexType::Covariant);
    }

    #[test]
    fn test_remove() {
        assert_eq!(<Removed<CovariantIndex, U0> as Variance>::variance(),
            vec![]);

        assert_eq!(<Removed<(CovariantIndex, ContravariantIndex), U0> as Variance>::variance(),
            vec![IndexType::Contravariant]);

        assert_eq!(<Removed<(CovariantIndex, ContravariantIndex), U1> as Variance>::variance(),
            vec![IndexType::Covariant]);

        assert_eq!(<Removed<(ContravariantIndex, (CovariantIndex, CovariantIndex)), U1> as Variance>::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant]);
    }

    #[test]
    fn test_contract() {
        assert_eq!(<Contracted<(CovariantIndex, ContravariantIndex), U0, U1> as Variance>::variance(),
            vec![]);

        assert_eq!(<Contracted<(ContravariantIndex, CovariantIndex), U0, U1> as Variance>::variance(),
            vec![]);

        assert_eq!(<Contracted<(ContravariantIndex, (CovariantIndex, CovariantIndex)), U0, U1> as Variance>::variance(),
            vec![IndexType::Covariant]);

        assert_eq!(<Contracted<(ContravariantIndex, (CovariantIndex, CovariantIndex)), U0, U2> as Variance>::variance(),
            vec![IndexType::Covariant]);
    }
}<|endoftext|>


<commit_before>extern crate orbfont;
extern crate ransid;

use self::orbfont::Font;

use std::mem;

use orbclient::{Color, Event, EventOption, Window};
use orbclient::event;

pub struct Console {
    pub window: Window,
    pub inner: ransid::Console,
    pub font: Font,
    pub font_bold: Font,
    pub focused: bool,
    pub command: String,
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            window: window,
            inner: ransid::Console::new(width as usize/8, height as usize/16),
            font: Font::find(Some("Mono"), None, None).unwrap(),
            font_bold: Font::find(Some("Mono"), None, Some("Bold")).unwrap(),
            focused: true,
            command: String::new(),
        }
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.inner.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x7F"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            return Some(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.inner.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.inner.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

                                    if c == '\n' {
                                        let mut command = String::new();
                                        mem::swap(&mut self.command, &mut command);
                                        return Some(command);
                                    }
                                }
                            },
                        }
                    }
                }
            },
            EventOption::Focus(focus_event) => if self.focused != focus_event.focused {
                self.focused = focus_event.focused;
                self.inner.redraw = true;
                self.write(&[]);
            },
            _ => (),
        }

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        self.inner.write(bytes);
        if self.inner.redraw {
            self.inner.redraw = false;

            self.window.set(Color {
                data: self.inner.background.data
            });
            for y in 0..self.inner.h {
                for x in 0..self.inner.w {
                    let block = self.inner.display[y * self.inner.w + x];
                    self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color {
                        data: block.bg.data
                    });
                    if block.c != ' ' {
                        if block.bold {
                            self.font_bold.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                data: block.fg.data
                            });
                        } else {
                            self.font.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                data: block.fg.data
                            });
                        }
                    }
                    if block.underlined {
                        self.window.rect(x as i32 * 8, y as i32 * 16 + 14, 8, 1, Color {
                            data: block.fg.data
                        });
                    }
                }
            }
            if self.inner.cursor {
                self.window.rect(self.inner.x as i32 * 8, self.inner.y as i32 * 16, 8, 16, Color {
                    data: self.inner.foreground.data
                });
                if ! self.focused {
                    self.window.rect(self.inner.x as i32 * 8 + 1, self.inner.y as i32 * 16 + 1, 6, 14, Color {
                        data: self.inner.background.data
                    });
                }
            }
            self.window.sync();
        }
    }
}
<commit_msg>Only render what has changed<commit_after>extern crate orbfont;
extern crate ransid;

use self::orbfont::Font;

use std::mem;

use orbclient::{Color, Event, EventOption, Window};
use orbclient::event;

pub struct Console {
    pub window: Window,
    pub inner: ransid::Console,
    pub font: Font,
    pub font_bold: Font,
    pub focused: bool,
    pub command: String,
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            window: window,
            inner: ransid::Console::new(width as usize/8, height as usize/16),
            font: Font::find(Some("Mono"), None, None).unwrap(),
            font_bold: Font::find(Some("Mono"), None, Some("Bold")).unwrap(),
            focused: true,
            command: String::new(),
        }
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.inner.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x7F"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            return Some(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.inner.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.inner.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

                                    if c == '\n' {
                                        let mut command = String::new();
                                        mem::swap(&mut self.command, &mut command);
                                        return Some(command);
                                    }
                                }
                            },
                        }
                    }
                }
            },
            EventOption::Focus(focus_event) => if self.focused != focus_event.focused {
                self.focused = focus_event.focused;
                self.inner.redraw = true;
                self.write(&[]);
            },
            _ => (),
        }

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        self.inner.write(bytes);
        if self.inner.redraw {
            self.inner.redraw = false;

            for y in 0..self.inner.h {
                if self.inner.changed[y] {
                    self.inner.changed[y] = false;

                    self.window.rect(0, y as i32 * 16, self.inner.h as u32 * 8, 16, Color {
                        data: self.inner.background.data
                    });
                    for x in 0..self.inner.w {
                        let block = self.inner.display[y * self.inner.w + x];
                        let (bg, fg) = if self.inner.cursor && self.inner.y == y && self.inner.x == x {
                            (block.fg.data, block.bg.data)
                        }else{
                            (block.bg.data, block.fg.data)
                        };
                        self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color {
                            data: bg
                        });
                        if block.c != ' ' {
                            if block.bold {
                                self.font_bold.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                    data: fg
                                });
                            } else {
                                self.font.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                    data: fg
                                });
                            }
                        }
                        if block.underlined {
                            self.window.rect(x as i32 * 8, y as i32 * 16 + 14, 8, 1, Color {
                                data: fg
                            });
                        }
                    }
                }
            }
            self.window.sync();
        }
    }
}
<|endoftext|>


<commit_before>use super::{plain_text_summary, short_markdown_summary};
use super::{ErrorCodes, IdMap, Ignore, LangString, Markdown, MarkdownHtml};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use std::cell::RefCell;

#[test]
fn test_unique_id() {
    let input = [
        "foo",
        "examples",
        "examples",
        "method.into_iter",
        "examples",
        "method.into_iter",
        "foo",
        "main",
        "search",
        "methods",
        "examples",
        "method.into_iter",
        "assoc_type.Item",
        "assoc_type.Item",
    ];
    let expected = [
        "foo",
        "examples",
        "examples-1",
        "method.into_iter",
        "examples-2",
        "method.into_iter-1",
        "foo-1",
        "main-1",
        "search-1",
        "methods",
        "examples-3",
        "method.into_iter-2",
        "assoc_type.Item",
        "assoc_type.Item-1",
    ];

    let map = RefCell::new(IdMap::new());
    let test = || {
        let mut map = map.borrow_mut();
        let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
        assert_eq!(&actual[..], expected);
    };
    test();
    map.borrow_mut().reset();
    test();
}

#[test]
fn test_lang_string_parse() {
    fn t(
        s: &str,
        should_panic: bool,
        no_run: bool,
        ignore: Ignore,
        rust: bool,
        test_harness: bool,
        compile_fail: bool,
        allow_fail: bool,
        error_codes: Vec<String>,
        edition: Option<Edition>,
    ) {
        assert_eq!(
            LangString::parse(s, ErrorCodes::Yes, true, None),
            LangString {
                should_panic,
                no_run,
                ignore,
                rust,
                test_harness,
                compile_fail,
                error_codes,
                original: s.to_owned(),
                allow_fail,
                edition,
            }
        )
    }
    let ignore_foo = Ignore::Some(vec!["foo".to_string()]);

    fn v() -> Vec<String> {
        Vec::new()
    }

    // marker                | should_panic | no_run | ignore | rust | test_harness
    //                       | compile_fail | allow_fail | error_codes | edition
    t("", false, false, Ignore::None, true, false, false, false, v(), None);
    t("rust", false, false, Ignore::None, true, false, false, false, v(), None);
    t("sh", false, false, Ignore::None, false, false, false, false, v(), None);
    t("ignore", false, false, Ignore::All, true, false, false, false, v(), None);
    t("ignore-foo", false, false, ignore_foo, true, false, false, false, v(), None);
    t("should_panic", true, false, Ignore::None, true, false, false, false, v(), None);
    t("no_run", false, true, Ignore::None, true, false, false, false, v(), None);
    t("test_harness", false, false, Ignore::None, true, true, false, false, v(), None);
    t("compile_fail", false, true, Ignore::None, true, false, true, false, v(), None);
    t("allow_fail", false, false, Ignore::None, true, false, false, true, v(), None);
    t("{.no_run .example}", false, true, Ignore::None, true, false, false, false, v(), None);
    t("{.sh .should_panic}", true, false, Ignore::None, false, false, false, false, v(), None);
    t("{.example .rust}", false, false, Ignore::None, true, false, false, false, v(), None);
    t("{.test_harness .rust}", false, false, Ignore::None, true, true, false, false, v(), None);
    t("text, no_run", false, true, Ignore::None, false, false, false, false, v(), None);
    t("text,no_run", false, true, Ignore::None, false, false, false, false, v(), None);
    t(
        "edition2015",
        false,
        false,
        Ignore::None,
        true,
        false,
        false,
        false,
        v(),
        Some(Edition::Edition2015),
    );
    t(
        "edition2018",
        false,
        false,
        Ignore::None,
        true,
        false,
        false,
        false,
        v(),
        Some(Edition::Edition2018),
    );
}

#[test]
fn test_header() {
    fn t(input: &str, expect: &str) {
        let mut map = IdMap::new();
        let output =
            Markdown(input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        "# Foo bar",
        "
Foo bar
",
    );
    t(
        "## Foo-bar_baz qux",
        "
\
         Foo-bar_baz qux
",
    );
    t(
        "### **Foo** *bar* baz!?!& -_qux_-%",
        "
\
            Foo \
            bar baz!?!& -qux-%\

",
    );
    t(
        "#### **Foo?** & \\*bar?!*  _`baz`_ ❤ #qux",
        "
\
             Foo? & *bar?!*  \
             baz ❤ #qux\

",
    );
}

#[test]
fn test_header_ids_multiple_blocks() {
    let mut map = IdMap::new();
    fn t(map: &mut IdMap, input: &str, expect: &str) {
        let output =
            Markdown(input, &[], map, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        &mut map,
        "# Example",
        "
Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
Panics
",
    );
    t(
        &mut map,
        "# Example",
        "
Example
",
    );
    t(
        &mut map,
        "# Main",
        "
Main
",
    );
    t(
        &mut map,
        "# Example",
        "
Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
Panics
",
    );
}

#[test]
fn test_short_markdown_summary() {
    fn t(input: &str, expect: &str) {
        let output = short_markdown_summary(input);
        assert_eq!(output, expect, "original: {}", input);
    }

    t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
    t("*italic*", "italic");
    t("**bold**", "bold");
    t("Multi-line\nsummary", "Multi-line summary");
    t("Hard-break  \nsummary", "Hard-break summary");
    t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)");
    t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
    t("code `let x = i32;` ...", "code let x = i32; ...");
    t("type `Type<'static>` ...", "type Type<'static> ...");
    t("# top header", "top header");
    t("## header", "header");
    t("first paragraph\n\nsecond paragraph", "first paragraph");
    t("```\nfn main() {}\n```", "");
    t("
hello
", "");
    t(
        "a *very*, **very** long first paragraph. it has lots of `inline code: Vec<T>`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break!  \nthat was a hard one\n\nsecond paragraph.",
        "a very, very long first paragraph. it has lots of …",
    );
}

#[test]
fn test_plain_text_summary() {
    fn t(input: &str, expect: &str) {
        let output = plain_text_summary(input);
        assert_eq!(output, expect, "original: {}", input);
    }

    t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
    t("**bold**", "bold");
    t("Multi-line\nsummary", "Multi-line summary");
    t("Hard-break  \nsummary", "Hard-break summary");
    t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)");
    t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
    t("code `let x = i32;` ...", "code `let x = i32;` ...");
    t("type `Type<'static>` ...", "type `Type<'static>` ...");
    t("# top header", "top header");
    t("## header", "header");
    t("first paragraph\n\nsecond paragraph", "first paragraph");
    t("```\nfn main() {}\n```", "");
    t("
hello
", "");
    t(
        "a *very*, **very** long first paragraph. it has lots of `inline code: Vec<T>`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break!  \nthat was a hard one\n\nsecond paragraph.",
        "a very, very long first paragraph. it has lots of `inline code: Vec<T>`. and it has a link. that was a soft line break! that was a hard one",
    );
}

#[test]
fn test_markdown_html_escape() {
    fn t(input: &str, expect: &str) {
        let mut idmap = IdMap::new();
        let output =
            MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t("`Struct<'a, T>`", "
Struct<'a, T>
\n");
    t("Struct<'a, T>", "
Struct<'a, T>
\n");
    t("Struct
", "
Struct<br>
\n");
}
<commit_msg>Rollup merge of #80013 - poliorcetics:rustdoc-test-refactor, r=jyn514<commit_after>use super::{plain_text_summary, short_markdown_summary};
use super::{ErrorCodes, IdMap, Ignore, LangString, Markdown, MarkdownHtml};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use std::cell::RefCell;

#[test]
fn test_unique_id() {
    let input = [
        "foo",
        "examples",
        "examples",
        "method.into_iter",
        "examples",
        "method.into_iter",
        "foo",
        "main",
        "search",
        "methods",
        "examples",
        "method.into_iter",
        "assoc_type.Item",
        "assoc_type.Item",
    ];
    let expected = [
        "foo",
        "examples",
        "examples-1",
        "method.into_iter",
        "examples-2",
        "method.into_iter-1",
        "foo-1",
        "main-1",
        "search-1",
        "methods",
        "examples-3",
        "method.into_iter-2",
        "assoc_type.Item",
        "assoc_type.Item-1",
    ];

    let map = RefCell::new(IdMap::new());
    let test = || {
        let mut map = map.borrow_mut();
        let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
        assert_eq!(&actual[..], expected);
    };
    test();
    map.borrow_mut().reset();
    test();
}

#[test]
fn test_lang_string_parse() {
    fn t(lg: LangString) {
        let s = &lg.original;
        assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg)
    }

    t(LangString::all_false());
    t(LangString { original: "rust".into(), ..LangString::all_false() });
    t(LangString { original: "sh".into(), rust: false, ..LangString::all_false() });
    t(LangString { original: "ignore".into(), ignore: Ignore::All, ..LangString::all_false() });
    t(LangString {
        original: "ignore-foo".into(),
        ignore: Ignore::Some(vec!["foo".to_string()]),
        ..LangString::all_false()
    });
    t(LangString {
        original: "should_panic".into(),
        should_panic: true,
        ..LangString::all_false()
    });
    t(LangString { original: "no_run".into(), no_run: true, ..LangString::all_false() });
    t(LangString {
        original: "test_harness".into(),
        test_harness: true,
        ..LangString::all_false()
    });
    t(LangString {
        original: "compile_fail".into(),
        no_run: true,
        compile_fail: true,
        ..LangString::all_false()
    });
    t(LangString { original: "allow_fail".into(), allow_fail: true, ..LangString::all_false() });
    t(LangString {
        original: "{.no_run .example}".into(),
        no_run: true,
        ..LangString::all_false()
    });
    t(LangString {
        original: "{.sh .should_panic}".into(),
        should_panic: true,
        rust: false,
        ..LangString::all_false()
    });
    t(LangString { original: "{.example .rust}".into(), ..LangString::all_false() });
    t(LangString {
        original: "{.test_harness .rust}".into(),
        test_harness: true,
        ..LangString::all_false()
    });
    t(LangString {
        original: "text, no_run".into(),
        no_run: true,
        rust: false,
        ..LangString::all_false()
    });
    t(LangString {
        original: "text,no_run".into(),
        no_run: true,
        rust: false,
        ..LangString::all_false()
    });
    t(LangString {
        original: "edition2015".into(),
        edition: Some(Edition::Edition2015),
        ..LangString::all_false()
    });
    t(LangString {
        original: "edition2018".into(),
        edition: Some(Edition::Edition2018),
        ..LangString::all_false()
    });
}

#[test]
fn test_header() {
    fn t(input: &str, expect: &str) {
        let mut map = IdMap::new();
        let output =
            Markdown(input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        "# Foo bar",
        "
Foo bar
",
    );
    t(
        "## Foo-bar_baz qux",
        "
\
         Foo-bar_baz qux
",
    );
    t(
        "### **Foo** *bar* baz!?!& -_qux_-%",
        "
\
            Foo \
            bar baz!?!& -qux-%\

",
    );
    t(
        "#### **Foo?** & \\*bar?!*  _`baz`_ ❤ #qux",
        "
\
             Foo? & *bar?!*  \
             baz ❤ #qux\

",
    );
}

#[test]
fn test_header_ids_multiple_blocks() {
    let mut map = IdMap::new();
    fn t(map: &mut IdMap, input: &str, expect: &str) {
        let output =
            Markdown(input, &[], map, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t(
        &mut map,
        "# Example",
        "
Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
Panics
",
    );
    t(
        &mut map,
        "# Example",
        "
Example
",
    );
    t(
        &mut map,
        "# Main",
        "
Main
",
    );
    t(
        &mut map,
        "# Example",
        "
Example
",
    );
    t(
        &mut map,
        "# Panics",
        "
Panics
",
    );
}

#[test]
fn test_short_markdown_summary() {
    fn t(input: &str, expect: &str) {
        let output = short_markdown_summary(input);
        assert_eq!(output, expect, "original: {}", input);
    }

    t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
    t("*italic*", "italic");
    t("**bold**", "bold");
    t("Multi-line\nsummary", "Multi-line summary");
    t("Hard-break  \nsummary", "Hard-break summary");
    t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)");
    t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
    t("code `let x = i32;` ...", "code let x = i32; ...");
    t("type `Type<'static>` ...", "type Type<'static> ...");
    t("# top header", "top header");
    t("## header", "header");
    t("first paragraph\n\nsecond paragraph", "first paragraph");
    t("```\nfn main() {}\n```", "");
    t("
hello
", "");
    t(
        "a *very*, **very** long first paragraph. it has lots of `inline code: Vec<T>`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break!  \nthat was a hard one\n\nsecond paragraph.",
        "a very, very long first paragraph. it has lots of …",
    );
}

#[test]
fn test_plain_text_summary() {
    fn t(input: &str, expect: &str) {
        let output = plain_text_summary(input);
        assert_eq!(output, expect, "original: {}", input);
    }

    t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
    t("**bold**", "bold");
    t("Multi-line\nsummary", "Multi-line summary");
    t("Hard-break  \nsummary", "Hard-break summary");
    t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)");
    t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
    t("code `let x = i32;` ...", "code `let x = i32;` ...");
    t("type `Type<'static>` ...", "type `Type<'static>` ...");
    t("# top header", "top header");
    t("## header", "header");
    t("first paragraph\n\nsecond paragraph", "first paragraph");
    t("```\nfn main() {}\n```", "");
    t("
hello
", "");
    t(
        "a *very*, **very** long first paragraph. it has lots of `inline code: Vec<T>`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break!  \nthat was a hard one\n\nsecond paragraph.",
        "a very, very long first paragraph. it has lots of `inline code: Vec<T>`. and it has a link. that was a soft line break! that was a hard one",
    );
}

#[test]
fn test_markdown_html_escape() {
    fn t(input: &str, expect: &str) {
        let mut idmap = IdMap::new();
        let output =
            MarkdownHtml(input, &mut idmap, ErrorCodes::Yes, DEFAULT_EDITION, &None).into_string();
        assert_eq!(output, expect, "original: {}", input);
    }

    t("`Struct<'a, T>`", "
Struct<'a, T>
\n");
    t("Struct<'a, T>", "
Struct<'a, T>
\n");
    t("Struct
", "
Struct<br>
\n");
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Anthony Deschamps <anthony.j.deschamps@gmail.com>
// (c) Sylvestre Ledru <sylvestre@debian.org>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes

#[macro_use]
extern crate uucore;

use chrono::{DateTime, FixedOffset, Local, Offset, Utc};
#[cfg(windows)]
use chrono::{Datelike, Timelike};
use clap::{crate_version, App, Arg};
#[cfg(all(unix, not(target_os = "macos")))]
use libc::{clock_settime, timespec, CLOCK_REALTIME};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
#[cfg(windows)]
use winapi::{
    shared::minwindef::WORD,
    um::{minwinbase::SYSTEMTIME, sysinfoapi::SetSystemTime},
};

// Options
const DATE: &str = "date";
const HOURS: &str = "hours";
const MINUTES: &str = "minutes";
const SECONDS: &str = "seconds";
const HOUR: &str = "hour";
const MINUTE: &str = "minute";
const SECOND: &str = "second";
const NS: &str = "ns";

const NAME: &str = "date";
const ABOUT: &str = "print or set the system date and time";

const OPT_DATE: &str = "date";
const OPT_FORMAT: &str = "format";
const OPT_FILE: &str = "file";
const OPT_DEBUG: &str = "debug";
const OPT_ISO_8601: &str = "iso-8601";
const OPT_RFC_EMAIL: &str = "rfc-email";
const OPT_RFC_3339: &str = "rfc-3339";
const OPT_SET: &str = "set";
const OPT_REFERENCE: &str = "reference";
const OPT_UNIVERSAL: &str = "universal";
const OPT_UNIVERSAL_2: &str = "utc";

// Help strings

static ISO_8601_HELP_STRING: &str = "output date/time in ISO 8601 format.
 FMT='date' for date only (the default),
 'hours', 'minutes', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14T02:34:56-06:00";

static RFC_5322_HELP_STRING: &str = "output date and time in RFC 5322 format.
 Example: Mon, 14 Aug 2006 02:34:56 -0600";

static RFC_3339_HELP_STRING: &str = "output date/time in RFC 3339 format.
 FMT='date', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14 02:34:56-06:00";

#[cfg(not(target_os = "macos"))]
static OPT_SET_HELP_STRING: &str = "set time described by STRING";
#[cfg(target_os = "macos")]
static OPT_SET_HELP_STRING: &str = "set time described by STRING (not available on mac yet)";

/// Settings for this program, parsed from the command line
struct Settings {
    utc: bool,
    format: Format,
    date_source: DateSource,
    set_to: Option<DateTime<FixedOffset>>,
}

/// Various ways of displaying the date
enum Format {
    Iso8601(Iso8601Format),
    Rfc5322,
    Rfc3339(Rfc3339Format),
    Custom(String),
    Default,
}

/// Various places that dates can come from
enum DateSource {
    Now,
    Custom(String),
    File(PathBuf),
}

enum Iso8601Format {
    Date,
    Hours,
    Minutes,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Iso8601Format {
    fn from(s: &str) -> Self {
        match s {
            HOURS | HOUR => Iso8601Format::Hours,
            MINUTES | MINUTE => Iso8601Format::Minutes,
            SECONDS | SECOND => Iso8601Format::Seconds,
            NS => Iso8601Format::Ns,
            DATE => Iso8601Format::Date,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

enum Rfc3339Format {
    Date,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Rfc3339Format {
    fn from(s: &str) -> Self {
        match s {
            DATE => Rfc3339Format::Date,
            SECONDS | SECOND => Rfc3339Format::Seconds,
            NS => Rfc3339Format::Ns,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let syntax = format!(
        "{0} [OPTION]... [+FORMAT]...
 {0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]",
        NAME
    );
    let matches = uu_app().usage(&syntax[..]).get_matches_from(args);

    let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
        if !form.starts_with('+') {
            eprintln!("date: invalid date '{}'", form);
            return 1;
        }
        let form = form[1..].to_string();
        Format::Custom(form)
    } else if let Some(fmt) = matches
        .values_of(OPT_ISO_8601)
        .map(|mut iter| iter.next().unwrap_or(DATE).into())
    {
        Format::Iso8601(fmt)
    } else if matches.is_present(OPT_RFC_EMAIL) {
        Format::Rfc5322
    } else if let Some(fmt) = matches.value_of(OPT_RFC_3339).map(Into::into) {
        Format::Rfc3339(fmt)
    } else {
        Format::Default
    };

    let date_source = if let Some(date) = matches.value_of(OPT_DATE) {
        DateSource::Custom(date.into())
    } else if let Some(file) = matches.value_of(OPT_FILE) {
        DateSource::File(file.into())
    } else {
        DateSource::Now
    };

    let set_to = match matches.value_of(OPT_SET).map(parse_date) {
        None => None,
        Some(Err((input, _err))) => {
            eprintln!("date: invalid date '{}'", input);
            return 1;
        }
        Some(Ok(date)) => Some(date),
    };

    let settings = Settings {
        utc: matches.is_present(OPT_UNIVERSAL),
        format,
        date_source,
        set_to,
    };

    if let Some(date) = settings.set_to {
        // All set time functions expect UTC datetimes.
        let date: DateTime<Utc> = if settings.utc {
            date.with_timezone(&Utc)
        } else {
            date.into()
        };

        return set_system_datetime(date);
    } else {
        // Declare a file here because it needs to outlive the `dates` iterator.
        let file: File;

        // Get the current time, either in the local time zone or UTC.
        let now: DateTime<FixedOffset> = if settings.utc {
            let now = Utc::now();
            now.with_timezone(&now.offset().fix())
        } else {
            let now = Local::now();
            now.with_timezone(now.offset())
        };

        // Iterate over all dates - whether it's a single date or a file.
        let dates: Box<dyn Iterator<Item = _>> = match settings.date_source {
            DateSource::Custom(ref input) => {
                let date = parse_date(input.clone());
                let iter = std::iter::once(date);
                Box::new(iter)
            }
            DateSource::File(ref path) => {
                file = File::open(path).unwrap();
                let lines = BufReader::new(file).lines();
                let iter = lines.filter_map(Result::ok).map(parse_date);
                Box::new(iter)
            }
            DateSource::Now => {
                let iter = std::iter::once(Ok(now));
                Box::new(iter)
            }
        };

        let format_string = make_format_string(&settings);

        // Format all the dates
        for date in dates {
            match date {
                Ok(date) => {
                    // GNU `date` uses `%N` for nano seconds, however crate::chrono uses `%f`
                    let format_string = &format_string.replace("%N", "%f");
                    let formatted = date.format(format_string).to_string().replace("%f", "%N");
                    println!("{}", formatted);
                }
                Err((input, _err)) => {
                    println!("date: invalid date '{}'", input);
                }
            }
        }
    }

    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(OPT_DATE)
                .short("d")
                .long(OPT_DATE)
                .takes_value(true)
                .help("display time described by STRING, not 'now'"),
        )
        .arg(
            Arg::with_name(OPT_FILE)
                .short("f")
                .long(OPT_FILE)
                .takes_value(true)
                .help("like --date; once for each line of DATEFILE"),
        )
        .arg(
            Arg::with_name(OPT_ISO_8601)
                .short("I")
                .long(OPT_ISO_8601)
                .takes_value(true)
                .help(ISO_8601_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_EMAIL)
                .short("R")
                .long(OPT_RFC_EMAIL)
                .help(RFC_5322_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_3339)
                .long(OPT_RFC_3339)
                .takes_value(true)
                .help(RFC_3339_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_DEBUG)
                .long(OPT_DEBUG)
                .help("annotate the parsed date, and warn about questionable usage to stderr"),
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
                .short("r")
                .long(OPT_REFERENCE)
                .takes_value(true)
                .help("display the last modification time of FILE"),
        )
        .arg(
            Arg::with_name(OPT_SET)
                .short("s")
                .long(OPT_SET)
                .takes_value(true)
                .help(OPT_SET_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_UNIVERSAL)
                .short("u")
                .long(OPT_UNIVERSAL)
                .alias(OPT_UNIVERSAL_2)
                .help("print or set Coordinated Universal Time (UTC)"),
        )
        .arg(Arg::with_name(OPT_FORMAT).multiple(false))
}

/// Return the appropriate format string for the given settings.
fn make_format_string(settings: &Settings) -> &str {
    match settings.format {
        Format::Iso8601(ref fmt) => match *fmt {
            Iso8601Format::Date => "%F",
            Iso8601Format::Hours => "%FT%H%:z",
            Iso8601Format::Minutes => "%FT%H:%M%:z",
            Iso8601Format::Seconds => "%FT%T%:z",
            Iso8601Format::Ns => "%FT%T,%f%:z",
        },
        Format::Rfc5322 => "%a, %d %h %Y %T %z",
        Format::Rfc3339(ref fmt) => match *fmt {
            Rfc3339Format::Date => "%F",
            Rfc3339Format::Seconds => "%F %T%:z",
            Rfc3339Format::Ns => "%F %T.%f%:z",
        },
        Format::Custom(ref fmt) => fmt,
        Format::Default => "%c",
    }
}

/// Parse a `String` into a `DateTime`.
/// If it fails, return a tuple of the `String` along with its `ParseError`.
fn parse_date<S: AsRef<str> + Clone>(
    s: S,
) -> Result<DateTime<FixedOffset>, (String, chrono::format::ParseError)> {
    // TODO: The GNU date command can parse a wide variety of inputs.
    s.as_ref().parse().map_err(|e| (s.as_ref().into(), e))
}

#[cfg(not(any(unix, windows)))]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    unimplemented!("setting date not implemented (unsupported target)");
}

#[cfg(target_os = "macos")]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    eprintln!("date: setting the date is not supported by macOS");
    1
}

#[cfg(all(unix, not(target_os = "macos")))]
/// System call to set date (unix).
/// See here for more:
/// https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html
/// https://linux.die.net/man/3/clock_settime
/// https://www.gnu.org/software/libc/manual/html_node/Time-Types.html
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let timespec = timespec {
        tv_sec: date.timestamp() as _,
        tv_nsec: date.timestamp_subsec_nanos() as _,
    };

    let result = unsafe { clock_settime(CLOCK_REALTIME, &timespec) };

    if result != 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}

#[cfg(windows)]
/// System call to set date (Windows).
/// See here for more:
/// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-setsystemtime
/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let system_time = SYSTEMTIME {
        wYear: date.year() as WORD,
        wMonth: date.month() as WORD,
        // Ignored
        wDayOfWeek: 0,
        wDay: date.day() as WORD,
        wHour: date.hour() as WORD,
        wMinute: date.minute() as WORD,
        wSecond: date.second() as WORD,
        // TODO: be careful of leap seconds - valid range is [0, 999] - how to handle?
        wMilliseconds: ((date.nanosecond() / 1_000_000) % 1000) as WORD,
    };

    let result = unsafe { SetSystemTime(&system_time) };

    if result == 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}
<commit_msg>date: disable setting time on redox<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Anthony Deschamps <anthony.j.deschamps@gmail.com>
// (c) Sylvestre Ledru <sylvestre@debian.org>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes

#[macro_use]
extern crate uucore;

use chrono::{DateTime, FixedOffset, Local, Offset, Utc};
#[cfg(windows)]
use chrono::{Datelike, Timelike};
use clap::{crate_version, App, Arg};
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
use libc::{clock_settime, timespec, CLOCK_REALTIME};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
#[cfg(windows)]
use winapi::{
    shared::minwindef::WORD,
    um::{minwinbase::SYSTEMTIME, sysinfoapi::SetSystemTime},
};

// Options
const DATE: &str = "date";
const HOURS: &str = "hours";
const MINUTES: &str = "minutes";
const SECONDS: &str = "seconds";
const HOUR: &str = "hour";
const MINUTE: &str = "minute";
const SECOND: &str = "second";
const NS: &str = "ns";

const NAME: &str = "date";
const ABOUT: &str = "print or set the system date and time";

const OPT_DATE: &str = "date";
const OPT_FORMAT: &str = "format";
const OPT_FILE: &str = "file";
const OPT_DEBUG: &str = "debug";
const OPT_ISO_8601: &str = "iso-8601";
const OPT_RFC_EMAIL: &str = "rfc-email";
const OPT_RFC_3339: &str = "rfc-3339";
const OPT_SET: &str = "set";
const OPT_REFERENCE: &str = "reference";
const OPT_UNIVERSAL: &str = "universal";
const OPT_UNIVERSAL_2: &str = "utc";

// Help strings

static ISO_8601_HELP_STRING: &str = "output date/time in ISO 8601 format.
 FMT='date' for date only (the default),
 'hours', 'minutes', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14T02:34:56-06:00";

static RFC_5322_HELP_STRING: &str = "output date and time in RFC 5322 format.
 Example: Mon, 14 Aug 2006 02:34:56 -0600";

static RFC_3339_HELP_STRING: &str = "output date/time in RFC 3339 format.
 FMT='date', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14 02:34:56-06:00";

#[cfg(not(any(target_os = "macos", target_os = "redox")))]
static OPT_SET_HELP_STRING: &str = "set time described by STRING";
#[cfg(target_os = "macos")]
static OPT_SET_HELP_STRING: &str = "set time described by STRING (not available on mac yet)";
#[cfg(target_os = "redox")]
static OPT_SET_HELP_STRING: &str = "set time described by STRING (not available on redox yet)";

/// Settings for this program, parsed from the command line
struct Settings {
    utc: bool,
    format: Format,
    date_source: DateSource,
    set_to: Option<DateTime<FixedOffset>>,
}

/// Various ways of displaying the date
enum Format {
    Iso8601(Iso8601Format),
    Rfc5322,
    Rfc3339(Rfc3339Format),
    Custom(String),
    Default,
}

/// Various places that dates can come from
enum DateSource {
    Now,
    Custom(String),
    File(PathBuf),
}

enum Iso8601Format {
    Date,
    Hours,
    Minutes,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Iso8601Format {
    fn from(s: &str) -> Self {
        match s {
            HOURS | HOUR => Iso8601Format::Hours,
            MINUTES | MINUTE => Iso8601Format::Minutes,
            SECONDS | SECOND => Iso8601Format::Seconds,
            NS => Iso8601Format::Ns,
            DATE => Iso8601Format::Date,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

enum Rfc3339Format {
    Date,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Rfc3339Format {
    fn from(s: &str) -> Self {
        match s {
            DATE => Rfc3339Format::Date,
            SECONDS | SECOND => Rfc3339Format::Seconds,
            NS => Rfc3339Format::Ns,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let syntax = format!(
        "{0} [OPTION]... [+FORMAT]...
 {0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]",
        NAME
    );
    let matches = uu_app().usage(&syntax[..]).get_matches_from(args);

    let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
        if !form.starts_with('+') {
            eprintln!("date: invalid date '{}'", form);
            return 1;
        }
        let form = form[1..].to_string();
        Format::Custom(form)
    } else if let Some(fmt) = matches
        .values_of(OPT_ISO_8601)
        .map(|mut iter| iter.next().unwrap_or(DATE).into())
    {
        Format::Iso8601(fmt)
    } else if matches.is_present(OPT_RFC_EMAIL) {
        Format::Rfc5322
    } else if let Some(fmt) = matches.value_of(OPT_RFC_3339).map(Into::into) {
        Format::Rfc3339(fmt)
    } else {
        Format::Default
    };

    let date_source = if let Some(date) = matches.value_of(OPT_DATE) {
        DateSource::Custom(date.into())
    } else if let Some(file) = matches.value_of(OPT_FILE) {
        DateSource::File(file.into())
    } else {
        DateSource::Now
    };

    let set_to = match matches.value_of(OPT_SET).map(parse_date) {
        None => None,
        Some(Err((input, _err))) => {
            eprintln!("date: invalid date '{}'", input);
            return 1;
        }
        Some(Ok(date)) => Some(date),
    };

    let settings = Settings {
        utc: matches.is_present(OPT_UNIVERSAL),
        format,
        date_source,
        set_to,
    };

    if let Some(date) = settings.set_to {
        // All set time functions expect UTC datetimes.
        let date: DateTime<Utc> = if settings.utc {
            date.with_timezone(&Utc)
        } else {
            date.into()
        };

        return set_system_datetime(date);
    } else {
        // Declare a file here because it needs to outlive the `dates` iterator.
        let file: File;

        // Get the current time, either in the local time zone or UTC.
        let now: DateTime<FixedOffset> = if settings.utc {
            let now = Utc::now();
            now.with_timezone(&now.offset().fix())
        } else {
            let now = Local::now();
            now.with_timezone(now.offset())
        };

        // Iterate over all dates - whether it's a single date or a file.
        let dates: Box<dyn Iterator<Item = _>> = match settings.date_source {
            DateSource::Custom(ref input) => {
                let date = parse_date(input.clone());
                let iter = std::iter::once(date);
                Box::new(iter)
            }
            DateSource::File(ref path) => {
                file = File::open(path).unwrap();
                let lines = BufReader::new(file).lines();
                let iter = lines.filter_map(Result::ok).map(parse_date);
                Box::new(iter)
            }
            DateSource::Now => {
                let iter = std::iter::once(Ok(now));
                Box::new(iter)
            }
        };

        let format_string = make_format_string(&settings);

        // Format all the dates
        for date in dates {
            match date {
                Ok(date) => {
                    // GNU `date` uses `%N` for nano seconds, however crate::chrono uses `%f`
                    let format_string = &format_string.replace("%N", "%f");
                    let formatted = date.format(format_string).to_string().replace("%f", "%N");
                    println!("{}", formatted);
                }
                Err((input, _err)) => {
                    println!("date: invalid date '{}'", input);
                }
            }
        }
    }

    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(OPT_DATE)
                .short("d")
                .long(OPT_DATE)
                .takes_value(true)
                .help("display time described by STRING, not 'now'"),
        )
        .arg(
            Arg::with_name(OPT_FILE)
                .short("f")
                .long(OPT_FILE)
                .takes_value(true)
                .help("like --date; once for each line of DATEFILE"),
        )
        .arg(
            Arg::with_name(OPT_ISO_8601)
                .short("I")
                .long(OPT_ISO_8601)
                .takes_value(true)
                .help(ISO_8601_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_EMAIL)
                .short("R")
                .long(OPT_RFC_EMAIL)
                .help(RFC_5322_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_3339)
                .long(OPT_RFC_3339)
                .takes_value(true)
                .help(RFC_3339_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_DEBUG)
                .long(OPT_DEBUG)
                .help("annotate the parsed date, and warn about questionable usage to stderr"),
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
                .short("r")
                .long(OPT_REFERENCE)
                .takes_value(true)
                .help("display the last modification time of FILE"),
        )
        .arg(
            Arg::with_name(OPT_SET)
                .short("s")
                .long(OPT_SET)
                .takes_value(true)
                .help(OPT_SET_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_UNIVERSAL)
                .short("u")
                .long(OPT_UNIVERSAL)
                .alias(OPT_UNIVERSAL_2)
                .help("print or set Coordinated Universal Time (UTC)"),
        )
        .arg(Arg::with_name(OPT_FORMAT).multiple(false))
}

/// Return the appropriate format string for the given settings.
fn make_format_string(settings: &Settings) -> &str {
    match settings.format {
        Format::Iso8601(ref fmt) => match *fmt {
            Iso8601Format::Date => "%F",
            Iso8601Format::Hours => "%FT%H%:z",
            Iso8601Format::Minutes => "%FT%H:%M%:z",
            Iso8601Format::Seconds => "%FT%T%:z",
            Iso8601Format::Ns => "%FT%T,%f%:z",
        },
        Format::Rfc5322 => "%a, %d %h %Y %T %z",
        Format::Rfc3339(ref fmt) => match *fmt {
            Rfc3339Format::Date => "%F",
            Rfc3339Format::Seconds => "%F %T%:z",
            Rfc3339Format::Ns => "%F %T.%f%:z",
        },
        Format::Custom(ref fmt) => fmt,
        Format::Default => "%c",
    }
}

/// Parse a `String` into a `DateTime`.
/// If it fails, return a tuple of the `String` along with its `ParseError`.
fn parse_date<S: AsRef<str> + Clone>(
    s: S,
) -> Result<DateTime<FixedOffset>, (String, chrono::format::ParseError)> {
    // TODO: The GNU date command can parse a wide variety of inputs.
    s.as_ref().parse().map_err(|e| (s.as_ref().into(), e))
}

#[cfg(not(any(unix, windows)))]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    unimplemented!("setting date not implemented (unsupported target)");
}

#[cfg(target_os = "macos")]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    eprintln!("date: setting the date is not supported by macOS");
    1
}

#[cfg(target_os = "redox")]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    eprintln!("date: setting the date is not supported by Redox");
    1
}

#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
/// System call to set date (unix).
/// See here for more:
/// https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html
/// https://linux.die.net/man/3/clock_settime
/// https://www.gnu.org/software/libc/manual/html_node/Time-Types.html
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let timespec = timespec {
        tv_sec: date.timestamp() as _,
        tv_nsec: date.timestamp_subsec_nanos() as _,
    };

    let result = unsafe { clock_settime(CLOCK_REALTIME, &timespec) };

    if result != 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}

#[cfg(windows)]
/// System call to set date (Windows).
/// See here for more:
/// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-setsystemtime
/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let system_time = SYSTEMTIME {
        wYear: date.year() as WORD,
        wMonth: date.month() as WORD,
        // Ignored
        wDayOfWeek: 0,
        wDay: date.day() as WORD,
        wHour: date.hour() as WORD,
        wMinute: date.minute() as WORD,
        wSecond: date.second() as WORD,
        // TODO: be careful of leap seconds - valid range is [0, 999] - how to handle?
        wMilliseconds: ((date.nanosecond() / 1_000_000) % 1000) as WORD,
    };

    let result = unsafe { SetSystemTime(&system_time) };

    if result == 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}
<|endoftext|>


<commit_before>//! Official Rust client for [Elasticsearch](https://www.elastic.co/products/elasticsearch)
//!
//! `Elasticsearch` is an official Rust client for Elasticsearch, providing an efficient asynchronous
//! client for all stable Elasticsearch APIs that's easy to use.
//!
//! # Versions and Compatibility
//!
//! | Rust client | Elasticsearch |
//! |-------------|---------------|
//! | 7.x         | 7.x           |
//!
//! The major version of the client is compatible with the same major version of Elasticsearch.
//! Since Elasticsearch is developed following [Semantic Versioning](https://semver.org/) principles,
//! Any minor/patch version of the client can be used against any minor/patch version of Elasticsearch
//! **within the same major version lineage**. For example,
//!
//! - A `7.5.0` client can be used against `7.0.0` Elasticsearch
//! - A `7.4.0` client can be used against `7.5.1` Elasticsearch
//!
//! In the former case, a 7.5.0 client may contain additional API functions that are not available
//! in 7.0.0 Elasticsearch. In this case, these APIs cannot be used, but for any APIs available in
//! Elasticsearch, the respective API functions on the client will be compatible.
//!
//! In the latter case, a 7.4.0 client won't contain API functions for APIs that are introduced in
//! Elasticsearch 7.5.0+, but for all other APIs available in Elasticsearch, the respective API
//! functions on the client will be compatible.
//!
//! **No compatibility assurances are given between different major versions of the client and
//! Elasticsearch**. Major differences likely exist between major versions of Elasticsearch, particularly
//! around request and response object formats, but also around API urls and behaviour.
//!
//! # Getting started
//!
//! Add the `elasticsearch` crate and version to Cargo.toml. Choose the version that is compatible with
//! the version of Elasticsearch you're using
//!
//! ```toml,no_run
//! [dependencies]
//! elasticsearch = "7.5.0-alpha.1"
//! ```
//! The following _optional_ dependencies may also be useful to create requests and read responses
//!
//! ```toml,no_run
//! serde = "~1"
//! serde_json = "~1"
//! ```
//! ## Create a client
//!
//! To create a client to make API calls to Elasticsearch running on `http://localhost:9200`
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # async fn run() -> Result<(), Error> {
//! let client = Elasticsearch::default();
//! # Ok(())
//! # }
//! ```
//! Alternatively, you can create a client to make API calls against Elasticsearch running on a
//! specific [url::Url]
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # use elasticsearch::http::transport::{Transport, SingleNodeConnectionPool};
//! # async fn run() -> Result<(), Error> {
//! let transport = Transport::single_node("https://example.com")?;
//! let client = Elasticsearch::new(transport);
//! # Ok(())
//! # }
//! ```
//!
//! If you're running against an Elasticsearch deployment in [Elastic Cloud](https://www.elastic.co/cloud/),
//! a client can be created using a [Cloud ID](https://www.elastic.co/guide/en/cloud/current/ec-cloud-id.html)
//! and credentials retrieved from the Cloud web console
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # use url::Url;
//! # use elasticsearch::http::transport::Transport;
//! # use elasticsearch::auth::Credentials;
//! # async fn run() -> Result<(), Error> {
//! let cloud_id = "cluster_name:Y2xvdWQtZW5kcG9pbnQuZXhhbXBsZSQzZGFkZjgyM2YwNTM4ODQ5N2VhNjg0MjM2ZDkxOGExYQ==";
//! let credentials = Credentials::Basic("<username>".into(), "<password>".into());
//! let transport = Transport::cloud(cloud_id, credentials)?;
//! let client = Elasticsearch::new(transport);
//! # Ok(())
//! # }
//! ```
//!
//! More control over how a [Transport](http::transport::Transport) is built can be
//! achieved using [TransportBuilder](http::transport::TransportBuilder) to build a transport, and
//! passing it to [Elasticsearch::new] create a new instance of [Elasticsearch]
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # use url::Url;
//! # use elasticsearch::http::transport::{TransportBuilder, SingleNodeConnectionPool};
//! # use elasticsearch::auth::Credentials;
//! # async fn run() -> Result<(), Error> {
//! let url = Url::parse("https://example.com")?;
//! let conn_pool = SingleNodeConnectionPool::new(url);
//! let transport = TransportBuilder::new(conn_pool).disable_proxy().build()?;
//! let client = Elasticsearch::new(transport);
//! # Ok(())
//! # }
//! ```
//!
//! ## Making API calls
//!
//! The client exposes all stable Elasticsearch APIs, either on the root [Elasticsearch] client,
//! or on a _namespace_ client that groups related APIs, such as [Cat](cat::Cat), which groups the
//! Cat related APIs. All API functions are `async` and can be `await`ed.
//!
//! The following makes an API call to the cat indices API
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, cat::CatIndicesParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let response = client
//!     .cat()
//!     .indices(CatIndicesParts::Index(&["*"]))
//!     .send()
//!     .await?;
//!
//! let response_body = response.read_body::<Value>().await?;
//! for record in response_body.as_array().unwrap() {
//!     // print the name of each index
//!     println!("{}", record["index"].as_str().unwrap());
//! }
//! # Ok(())
//! # }
//! ```
//! For APIs that contain parts of the Url path to be provided by the consumer, the Url path
//! variants are modelled as an `enum`, such as [CatIndicesParts] in the above example, which models
//! the variants of the [CatIndices] API.
//!
//! ### Indexing
//!
//! Indexing a single document can be achieved with the index API
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, SearchParts, IndexParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let response = client
//!     .index(IndexParts::IndexId("tweets", "1"))
//!     .body(json!({
//!         "id": 1,
//!         "user": "kimchy",
//!         "post_date": "2009-11-15T00:00:00Z",
//!         "message": "Trying out Elasticsearch, so far so good?"
//!     }))
//!     .send()
//!     .await?;
//!
//! let successful = response.status_code().is_success();
//! # Ok(())
//! # }
//! ```
//!
//! For indexing multiple documents, the bulk API is a better option, allowing multiple operations
//! to be sent in one API call
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, IndexParts, BulkParts, http::request::JsonBody};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let mut body: Vec<JsonBody<_>> = Vec::with_capacity(4);
//!
//! // add the first operation and document
//! body.push(json!({"index": {"_id": "1"}}).into());
//! body.push(json!({
//!     "id": 1,
//!     "user": "kimchy",
//!     "post_date": "2009-11-15T00:00:00Z",
//!     "message": "Trying out Elasticsearch, so far so good?"
//! }).into());
//!
//! // add the second operation and document
//! body.push(json!({"index": {"_id": "2"}}).into());
//! body.push(json!({
//!     "id": 2,
//!     "user": "forloop",
//!     "post_date": "2020-01-08T00:00:00Z",
//!     "message": "Bulk indexing with the rust client, yeah!"
//! }).into());
//!
//! let response = client
//!     .bulk(BulkParts::Index("tweets"))
//!     .body(body)
//!     .send()
//!     .await?;
//!
//! let response_body = response.read_body::<Value>().await?;
//! let successful = response_body["errors"].as_bool().unwrap() == false;
//! # Ok(())
//! # }
//! ```
//! ### Searching
//!
//! The following makes an API call to `tweets/_search` with the json body
//! `{"query":{"match":{"message":"Elasticsearch"}}}`
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, SearchParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let response = client
//!     .search(SearchParts::Index(&["tweets"]))
//!     .from(0)
//!     .size(10)
//!     .body(json!({
//!         "query": {
//!             "match": {
//!                 "message": "Elasticsearch rust"
//!             }
//!         }
//!     }))
//!     .send()
//!     .await?;
//!
//! let response_body = response.read_body::<Value>().await?;
//! let took = response_body["took"].as_i64().unwrap();
//! for hit in response_body["hits"]["hits"].as_array().unwrap() {
//!     // print the source document
//!     println!("{:?}", hit["_source"]);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Request bodies
//!
//! For APIs that expect JSON, the `body` associated function of the API constrains the input
//! to a type that implements [serde::Serialize] trait. An example of this was the indexing a single
//! document example above.
//!
//! Some APIs expect newline delimited JSON
//! (NDJSON) however, so the `body` associated for these APIs constrain the input to a vector of
//! types that implement [Body] trait.  An example of this was the bulk indexing multiple documents
//! above.
//!
//! The [Body] trait represents the body of an API call, allowing for different body implementations.
//! As well as those to represent JSON and NDJSON, a few other types also have implementations for
//! [Body], such as byte slice. Whilst these can't be passed to the API functions directly,
//! [Elasticsearch::send] can be used
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, SearchParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # use http::HeaderMap;
//! # use elasticsearch::http::Method;
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let body = b"{\"query\":{\"match_all\":{}}}";
//!
//! let response = client
//!     .send(Method::Post,
//!         SearchParts::Index(&["tweets"]).url().as_ref(),
//!         HeaderMap::new(),
//!         Option::<&Value>::None,
//!         Some(body.as_ref())
//!     )
//!     .await?;
//!
//! # Ok(())
//! # }
//! ```
//!
//!
//!
//!

// also test examples in README
#![feature(external_doc)]
#[doc(include="../../README.md")]
#[cfg(doctest)]
type _DoctestReadme = ();

// TODO: turn on before releasing :) Will require adding documentation within all REST API specs
//#![deny(missing_docs)]

#[macro_use]
extern crate objekt;
extern crate reqwest;
extern crate serde;
extern crate serde_json;

pub mod auth;
pub mod http;
pub mod cat;
pub mod ccr;
pub mod cluster;
pub mod enrich;
pub mod graph;
pub mod ilm;
pub mod indices;
pub mod ingest;
pub mod license;
pub mod migration;
pub mod ml;
pub mod nodes;
pub mod params;
pub mod security;
pub mod slm;
pub mod snapshot;
pub mod sql;
pub mod ssl;
pub mod tasks;
pub mod watcher;
pub mod xpack;

mod client;
mod error;
mod generated;

// exposes types within modules at the library root level
pub use crate::{
    client::*, error::*, generated::root::*,
    http::transport::DEFAULT_ADDRESS,
};

#[cfg(test)]
pub mod tests {
    use crate::SearchParts;

    #[test]
    fn build_search_on_all_indices_and_types() {
        let parts = SearchParts::None;
        let url = parts.url();
        assert_eq!(url, "/_search");
    }

    #[test]
    fn build_search_on_selected_indices() {
        let parts = SearchParts::Index(&["index-1", "index-2"]);
        let url = parts.url();
        assert_eq!(url, "/index-1,index-2/_search");
    }

    #[test]
    fn build_search_on_selected_indices_and_types() {
        let parts = SearchParts::IndexType(&["index-1", "index-2"], &["type-1", "type-2"]);
        let url = parts.url();
        assert_eq!(url, "/index-1,index-2/type-1,type-2/_search");
    }
}
<commit_msg>Fix unresolved doc links<commit_after>//! Official Rust client for [Elasticsearch](https://www.elastic.co/products/elasticsearch)
//!
//! `Elasticsearch` is an official Rust client for Elasticsearch, providing an efficient asynchronous
//! client for all stable Elasticsearch APIs that's easy to use.
//!
//! # Versions and Compatibility
//!
//! | Rust client | Elasticsearch |
//! |-------------|---------------|
//! | 7.x         | 7.x           |
//!
//! The major version of the client is compatible with the same major version of Elasticsearch.
//! Since Elasticsearch is developed following [Semantic Versioning](https://semver.org/) principles,
//! Any minor/patch version of the client can be used against any minor/patch version of Elasticsearch
//! **within the same major version lineage**. For example,
//!
//! - A `7.5.0` client can be used against `7.0.0` Elasticsearch
//! - A `7.4.0` client can be used against `7.5.1` Elasticsearch
//!
//! In the former case, a 7.5.0 client may contain additional API functions that are not available
//! in 7.0.0 Elasticsearch. In this case, these APIs cannot be used, but for any APIs available in
//! Elasticsearch, the respective API functions on the client will be compatible.
//!
//! In the latter case, a 7.4.0 client won't contain API functions for APIs that are introduced in
//! Elasticsearch 7.5.0+, but for all other APIs available in Elasticsearch, the respective API
//! functions on the client will be compatible.
//!
//! **No compatibility assurances are given between different major versions of the client and
//! Elasticsearch**. Major differences likely exist between major versions of Elasticsearch, particularly
//! around request and response object formats, but also around API urls and behaviour.
//!
//! # Getting started
//!
//! Add the `elasticsearch` crate and version to Cargo.toml. Choose the version that is compatible with
//! the version of Elasticsearch you're using
//!
//! ```toml,no_run
//! [dependencies]
//! elasticsearch = "7.5.0-alpha.1"
//! ```
//! The following _optional_ dependencies may also be useful to create requests and read responses
//!
//! ```toml,no_run
//! serde = "~1"
//! serde_json = "~1"
//! ```
//! ## Create a client
//!
//! To create a client to make API calls to Elasticsearch running on `http://localhost:9200`
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # async fn run() -> Result<(), Error> {
//! let client = Elasticsearch::default();
//! # Ok(())
//! # }
//! ```
//! Alternatively, you can create a client to make API calls against Elasticsearch running on a
//! specific [url::Url]
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # use elasticsearch::http::transport::{Transport, SingleNodeConnectionPool};
//! # async fn run() -> Result<(), Error> {
//! let transport = Transport::single_node("https://example.com")?;
//! let client = Elasticsearch::new(transport);
//! # Ok(())
//! # }
//! ```
//!
//! If you're running against an Elasticsearch deployment in [Elastic Cloud](https://www.elastic.co/cloud/),
//! a client can be created using a [Cloud ID](https://www.elastic.co/guide/en/cloud/current/ec-cloud-id.html)
//! and credentials retrieved from the Cloud web console
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # use url::Url;
//! # use elasticsearch::http::transport::Transport;
//! # use elasticsearch::auth::Credentials;
//! # async fn run() -> Result<(), Error> {
//! let cloud_id = "cluster_name:Y2xvdWQtZW5kcG9pbnQuZXhhbXBsZSQzZGFkZjgyM2YwNTM4ODQ5N2VhNjg0MjM2ZDkxOGExYQ==";
//! let credentials = Credentials::Basic("<username>".into(), "<password>".into());
//! let transport = Transport::cloud(cloud_id, credentials)?;
//! let client = Elasticsearch::new(transport);
//! # Ok(())
//! # }
//! ```
//!
//! More control over how a [Transport](http::transport::Transport) is built can be
//! achieved using [TransportBuilder](http::transport::TransportBuilder) to build a transport, and
//! passing it to [Elasticsearch::new] create a new instance of [Elasticsearch]
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::Error;
//! # use elasticsearch::Elasticsearch;
//! # use url::Url;
//! # use elasticsearch::http::transport::{TransportBuilder, SingleNodeConnectionPool};
//! # use elasticsearch::auth::Credentials;
//! # async fn run() -> Result<(), Error> {
//! let url = Url::parse("https://example.com")?;
//! let conn_pool = SingleNodeConnectionPool::new(url);
//! let transport = TransportBuilder::new(conn_pool).disable_proxy().build()?;
//! let client = Elasticsearch::new(transport);
//! # Ok(())
//! # }
//! ```
//!
//! ## Making API calls
//!
//! The client exposes all stable Elasticsearch APIs, either on the root [Elasticsearch] client,
//! or on a _namespace_ client that groups related APIs, such as [Cat](cat::Cat), which groups the
//! Cat related APIs. All API functions are `async` and can be `await`ed.
//!
//! The following makes an API call to the cat indices API
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, cat::CatIndicesParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let response = client
//!     .cat()
//!     .indices(CatIndicesParts::Index(&["*"]))
//!     .send()
//!     .await?;
//!
//! let response_body = response.read_body::<Value>().await?;
//! for record in response_body.as_array().unwrap() {
//!     // print the name of each index
//!     println!("{}", record["index"].as_str().unwrap());
//! }
//! # Ok(())
//! # }
//! ```
//! For APIs that contain parts of the Url path to be provided by the consumer, the Url path
//! variants are modelled as an `enum`, such as [CatIndicesParts](cat::CatIndicesParts) in the above example, which models
//! the variants of the [CatIndices](cat::CatIndices) API.
//!
//! ### Indexing
//!
//! Indexing a single document can be achieved with the index API
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, SearchParts, IndexParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let response = client
//!     .index(IndexParts::IndexId("tweets", "1"))
//!     .body(json!({
//!         "id": 1,
//!         "user": "kimchy",
//!         "post_date": "2009-11-15T00:00:00Z",
//!         "message": "Trying out Elasticsearch, so far so good?"
//!     }))
//!     .send()
//!     .await?;
//!
//! let successful = response.status_code().is_success();
//! # Ok(())
//! # }
//! ```
//!
//! For indexing multiple documents, the bulk API is a better option, allowing multiple operations
//! to be sent in one API call
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, IndexParts, BulkParts, http::request::JsonBody};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let mut body: Vec<JsonBody<_>> = Vec::with_capacity(4);
//!
//! // add the first operation and document
//! body.push(json!({"index": {"_id": "1"}}).into());
//! body.push(json!({
//!     "id": 1,
//!     "user": "kimchy",
//!     "post_date": "2009-11-15T00:00:00Z",
//!     "message": "Trying out Elasticsearch, so far so good?"
//! }).into());
//!
//! // add the second operation and document
//! body.push(json!({"index": {"_id": "2"}}).into());
//! body.push(json!({
//!     "id": 2,
//!     "user": "forloop",
//!     "post_date": "2020-01-08T00:00:00Z",
//!     "message": "Bulk indexing with the rust client, yeah!"
//! }).into());
//!
//! let response = client
//!     .bulk(BulkParts::Index("tweets"))
//!     .body(body)
//!     .send()
//!     .await?;
//!
//! let response_body = response.read_body::<Value>().await?;
//! let successful = response_body["errors"].as_bool().unwrap() == false;
//! # Ok(())
//! # }
//! ```
//! ### Searching
//!
//! The following makes an API call to `tweets/_search` with the json body
//! `{"query":{"match":{"message":"Elasticsearch"}}}`
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, SearchParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let response = client
//!     .search(SearchParts::Index(&["tweets"]))
//!     .from(0)
//!     .size(10)
//!     .body(json!({
//!         "query": {
//!             "match": {
//!                 "message": "Elasticsearch rust"
//!             }
//!         }
//!     }))
//!     .send()
//!     .await?;
//!
//! let response_body = response.read_body::<Value>().await?;
//! let took = response_body["took"].as_i64().unwrap();
//! for hit in response_body["hits"]["hits"].as_array().unwrap() {
//!     // print the source document
//!     println!("{:?}", hit["_source"]);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Request bodies
//!
//! For APIs that expect JSON, the `body` associated function of the API constrains the input
//! to a type that implements [serde::Serialize] trait. An example of this was the indexing a single
//! document example above.
//!
//! Some APIs expect newline delimited JSON
//! (NDJSON) however, so the `body` associated for these APIs constrain the input to a vector of
//! types that implement [Body](http::request::Body) trait.  An example of this was the bulk indexing multiple documents
//! above.
//!
//! The [Body](http::request::Body) trait represents the body of an API call, allowing for different body implementations.
//! As well as those to represent JSON and NDJSON, a few other types also have implementations for
//! [Body](http::request::Body), such as byte slice. Whilst these can't be passed to the API functions directly,
//! [Elasticsearch::send] can be used
//!
//! ```rust,no_run
//! # use elasticsearch;
//! # use elasticsearch::{Elasticsearch, Error, SearchParts};
//! # use url::Url;
//! # use elasticsearch::auth::Credentials;
//! # use serde_json::{json, Value};
//! # use http::HeaderMap;
//! # use elasticsearch::http::Method;
//! # async fn run() -> Result<(), Error> {
//! # let client = Elasticsearch::default();
//! let body = b"{\"query\":{\"match_all\":{}}}";
//!
//! let response = client
//!     .send(Method::Post,
//!         SearchParts::Index(&["tweets"]).url().as_ref(),
//!         HeaderMap::new(),
//!         Option::<&Value>::None,
//!         Some(body.as_ref())
//!     )
//!     .await?;
//!
//! # Ok(())
//! # }
//! ```
//!
//!
//!
//!

// also test examples in README
#![feature(external_doc)]
#[doc(include="../../README.md")]
#[cfg(doctest)]
type _DoctestReadme = ();

// TODO: turn on before releasing :) Will require adding documentation within all REST API specs
//#![deny(missing_docs)]

#[macro_use]
extern crate objekt;
extern crate reqwest;
extern crate serde;
extern crate serde_json;

pub mod auth;
pub mod http;
pub mod cat;
pub mod ccr;
pub mod cluster;
pub mod enrich;
pub mod graph;
pub mod ilm;
pub mod indices;
pub mod ingest;
pub mod license;
pub mod migration;
pub mod ml;
pub mod nodes;
pub mod params;
pub mod security;
pub mod slm;
pub mod snapshot;
pub mod sql;
pub mod ssl;
pub mod tasks;
pub mod watcher;
pub mod xpack;

mod client;
mod error;
mod generated;

// exposes types within modules at the library root level
pub use crate::{
    client::*, error::*, generated::root::*,
    http::transport::DEFAULT_ADDRESS,
};

#[cfg(test)]
pub mod tests {
    use crate::SearchParts;

    #[test]
    fn build_search_on_all_indices_and_types() {
        let parts = SearchParts::None;
        let url = parts.url();
        assert_eq!(url, "/_search");
    }

    #[test]
    fn build_search_on_selected_indices() {
        let parts = SearchParts::Index(&["index-1", "index-2"]);
        let url = parts.url();
        assert_eq!(url, "/index-1,index-2/_search");
    }

    #[test]
    fn build_search_on_selected_indices_and_types() {
        let parts = SearchParts::IndexType(&["index-1", "index-2"], &["type-1", "type-2"]);
        let url = parts.url();
        assert_eq!(url, "/index-1,index-2/type-1,type-2/_search");
    }
}
<|endoftext|>