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

//use super::offsets::Byte;
use super::offsets::Grapheme;
//use core::any::Any;
use core::ops::Range;
use std::rc::Rc;
//use core::ops::Deref;

use crate::*;

/// [Piece] is a `str`-like type that also:
/// - is immutable,
/// - shares a backing buffer for cheap clones and slices,
/// - and measure offsets in user-perceived graphemes instead of bytes or chars.
///
/// [Piece] is intended to be used with [MarkStr] and [Text].
///
/// # Examples
///
/// ```
/// use notemaps_text::Piece;
/// use notemaps_text::offsets::Grapheme;
///
/// let piece: Piece = "this is a test".into();
/// let slice: Piece = piece.slice(Grapheme(10)..Grapheme(14));
/// assert_eq!(slice.as_str(), "test");
/// ```
#[derive(Clone, Debug)]
pub struct Piece {
    buffer: Rc<str>,
    byte_range: Range<usize>,
    len_graphemes: Grapheme,
    marks: MarkSet,
}

impl Piece {
    pub fn new(buffer: Rc<str>) -> Self {
        use unicode_segmentation::UnicodeSegmentation;
        let byte_range = 0..buffer.len();
        let len_graphemes = buffer.graphemes(true).count().into();
        Self {
            buffer,
            byte_range,
            len_graphemes,
            marks: MarkSet::new(),
        }
    }

    pub fn slice(&self, r: Range<Grapheme>) -> Self {
        use std::iter;
        use unicode_segmentation::UnicodeSegmentation;
        let mut graphemes = self
            .as_str()
            .grapheme_indices(true)
            .map(|t| t.0)
            .chain(iter::once(self.as_str().len()));
        let start = graphemes
            .by_ref()
            .nth(*r.start.as_ref())
            .expect("range starts within bounds of this piece");
        let end = if r.is_empty() {
            start
        } else {
            graphemes
                .by_ref()
                .nth(*r.end.as_ref() - 1 - *r.start.as_ref())
                .expect("range ends within bounds of piece")
        };
        Self {
            buffer: self.buffer.clone(),
            byte_range: start..end,
            len_graphemes: (r.end - r.start).into(),
            marks: MarkSet::new(),
        }
    }

    pub fn len(&self) -> Grapheme {
        self.len_graphemes
    }

    pub fn as_str(&self) -> &str {
        &self.buffer[self.byte_range.clone()]
    }

    pub fn marks(&self) -> &MarkSet {
        &self.marks
    }

    pub fn marks_mut(&mut self) -> &mut MarkSet {
        &mut self.marks
    }
}

impl AsRef<str> for Piece {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl<'a> From<&'a str> for Piece {
    fn from(s: &'a str) -> Self {
        Self::new(s.into())
    }
}

#[derive(Clone, Debug)]
struct Table {
    pieces: Vec<MarkStr<Piece>>,
    len_graphemes: Grapheme,
}

impl Table {
    pub fn len(&self) -> Grapheme {
        self.len_graphemes
    }
}

impl FromIterator<MarkStr<Piece>> for Table {
    fn from_iter<T: IntoIterator<Item = MarkStr<Piece>>>(iter: T) -> Self {
        let pieces: Vec<_> = iter.into_iter().collect();
        let len_graphemes = pieces.iter().map(|m| m.get().len()).sum();
        Self {
            pieces,
            len_graphemes,
        }
    }
}

enum TextInternal {
    Empty,
    Piece(MarkStr<Piece>),
    Table(Table),
}

use std::any::Any;

/// [Text] is a "piece chain" or "piece table" using [Piece] and [MarkStr] to represent a
/// formatted, or "rich-text", document.
///
/// # Examples
///
/// ```rust
/// use notemaps_text::Text;
///
/// let text: Text = ["Hello, world!", "\n"].into_iter().collect();
/// assert_eq!(text.to_string(), "Hello, world!\n");
/// ```
pub struct Text(TextInternal);

impl Text {
    pub fn pieces(&self) -> Pieces {
        use std::iter;
        Pieces(match &self.0 {
            TextInternal::Empty => PiecesInternal::Empty(iter::empty()),
            TextInternal::Piece(piece) => PiecesInternal::Piece(iter::once(&piece)),
            TextInternal::Table(table) => PiecesInternal::Table(table.pieces.iter()),
        })
    }

    pub fn len(&self) -> Grapheme {
        match &self.0 {
            TextInternal::Empty => Grapheme(0),
            TextInternal::Piece(piece) => piece.get().len(),
            TextInternal::Table(table) => table.len(),
        }
    }

    pub fn to_string(&self) -> String {
        self.pieces().map(|p| p.as_str()).collect()
    }

    pub fn with_insert<I: IntoIterator>(&self, _at: Grapheme, _text: I) -> Self
    where
        Text: FromIterator<I::Item>,
    {
        todo!("");
    }

    pub fn with_mark<M: Any>(&self, _r: Range<Grapheme>, _mark: M) -> Self {
        todo!("");
    }
}

impl FromIterator<MarkStr<Piece>> for Text {
    fn from_iter<T: IntoIterator<Item = MarkStr<Piece>>>(iter: T) -> Self {
        use std::iter;
        let mut iter = iter.into_iter();
        Text(match iter.next() {
            Some(piece0) => match iter.next() {
                Some(piece1) => TextInternal::Table(
                    iter::once(piece0)
                        .chain(iter::once(piece1))
                        .chain(iter)
                        .collect(),
                ),
                None => TextInternal::Piece(piece0),
            },
            None => TextInternal::Empty,
        })
    }
}

impl FromIterator<Piece> for Text {
    fn from_iter<T: IntoIterator<Item = Piece>>(iter: T) -> Self {
        iter.into_iter()
            .map(|piece| MarkStr::new(MarkSet::new(), piece))
            .collect()
    }
}

impl<'a> FromIterator<&'a str> for Text {
    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
        iter.into_iter().map(Piece::from).collect()
    }
}

use core::ops;

impl ops::Add<Self> for Text {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        self.pieces().chain(other.pieces()).cloned().collect()
    }
}

enum PiecesInternal<'a> {
    Empty(std::iter::Empty<&'a MarkStr<Piece>>),
    Piece(std::iter::Once<&'a MarkStr<Piece>>),
    Table(std::slice::Iter<'a, MarkStr<Piece>>),
}

/// [Pieces] is the [Iterator] type returned by [Text::pieces].
pub struct Pieces<'a>(PiecesInternal<'a>);

impl<'a> Iterator for Pieces<'a> {
    type Item = &'a MarkStr<Piece>;
    fn next(&mut self) -> Option<Self::Item> {
        match &mut self.0 {
            PiecesInternal::Empty(iter) => iter.next(),
            PiecesInternal::Piece(iter) => iter.next(),
            PiecesInternal::Table(iter) => iter.next(),
        }
    }
}

#[cfg(test)]
mod a_piece_table {
    use super::*;
    #[test]
    fn constructed_from_strs() {
        let text: Text = ["a̐éö̲", "\r\n"].into_iter().collect();
        assert_eq!(text.to_string(), "a̐éö̲\r\n");
        assert_eq!(text.len(), Grapheme(4));
    }
}

/*
pub trait Cursor {
    type Item:?Sized;
    fn move_next(&mut self);
    fn move_prev(&mut self);
    fn current(&self) -> Option<&Self::Item>;
    fn index(&self)->Option<usize>;
}

pub struct SliceCursor<'a, T> {
    slice: &'a [T],
    i: usize,
}

impl<'a, T> Cursor for SliceCursor<'a, T> {
    type Item = T;
    fn move_next(&mut self) {
        if self.i < self.slice.len() {
            self.i += 1;
        } else {
            self.i = 0;
        }
    }
    fn move_prev(&mut self) {
        if self.i > 0 {
            self.i -= 1;
        } else {
            self.i = self.slice.len();
        }
    }
    fn current(&self) -> Option<&T> {
        if self.i < self.slice.len() {
            Some(&self.slice[self.i])
        } else {
            None
        }
    }
    fn index(&self)->Option<usize>{
        if self.i<self.slice.len(){
            Some(self.i)
        }else{
            None
        }
    }
}

impl<'a, T> From<&'a [T]> for SliceCursor<'a, T> {
    fn from(slice: &'a [T]) -> Self {
        Self { slice, i: 0 }
    }
}

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

    #[test]
    fn moves_forward_through_a_slice_as_ring() {
        let slice = vec!["red", "blue", "green"];
        let mut cursor = SliceCursor::from(slice.as_ref());
        assert_eq!(cursor.current().copied(), Some("red"));
        cursor.move_next();
        assert_eq!(cursor.current().copied(), Some("blue"));
        cursor.move_next();
        assert_eq!(cursor.current().copied(), Some("green"));
        cursor.move_next();
        assert_eq!(cursor.current(), None);
        cursor.move_next();
        assert_eq!(cursor.current().copied(), Some("red"));
    }

    #[test]
    fn moves_backward_through_a_slice_as_ring() {
        let slice = vec!["red", "blue", "green"];
        let mut cursor = SliceCursor::from(slice.as_ref());
        assert_eq!(cursor.current().copied(), Some("red"));
        cursor.move_prev();
        assert_eq!(cursor.current(), None);
        cursor.move_prev();
        assert_eq!(cursor.current().copied(), Some("green"));
        cursor.move_prev();
        assert_eq!(cursor.current().copied(), Some("blue"));
    }
}

use core::ops::Range;
use unicode_segmentation::GraphemeCursor as GraphemeBoundaryCursor;
use unicode_segmentation::GraphemeIncomplete;
//use std::borrow::Cow;

pub struct GraphemeCursor< T> {
    //'a, T:Cursor<'a,U >, U:AsRef<str>> {
    strings: T,
    absolute: Range<usize>,
    relative: Range<usize>,
    boundaries: GraphemeBoundaryCursor,
    broken: Option<GraphemeIncomplete>,
}

impl< T: Cursor> GraphemeCursor< T> {}

impl< T: Cursor> Cursor for GraphemeCursor< T>
where
    T::Item: AsRef<str>,
{
    type Item = str;

    fn move_next(&mut self) {
        //if self.strings.current().is_none() { self.strings.move_next(); self.start = 0; self.boundaries.set_cursor(0); self.broken = None; }
        while let Some(s) = self.strings.current().map(|s|s.as_ref()) {
            let start = self.boundaries.cur_cursor();
            match self.boundaries.next_boundary(s, 0) {
                Ok(Some(end)) => {
                    // This is the typical case: we have moved to the next grapheme within the
                    // current string.
                    self.relative = start..end;
                    self.absolute =
                    return;
                }
                Ok(None) => {
                    // We've reached the end of current string, so let's start anew at the
                    // beginning of the next one.
                    self.strings.move_next();
                    self.cur = 0..0;
                    self.boundaries.set_cursor(0);
                }
                Err(err) => {
                    self.broken = Some(err);
                    return;
                }
            }
        }
        // In case we break out of the loop without returning, we've reached the end of the
        // sequence of underlying strings.
    }

    fn move_prev(&mut self) {
        todo!("")
    }

    fn current(&self) -> Option<&str> {
        self.strings.current().map(|s|&s.as_ref()[self.cur.clone()])
    }

    fn index(&self)->Option<usize>{
        self.
    }
}

impl< T: Cursor> From<T> for GraphemeCursor< T>
where
    T::Item: AsRef<str>,
{
    fn from(strings: T) -> Self {
        let mut self_ = Self {
            strings,
            cur: 0..0,
            boundaries: GraphemeBoundaryCursor::new(0, usize::MAX, true),
            broken: None,
        };
        self_.move_next();
        self_
    }
}

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

    #[test]
    fn moves_forward_through_slice_of_strings() {
        let slice = vec!["a̐éö̲", "", "\r\n"];
        let strs = SliceCursor::from(slice.as_ref());
        let mut graphemes = GraphemeCursor::from(strs);
        assert_eq!(graphemes.current(), Some("a̐"));
        graphemes.move_next();
        assert_eq!(graphemes.current(), Some("é"));
        graphemes.move_next();
        assert_eq!(graphemes.current(), Some("ö̲"));
        // graphemes.move_next();
        //assert_eq!(graphemes.current(),Some("\r\n"));
        // graphemes.move_next();
        //assert_eq!(graphemes.current(),None);
    }
}
*/
<commit_msg>rust/notemaps_text: Text type can be sliced<commit_after>// Copyright 2021-2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing permissions and
// limitations under the License.

//use super::offsets::Byte;
use super::offsets::Grapheme;
//use core::any::Any;
use core::ops::Range;
use std::rc::Rc;
//use core::ops::Deref;
use std::iter;

use crate::*;

/// [Piece] is a `str`-like type that also:
/// - is immutable,
/// - shares a backing buffer for cheap clones and slices,
/// - and measure offsets in user-perceived graphemes instead of bytes or chars.
///
/// [Piece] is intended to be used within a [Text].
///
/// # Examples
///
/// ```
/// use notemaps_text::Piece;
/// use notemaps_text::offsets::Grapheme;
///
/// let piece: Piece = "this is a test".into();
/// let slice: Piece = piece.slice(Grapheme(10)..Grapheme(14));
/// assert_eq!(slice.as_str(), "test");
/// ```
#[derive(Clone, Debug)]
pub struct Piece {
    buffer: Rc<str>,
    byte_range: Range<usize>,
    len_graphemes: Grapheme,
    marks: MarkSet,
}

impl Piece {
    pub fn new(buffer: Rc<str>) -> Self {
        use unicode_segmentation::UnicodeSegmentation;
        let byte_range = 0..buffer.len();
        let len_graphemes = buffer.graphemes(true).count().into();
        Self {
            buffer,
            byte_range,
            len_graphemes,
            marks: MarkSet::new(),
        }
    }

    pub fn slice(&self, r: Range<Grapheme>) -> Self {
        use unicode_segmentation::UnicodeSegmentation;
        let mut graphemes = self
            .as_str()
            .grapheme_indices(true)
            .map(|t| t.0)
            .chain(iter::once(self.as_str().len()));
        let start = graphemes
            .by_ref()
            .nth(*r.start.as_ref())
            .expect("range starts within bounds of this piece");
        let end = if r.is_empty() {
            start
        } else {
            graphemes
                .by_ref()
                .nth(*r.end.as_ref() - 1 - *r.start.as_ref())
                .expect("range ends within bounds of piece")
        };
        Self {
            buffer: self.buffer.clone(),
            byte_range: start..end,
            len_graphemes: (r.end - r.start).into(),
            marks: MarkSet::new(),
        }
    }

    pub fn len(&self) -> Grapheme {
        self.len_graphemes
    }

    pub fn as_str(&self) -> &str {
        &self.buffer[self.byte_range.clone()]
    }

    pub fn marks(&self) -> &MarkSet {
        &self.marks
    }

    pub fn marks_mut(&mut self) -> &mut MarkSet {
        &mut self.marks
    }
}

impl AsRef<str> for Piece {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl<'a> From<&'a str> for Piece {
    fn from(s: &'a str) -> Self {
        Self::new(s.into())
    }
}

#[derive(Clone, Debug)]
struct Table {
    pieces: Vec<Piece>,
    len_graphemes: Grapheme,
}

impl Table {
    pub fn len(&self) -> Grapheme {
        self.len_graphemes
    }
    pub fn mark<M: Any>(&mut self, m: Rc<M>) {
        for piece in &mut self.pieces {
            piece.marks_mut().push(m.clone());
        }
    }
}

impl FromIterator<Piece> for Table {
    fn from_iter<T: IntoIterator<Item = Piece>>(iter: T) -> Self {
        let pieces: Vec<_> = iter.into_iter().collect();
        let len_graphemes = pieces.iter().map(|m| m.len()).sum();
        Self {
            pieces,
            len_graphemes,
        }
    }
}

enum TextInternal {
    Empty,
    Piece(Piece),
    Table(Table),
}

use std::any::Any;

/// [Text] is a "piece chain" or "piece table" using [Piece] to represent a
/// formatted, or "rich-text", document.
///
/// # Examples
///
/// ```rust
/// use notemaps_text::Text;
///
/// let text: Text = ["Hello, world!", "\n"].into_iter().collect();
/// assert_eq!(text.to_string(), "Hello, world!\n");
/// ```
pub struct Text(TextInternal);

impl Text {
    pub fn pieces(&self) -> Pieces {
        Pieces(match &self.0 {
            TextInternal::Empty => PiecesInternal::Empty(iter::empty()),
            TextInternal::Piece(piece) => PiecesInternal::Piece(iter::once(&piece)),
            TextInternal::Table(table) => PiecesInternal::Table(table.pieces.iter()),
        })
    }

    pub fn len(&self) -> Grapheme {
        match &self.0 {
            TextInternal::Empty => Grapheme(0),
            TextInternal::Piece(piece) => piece.len(),
            TextInternal::Table(table) => table.len(),
        }
    }

    pub fn to_string(&self) -> String {
        self.pieces().map(|p| p.as_str()).collect()
    }

    pub fn slice(&self, r: Range<Grapheme>) -> Self {
        if r.end <= r.start {
            return Text(TextInternal::Empty);
        }
        let mut start = r.start;
        let mut all_pieces = self.pieces();
        let mut first_piece = None;
        while let Some(piece) = all_pieces.next() {
            let piece_len = piece.len();
            if piece_len < start {
                start = start - piece_len;
            } else {
                first_piece = Some(piece.slice(start..piece_len));
                break;
            }
        }
        let first_piece = first_piece.expect("text slice range within bounds");
        let mut take = r.end - r.start;
        if first_piece.len() >= take {
            return Self(TextInternal::Piece(first_piece.slice(Grapheme(0)..take)));
        } else {
            take = take - first_piece.len();
        }
        let mut pieces = vec![first_piece];
        for piece in all_pieces {
            if piece.len() < take {
                pieces.push(piece.clone());
            } else {
                pieces.push(piece.slice(Grapheme(0)..take));
                return Self(TextInternal::Table(Table {
                    pieces,
                    len_graphemes: r.end - r.start,
                }));
            }
        }
        panic!("text slice r exceeds length of text");
    }

    pub fn with_insert<I: IntoIterator>(&self, _at: Grapheme, _text: I) -> Self
    where
        Text: FromIterator<I::Item>,
    {
        todo!("");
    }

    pub fn mark<M: Any>(&mut self, m: Rc<M>) {
        match &mut self.0 {
            TextInternal::Empty => {}
            TextInternal::Piece(piece) => {
                piece.marks_mut().push(m);
            }
            TextInternal::Table(table) => {
                table.mark(m);
            }
        }
    }
}

impl FromIterator<Piece> for Text {
    fn from_iter<T: IntoIterator<Item = Piece>>(iter: T) -> Self {
        let mut iter = iter.into_iter();
        Text(match iter.next() {
            Some(piece0) => match iter.next() {
                Some(piece1) => TextInternal::Table(
                    iter::once(piece0)
                        .chain(iter::once(piece1))
                        .chain(iter)
                        .collect(),
                ),
                None => TextInternal::Piece(piece0),
            },
            None => TextInternal::Empty,
        })
    }
}

impl<'a> FromIterator<&'a str> for Text {
    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
        iter.into_iter().map(Piece::from).collect()
    }
}

use core::ops;

impl ops::Add<Self> for Text {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        self.pieces().chain(other.pieces()).cloned().collect()
    }
}

enum PiecesInternal<'a> {
    Empty(std::iter::Empty<&'a Piece>),
    Piece(std::iter::Once<&'a Piece>),
    Table(std::slice::Iter<'a, Piece>),
}

/// [Pieces] is the [Iterator] type returned by [Text::pieces].
pub struct Pieces<'a>(PiecesInternal<'a>);

impl<'a> Iterator for Pieces<'a> {
    type Item = &'a Piece;
    fn next(&mut self) -> Option<Self::Item> {
        match &mut self.0 {
            PiecesInternal::Empty(iter) => iter.next(),
            PiecesInternal::Piece(iter) => iter.next(),
            PiecesInternal::Table(iter) => iter.next(),
        }
    }
}

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

    #[test]
    fn is_made_pieces_pieces() {
        let text = Text::from_iter([Piece::from("a̐éö̲"), Piece::from("\r\n")]);
        assert_eq!(text.to_string(), "a̐éö̲\r\n");
        assert_eq!(text.len(), Grapheme(4));
        assert_eq!(
            text.pieces().map(|p| p.as_str()).collect::<Vec<&str>>(),
            vec!["a̐éö̲", "\r\n"]
        );
    }

    #[test]
    fn can_be_sliced() {
        let text = Text::from_iter([Piece::from("a̐éö̲"), Piece::from("\r\n")]);
        assert_eq!(text.slice(Grapheme(0)..Grapheme(0)).to_string(), "");
        assert_eq!(text.slice(Grapheme(0)..Grapheme(1)).to_string(), "a̐");
        assert_eq!(text.slice(Grapheme(0)..Grapheme(2)).to_string(), "a̐é");
        assert_eq!(text.slice(Grapheme(0)..Grapheme(3)).to_string(), "a̐éö̲");
        assert_eq!(text.slice(Grapheme(0)..Grapheme(4)).to_string(), "a̐éö̲\r\n");
        assert_eq!(text.slice(Grapheme(1)..Grapheme(4)).to_string(), "éö̲\r\n");
        assert_eq!(text.slice(Grapheme(2)..Grapheme(4)).to_string(), "ö̲\r\n");
        assert_eq!(text.slice(Grapheme(3)..Grapheme(4)).to_string(), "\r\n");
        assert_eq!(text.slice(Grapheme(4)..Grapheme(4)).to_string(), "");
    }
}

/*
pub trait Cursor {
    type Item:?Sized;
    fn move_next(&mut self);
    fn move_prev(&mut self);
    fn current(&self) -> Option<&Self::Item>;
    fn index(&self)->Option<usize>;
}

pub struct SliceCursor<'a, T> {
    slice: &'a [T],
    i: usize,
}

impl<'a, T> Cursor for SliceCursor<'a, T> {
    type Item = T;
    fn move_next(&mut self) {
        if self.i < self.slice.len() {
            self.i += 1;
        } else {
            self.i = 0;
        }
    }
    fn move_prev(&mut self) {
        if self.i > 0 {
            self.i -= 1;
        } else {
            self.i = self.slice.len();
        }
    }
    fn current(&self) -> Option<&T> {
        if self.i < self.slice.len() {
            Some(&self.slice[self.i])
        } else {
            None
        }
    }
    fn index(&self)->Option<usize>{
        if self.i<self.slice.len(){
            Some(self.i)
        }else{
            None
        }
    }
}

impl<'a, T> From<&'a [T]> for SliceCursor<'a, T> {
    fn from(slice: &'a [T]) -> Self {
        Self { slice, i: 0 }
    }
}

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

    #[test]
    fn moves_forward_through_a_slice_as_ring() {
        let slice = vec!["red", "blue", "green"];
        let mut cursor = SliceCursor::from(slice.as_ref());
        assert_eq!(cursor.current().copied(), Some("red"));
        cursor.move_next();
        assert_eq!(cursor.current().copied(), Some("blue"));
        cursor.move_next();
        assert_eq!(cursor.current().copied(), Some("green"));
        cursor.move_next();
        assert_eq!(cursor.current(), None);
        cursor.move_next();
        assert_eq!(cursor.current().copied(), Some("red"));
    }

    #[test]
    fn moves_backward_through_a_slice_as_ring() {
        let slice = vec!["red", "blue", "green"];
        let mut cursor = SliceCursor::from(slice.as_ref());
        assert_eq!(cursor.current().copied(), Some("red"));
        cursor.move_prev();
        assert_eq!(cursor.current(), None);
        cursor.move_prev();
        assert_eq!(cursor.current().copied(), Some("green"));
        cursor.move_prev();
        assert_eq!(cursor.current().copied(), Some("blue"));
    }
}

use core::ops::Range;
use unicode_segmentation::GraphemeCursor as GraphemeBoundaryCursor;
use unicode_segmentation::GraphemeIncomplete;
//use std::borrow::Cow;

pub struct GraphemeCursor< T> {
    //'a, T:Cursor<'a,U >, U:AsRef<str>> {
    strings: T,
    absolute: Range<usize>,
    relative: Range<usize>,
    boundaries: GraphemeBoundaryCursor,
    broken: Option<GraphemeIncomplete>,
}

impl< T: Cursor> GraphemeCursor< T> {}

impl< T: Cursor> Cursor for GraphemeCursor< T>
where
    T::Item: AsRef<str>,
{
    type Item = str;

    fn move_next(&mut self) {
        //if self.strings.current().is_none() { self.strings.move_next(); self.start = 0; self.boundaries.set_cursor(0); self.broken = None; }
        while let Some(s) = self.strings.current().map(|s|s.as_ref()) {
            let start = self.boundaries.cur_cursor();
            match self.boundaries.next_boundary(s, 0) {
                Ok(Some(end)) => {
                    // This is the typical case: we have moved to the next grapheme within the
                    // current string.
                    self.relative = start..end;
                    self.absolute =
                    return;
                }
                Ok(None) => {
                    // We've reached the end of current string, so let's start anew at the
                    // beginning of the next one.
                    self.strings.move_next();
                    self.cur = 0..0;
                    self.boundaries.set_cursor(0);
                }
                Err(err) => {
                    self.broken = Some(err);
                    return;
                }
            }
        }
        // In case we break out of the loop without returning, we've reached the end of the
        // sequence of underlying strings.
    }

    fn move_prev(&mut self) {
        todo!("")
    }

    fn current(&self) -> Option<&str> {
        self.strings.current().map(|s|&s.as_ref()[self.cur.clone()])
    }

    fn index(&self)->Option<usize>{
        self.
    }
}

impl< T: Cursor> From<T> for GraphemeCursor< T>
where
    T::Item: AsRef<str>,
{
    fn from(strings: T) -> Self {
        let mut self_ = Self {
            strings,
            cur: 0..0,
            boundaries: GraphemeBoundaryCursor::new(0, usize::MAX, true),
            broken: None,
        };
        self_.move_next();
        self_
    }
}

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

    #[test]
    fn moves_forward_through_slice_of_strings() {
        let slice = vec!["a̐éö̲", "", "\r\n"];
        let strs = SliceCursor::from(slice.as_ref());
        let mut graphemes = GraphemeCursor::from(strs);
        assert_eq!(graphemes.current(), Some("a̐"));
        graphemes.move_next();
        assert_eq!(graphemes.current(), Some("é"));
        graphemes.move_next();
        assert_eq!(graphemes.current(), Some("ö̲"));
        // graphemes.move_next();
        //assert_eq!(graphemes.current(),Some("\r\n"));
        // graphemes.move_next();
        //assert_eq!(graphemes.current(),None);
    }
}
*/
<|endoftext|>


<commit_before>use super::*;

use std;
use std::fmt::{Debug, Display, Formatter};
use std::ops::*;

/// Represents an element of finite field.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
struct Finite(i64);

impl Finite {
    fn pow(self, e: i64) -> Self {
        pow(self.0, e).into()
    }
}

impl Debug for Finite {
    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
        formatter.write_fmt(format_args!("{:?}", self.0))
    }
}

impl Display for Finite {
    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
        formatter.write_fmt(format_args!("{}", self.0))
    }
}

impl From<i64> for Finite {
    fn from(value: i64) -> Self {
        Finite((value % P + P) % P)
    }
}

// Generate binary operation traits.
macro_rules! impl_binary_op_for_finite {
    ($op_trait:ident, $op:ident, $assign_trait:ident, $assign:ident $(, $f:ident)*) => {
        $(impl $op_trait<Finite> for Finite {
            type Output = Self;

            fn $op(self, other: Self) -> Self {
                Finite::from((self.0).$f(other.0))
            }
        })*

        impl $op_trait<i64> for Finite {
            type Output = Self;

            fn $op(self, other: i64) -> Self {
                self.$op(Finite::from(other))
            }
        }

        impl $assign_trait<Finite> for Finite {
            fn $assign(&mut self, other: Self) {
                *self = self.$op(other)
            }
        }

        impl $assign_trait<i64> for Finite {
            fn $assign(&mut self, other: i64) {
                *self = self.$op(other)
            }
        }
    };
}

impl_binary_op_for_finite! {Add, add, AddAssign, add_assign, add}
impl_binary_op_for_finite! {Sub, sub, SubAssign, sub_assign, sub}
impl_binary_op_for_finite! {Mul, mul, MulAssign, mul_assign, mul}
impl_binary_op_for_finite! {Div, div, DivAssign, div_assign}

impl Div<Finite> for Finite {
    type Output = Finite;

    fn div(self, other: Self) -> Self {
        self * other.pow(P - 2)
    }
}

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

    #[test]
    fn test_finite() {
        let x = Finite::from(P + 2);

        // `from` should normalize the value.
        assert_eq!(x.0, 2);

        // Operations.
        assert_eq!(x + 7, (2 + 7).into());
        assert_eq!(x - 7, (P + (2 - 7)).into());
        assert_eq!(x * 3, (2 * 3).into());
        assert_eq!((x / 11) * 11, 2.into());

        assert_eq!(x + Finite::from(7), (2 + 7).into());
        assert_eq!(x - Finite::from(7), (P + (2 - 7)).into());
        assert_eq!(x * Finite::from(3), (2 * 3).into());
        assert_eq!((x / Finite::from(11)) * 11, 2.into());

        let mut x = x;
        x += 7;
        assert_eq!(x, 9.into());
        x -= 5;
        assert_eq!(x, 4.into());
        x *= 6;
        assert_eq!(x, 24.into());
        x /= 3;
        assert_eq!(x, 8.into());
    }

    #[test]
    fn test_finite_fmt() {
        assert_eq!(format!("{:?}", Finite::from(2)), "2");
        assert_eq!(format!("{}", Finite::from(2)), "2");
    }
}
<commit_msg>refactor: Rename Finite to ModInt<commit_after>use super::*;

use std;
use std::fmt::{Debug, Display, Formatter};
use std::ops::*;

/// Represents an element of ModInt field.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
struct ModInt(i64);

impl ModInt {
    fn pow(self, e: i64) -> Self {
        pow(self.0, e).into()
    }
}

impl Debug for ModInt {
    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
        formatter.write_fmt(format_args!("{:?}", self.0))
    }
}

impl Display for ModInt {
    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
        formatter.write_fmt(format_args!("{}", self.0))
    }
}

impl From<i64> for ModInt {
    fn from(value: i64) -> Self {
        ModInt((value % P + P) % P)
    }
}

// Generate binary operation traits.
macro_rules! impl_binary_op_for_modint {
    ($op_trait:ident, $op:ident, $assign_trait:ident, $assign:ident $(, $f:ident)*) => {
        $(impl $op_trait<ModInt> for ModInt {
            type Output = Self;

            fn $op(self, other: Self) -> Self {
                ModInt::from((self.0).$f(other.0))
            }
        })*

        impl $op_trait<i64> for ModInt {
            type Output = Self;

            fn $op(self, other: i64) -> Self {
                self.$op(ModInt::from(other))
            }
        }

        impl $assign_trait<ModInt> for ModInt {
            fn $assign(&mut self, other: Self) {
                *self = self.$op(other)
            }
        }

        impl $assign_trait<i64> for ModInt {
            fn $assign(&mut self, other: i64) {
                *self = self.$op(other)
            }
        }
    };
}

impl_binary_op_for_modint! {Add, add, AddAssign, add_assign, add}
impl_binary_op_for_modint! {Sub, sub, SubAssign, sub_assign, sub}
impl_binary_op_for_modint! {Mul, mul, MulAssign, mul_assign, mul}
impl_binary_op_for_modint! {Div, div, DivAssign, div_assign}

impl Div<ModInt> for ModInt {
    type Output = ModInt;

    fn div(self, other: Self) -> Self {
        self * other.pow(P - 2)
    }
}

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

    #[test]
    fn test_ops() {
        let x = ModInt::from(P + 2);

        // `from` should normalize the value.
        assert_eq!(x.0, 2);

        // Operations.
        assert_eq!(x + 7, (2 + 7).into());
        assert_eq!(x - 7, (P + (2 - 7)).into());
        assert_eq!(x * 3, (2 * 3).into());
        assert_eq!((x / 11) * 11, 2.into());

        assert_eq!(x + ModInt::from(7), (2 + 7).into());
        assert_eq!(x - ModInt::from(7), (P + (2 - 7)).into());
        assert_eq!(x * ModInt::from(3), (2 * 3).into());
        assert_eq!((x / ModInt::from(11)) * 11, 2.into());

        let mut x = x;
        x += 7;
        assert_eq!(x, 9.into());
        x -= 5;
        assert_eq!(x, 4.into());
        x *= 6;
        assert_eq!(x, 24.into());
        x /= 3;
        assert_eq!(x, 8.into());
    }

    #[test]
    fn test_fmt() {
        assert_eq!(format!("{:?}", ModInt::from(2)), "2");
        assert_eq!(format!("{}", ModInt::from(2)), "2");
    }
}
<|endoftext|>


<commit_before>//! # The Rust Core Library
//!
//! The Rust Core Library is the dependency-free[^free] foundation of [The
//! Rust Standard Library](../std/index.html). It is the portable glue
//! between the language and its libraries, defining the intrinsic and
//! primitive building blocks of all Rust code. It links to no
//! upstream libraries, no system libraries, and no libc.
//!
//! [^free]: Strictly speaking, there are some symbols which are needed but
//!          they aren't always necessary.
//!
//! The core library is *minimal*: it isn't even aware of heap allocation,
//! nor does it provide concurrency or I/O. These things require
//! platform integration, and this library is platform-agnostic.
//!
//! # How to use the core library
//!
//! Please note that all of these details are currently not considered stable.
//!
// FIXME: Fill me in with more detail when the interface settles
//! This library is built on the assumption of a few existing symbols:
//!
//! * `memcpy`, `memcmp`, `memset` - These are core memory routines which are
//!   often generated by LLVM. Additionally, this library can make explicit
//!   calls to these functions. Their signatures are the same as found in C.
//!   These functions are often provided by the system libc, but can also be
//!   provided by the [compiler-builtins crate](https://crates.io/crates/compiler_builtins).
//!
//! * `rust_begin_panic` - This function takes four arguments, a
//!   `fmt::Arguments`, a `&'static str`, and two `u32`'s. These four arguments
//!   dictate the panic message, the file at which panic was invoked, and the
//!   line and column inside the file. It is up to consumers of this core
//!   library to define this panic function; it is only required to never
//!   return. This requires a `lang` attribute named `panic_impl`.
//!
//! * `rust_eh_personality` - is used by the failure mechanisms of the
//!    compiler. This is often mapped to GCC's personality function, but crates
//!    which do not trigger a panic can be assured that this function is never
//!    called. The `lang` attribute is called `eh_personality`.

// Since libcore defines many fundamental lang items, all tests live in a
// separate crate, libcoretest, to avoid bizarre issues.
//
// Here we explicitly #[cfg]-out this whole crate when testing. If we don't do
// this, both the generated test artifact and the linked libtest (which
// transitively includes libcore) will both define the same set of lang items,
// and this will cause the E0152 "found duplicate lang item" error. See
// discussion in #50466 for details.
//
// This cfg won't affect doc tests.
#![cfg(not(test))]
// To run libcore tests without x.py without ending up with two copies of libcore, Miri needs to be
// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
// rustc itself never sets the feature, so this line has no affect there.
#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
#![stable(feature = "core", since = "1.6.0")]
#![doc(
    html_playground_url = "https://play.rust-lang.org/",
    issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
    test(no_crate_inject, attr(deny(warnings))),
    test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
#![no_core]
#![warn(deprecated_in_future)]
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
#![allow(explicit_outlives_requirements)]
#![feature(rustc_allow_const_fn_unstable)]
#![feature(allow_internal_unstable)]
#![feature(arbitrary_self_types)]
#![feature(asm)]
#![feature(bool_to_option)]
#![feature(cfg_target_has_atomic)]
#![feature(const_heap)]
#![feature(const_alloc_layout)]
#![feature(const_arguments_as_str)]
#![feature(const_assert_type)]
#![feature(const_discriminant)]
#![feature(const_cell_into_inner)]
#![feature(const_intrinsic_copy)]
#![feature(const_intrinsic_forget)]
#![feature(const_float_classify)]
#![feature(const_float_bits_conv)]
#![feature(const_int_unchecked_arith)]
#![feature(const_inherent_unchecked_arith)]
#![feature(const_mut_refs)]
#![feature(const_refs_to_cell)]
#![feature(const_panic)]
#![feature(const_pin)]
#![cfg_attr(bootstrap, feature(const_fn_union))]
#![feature(const_impl_trait)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(const_fn_trait_bound)]
#![feature(const_option)]
#![feature(const_precise_live_drops)]
#![feature(const_ptr_offset)]
#![feature(const_ptr_offset_from)]
#![feature(const_ptr_read)]
#![feature(const_ptr_write)]
#![feature(const_raw_ptr_comparison)]
#![feature(const_raw_ptr_deref)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_slice_ptr_len)]
#![feature(const_size_of_val)]
#![feature(const_swap)]
#![feature(const_align_of_val)]
#![feature(const_type_id)]
#![feature(const_type_name)]
#![feature(const_likely)]
#![feature(const_unreachable_unchecked)]
#![feature(const_maybe_uninit_assume_init)]
#![feature(const_maybe_uninit_as_ptr)]
#![feature(custom_inner_attributes)]
#![feature(decl_macro)]
#![feature(doc_cfg)]
#![feature(doc_notable_trait)]
#![feature(duration_consts_2)]
#![feature(extern_types)]
#![feature(fundamental)]
#![feature(intra_doc_pointers)]
#![feature(intrinsics)]
#![feature(lang_items)]
#![feature(link_llvm_intrinsics)]
#![feature(llvm_asm)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(nll)]
#![feature(exhaustive_patterns)]
#![feature(no_core)]
#![feature(auto_traits)]
#![feature(pin_deref_mut)]
#![feature(prelude_import)]
#![feature(ptr_metadata)]
#![feature(repr_simd, platform_intrinsics)]
#![feature(rustc_attrs)]
#![feature(simd_ffi)]
#![feature(min_specialization)]
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(stmt_expr_attributes)]
#![feature(str_split_as_str)]
#![feature(str_split_inclusive_as_str)]
#![feature(char_indices_offset)]
#![feature(trait_alias)]
#![feature(transparent_unions)]
#![feature(try_blocks)]
#![feature(unboxed_closures)]
#![feature(unsized_fn_params)]
#![feature(variant_count)]
#![feature(tbm_target_feature)]
#![feature(sse4a_target_feature)]
#![feature(arm_target_feature)]
#![feature(powerpc_target_feature)]
#![feature(mips_target_feature)]
#![feature(aarch64_target_feature)]
#![feature(wasm_target_feature)]
#![feature(avx512_target_feature)]
#![feature(cmpxchg16b_target_feature)]
#![feature(rtm_target_feature)]
#![feature(f16c_target_feature)]
#![feature(hexagon_target_feature)]
#![cfg_attr(bootstrap, feature(const_fn_transmute))]
#![feature(abi_unadjusted)]
#![feature(adx_target_feature)]
#![feature(associated_type_bounds)]
#![feature(const_caller_location)]
#![feature(slice_ptr_get)]
#![feature(no_niche)] // rust-lang/rust#68303
#![feature(no_coverage)] // rust-lang/rust#84605
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(rust_2021_incompatible_or_patterns)]

// allow using `core::` in intra-doc links
#[allow(unused_extern_crates)]
extern crate self as core;

#[prelude_import]
#[allow(unused)]
use prelude::v1::*;

#[cfg(not(test))] // See #65860
#[macro_use]
mod macros;

// We don't export this through #[macro_export] for now, to avoid breakage.
// See https://github.com/rust-lang/rust/issues/82913
#[cfg(not(test))]
#[unstable(feature = "assert_matches", issue = "82775")]
/// Unstable module containing the unstable `assert_matches` macro.
pub mod assert_matches {
    #[unstable(feature = "assert_matches", issue = "82775")]
    pub use crate::macros::{assert_matches, debug_assert_matches};
}

#[macro_use]
mod internal_macros;

#[path = "num/shells/int_macros.rs"]
#[macro_use]
mod int_macros;

#[path = "num/shells/i128.rs"]
pub mod i128;
#[path = "num/shells/i16.rs"]
pub mod i16;
#[path = "num/shells/i32.rs"]
pub mod i32;
#[path = "num/shells/i64.rs"]
pub mod i64;
#[path = "num/shells/i8.rs"]
pub mod i8;
#[path = "num/shells/isize.rs"]
pub mod isize;

#[path = "num/shells/u128.rs"]
pub mod u128;
#[path = "num/shells/u16.rs"]
pub mod u16;
#[path = "num/shells/u32.rs"]
pub mod u32;
#[path = "num/shells/u64.rs"]
pub mod u64;
#[path = "num/shells/u8.rs"]
pub mod u8;
#[path = "num/shells/usize.rs"]
pub mod usize;

#[path = "num/f32.rs"]
pub mod f32;
#[path = "num/f64.rs"]
pub mod f64;

#[macro_use]
pub mod num;

/* The libcore prelude, not as all-encompassing as the libstd prelude */

pub mod prelude;

/* Core modules for ownership management */

pub mod hint;
pub mod intrinsics;
pub mod mem;
pub mod ptr;

/* Core language traits */

pub mod borrow;
pub mod clone;
pub mod cmp;
pub mod convert;
pub mod default;
pub mod marker;
pub mod ops;

/* Core types and methods on primitives */

pub mod any;
pub mod array;
pub mod ascii;
pub mod cell;
pub mod char;
pub mod ffi;
pub mod iter;
#[unstable(feature = "once_cell", issue = "74465")]
pub mod lazy;
pub mod option;
pub mod panic;
pub mod panicking;
pub mod pin;
pub mod result;
#[unstable(feature = "async_stream", issue = "79024")]
pub mod stream;
pub mod sync;

pub mod fmt;
pub mod hash;
pub mod slice;
pub mod str;
pub mod time;

pub mod unicode;

/* Async */
pub mod future;
pub mod task;

/* Heap memory allocator trait */
#[allow(missing_docs)]
pub mod alloc;

// note: does not need to be public
mod bool;
mod tuple;
mod unit;

#[stable(feature = "core_primitive", since = "1.43.0")]
pub mod primitive;

// Pull in the `core_arch` crate directly into libcore. The contents of
// `core_arch` are in a different repository: rust-lang/stdarch.
//
// `core_arch` depends on libcore, but the contents of this module are
// set up in such a way that directly pulling it here works such that the
// crate uses the this crate as its libcore.
#[path = "../../stdarch/crates/core_arch/src/mod.rs"]
#[allow(
    missing_docs,
    missing_debug_implementations,
    dead_code,
    unused_imports,
    unsafe_op_in_unsafe_fn
)]
#[allow(rustdoc::bare_urls)]
// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is
// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet.
#[allow(clashing_extern_declarations)]
#[unstable(feature = "stdsimd", issue = "48556")]
mod core_arch;

#[doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")]
#[stable(feature = "simd_arch", since = "1.27.0")]
pub mod arch {
    #[stable(feature = "simd_arch", since = "1.27.0")]
    pub use crate::core_arch::arch::*;

    /// Inline assembly.
    ///
    /// Read the [unstable book] for the usage.
    ///
    /// [unstable book]: ../../unstable-book/library-features/asm.html
    #[unstable(
        feature = "asm",
        issue = "72016",
        reason = "inline assembly is not stable enough for use and is subject to change"
    )]
    #[rustc_builtin_macro]
    pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
        /* compiler built-in */
    }

    /// Module-level inline assembly.
    #[unstable(
        feature = "global_asm",
        issue = "35119",
        reason = "`global_asm!` is not stable enough for use and is subject to change"
    )]
    #[rustc_builtin_macro]
    pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) {
        /* compiler built-in */
    }
}
<commit_msg>Sort and categorize lint and feature attributes in core.<commit_after>//! # The Rust Core Library
//!
//! The Rust Core Library is the dependency-free[^free] foundation of [The
//! Rust Standard Library](../std/index.html). It is the portable glue
//! between the language and its libraries, defining the intrinsic and
//! primitive building blocks of all Rust code. It links to no
//! upstream libraries, no system libraries, and no libc.
//!
//! [^free]: Strictly speaking, there are some symbols which are needed but
//!          they aren't always necessary.
//!
//! The core library is *minimal*: it isn't even aware of heap allocation,
//! nor does it provide concurrency or I/O. These things require
//! platform integration, and this library is platform-agnostic.
//!
//! # How to use the core library
//!
//! Please note that all of these details are currently not considered stable.
//!
// FIXME: Fill me in with more detail when the interface settles
//! This library is built on the assumption of a few existing symbols:
//!
//! * `memcpy`, `memcmp`, `memset` - These are core memory routines which are
//!   often generated by LLVM. Additionally, this library can make explicit
//!   calls to these functions. Their signatures are the same as found in C.
//!   These functions are often provided by the system libc, but can also be
//!   provided by the [compiler-builtins crate](https://crates.io/crates/compiler_builtins).
//!
//! * `rust_begin_panic` - This function takes four arguments, a
//!   `fmt::Arguments`, a `&'static str`, and two `u32`'s. These four arguments
//!   dictate the panic message, the file at which panic was invoked, and the
//!   line and column inside the file. It is up to consumers of this core
//!   library to define this panic function; it is only required to never
//!   return. This requires a `lang` attribute named `panic_impl`.
//!
//! * `rust_eh_personality` - is used by the failure mechanisms of the
//!    compiler. This is often mapped to GCC's personality function, but crates
//!    which do not trigger a panic can be assured that this function is never
//!    called. The `lang` attribute is called `eh_personality`.

// Since libcore defines many fundamental lang items, all tests live in a
// separate crate, libcoretest, to avoid bizarre issues.
//
// Here we explicitly #[cfg]-out this whole crate when testing. If we don't do
// this, both the generated test artifact and the linked libtest (which
// transitively includes libcore) will both define the same set of lang items,
// and this will cause the E0152 "found duplicate lang item" error. See
// discussion in #50466 for details.
//
// This cfg won't affect doc tests.
#![cfg(not(test))]
// To run libcore tests without x.py without ending up with two copies of libcore, Miri needs to be
// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
// rustc itself never sets the feature, so this line has no affect there.
#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
#![stable(feature = "core", since = "1.6.0")]
#![doc(
    html_playground_url = "https://play.rust-lang.org/",
    issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
    test(no_crate_inject, attr(deny(warnings))),
    test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
#![no_core]

// Lints:
#![deny(rust_2021_incompatible_or_patterns)]
#![deny(unsafe_op_in_unsafe_fn)]
#![warn(deprecated_in_future)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![allow(explicit_outlives_requirements)]

// Library features:
#![feature(bool_to_option)]
#![feature(char_indices_offset)]
#![feature(const_align_of_val)]
#![feature(const_alloc_layout)]
#![feature(const_arguments_as_str)]
#![feature(const_assert_type)]
#![feature(const_caller_location)]
#![feature(const_cell_into_inner)]
#![feature(const_discriminant)]
#![feature(const_float_bits_conv)]
#![feature(const_float_classify)]
#![feature(const_heap)]
#![feature(const_inherent_unchecked_arith)]
#![feature(const_int_unchecked_arith)]
#![feature(const_intrinsic_copy)]
#![feature(const_intrinsic_forget)]
#![feature(const_likely)]
#![feature(const_maybe_uninit_as_ptr)]
#![feature(const_maybe_uninit_assume_init)]
#![feature(const_option)]
#![feature(const_pin)]
#![feature(const_ptr_offset)]
#![feature(const_ptr_offset_from)]
#![feature(const_ptr_read)]
#![feature(const_ptr_write)]
#![feature(const_raw_ptr_comparison)]
#![feature(const_size_of_val)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_slice_ptr_len)]
#![feature(const_swap)]
#![feature(const_type_id)]
#![feature(const_type_name)]
#![feature(const_unreachable_unchecked)]
#![feature(duration_consts_2)]
#![feature(pin_deref_mut)]
#![feature(ptr_metadata)]
#![feature(slice_ptr_get)]
#![feature(std_internals)]
#![feature(str_split_as_str)]
#![feature(str_split_inclusive_as_str)]
#![feature(variant_count)]

// Language features:
#![feature(abi_unadjusted)]
#![feature(allow_internal_unstable)]
#![feature(arbitrary_self_types)]
#![feature(asm)]
#![feature(associated_type_bounds)]
#![feature(auto_traits)]
#![feature(cfg_target_has_atomic)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(const_fn_trait_bound)]
#![cfg_attr(bootstrap, feature(const_fn_transmute))]
#![cfg_attr(bootstrap, feature(const_fn_union))]
#![feature(const_impl_trait)]
#![feature(const_mut_refs)]
#![feature(const_panic)]
#![feature(const_precise_live_drops)]
#![feature(const_raw_ptr_deref)]
#![feature(const_refs_to_cell)]
#![feature(custom_inner_attributes)]
#![feature(decl_macro)]
#![feature(doc_cfg)]
#![feature(doc_notable_trait)]
#![feature(exhaustive_patterns)]
#![feature(extern_types)]
#![feature(fundamental)]
#![feature(intra_doc_pointers)]
#![feature(intrinsics)]
#![feature(lang_items)]
#![feature(link_llvm_intrinsics)]
#![feature(llvm_asm)]
#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(nll)]
#![feature(no_core)]
#![feature(no_coverage)] // rust-lang/rust#84605
#![feature(no_niche)] // rust-lang/rust#68303
#![feature(platform_intrinsics)]
#![feature(prelude_import)]
#![feature(repr_simd)]
#![feature(rustc_allow_const_fn_unstable)]
#![feature(rustc_attrs)]
#![feature(simd_ffi)]
#![feature(staged_api)]
#![feature(stmt_expr_attributes)]
#![feature(trait_alias)]
#![feature(transparent_unions)]
#![feature(try_blocks)]
#![feature(unboxed_closures)]
#![feature(unsized_fn_params)]

// Target features:
#![feature(aarch64_target_feature)]
#![feature(adx_target_feature)]
#![feature(arm_target_feature)]
#![feature(avx512_target_feature)]
#![feature(cmpxchg16b_target_feature)]
#![feature(f16c_target_feature)]
#![feature(hexagon_target_feature)]
#![feature(mips_target_feature)]
#![feature(powerpc_target_feature)]
#![feature(rtm_target_feature)]
#![feature(sse4a_target_feature)]
#![feature(tbm_target_feature)]
#![feature(wasm_target_feature)]

// allow using `core::` in intra-doc links
#[allow(unused_extern_crates)]
extern crate self as core;

#[prelude_import]
#[allow(unused)]
use prelude::v1::*;

#[cfg(not(test))] // See #65860
#[macro_use]
mod macros;

// We don't export this through #[macro_export] for now, to avoid breakage.
// See https://github.com/rust-lang/rust/issues/82913
#[cfg(not(test))]
#[unstable(feature = "assert_matches", issue = "82775")]
/// Unstable module containing the unstable `assert_matches` macro.
pub mod assert_matches {
    #[unstable(feature = "assert_matches", issue = "82775")]
    pub use crate::macros::{assert_matches, debug_assert_matches};
}

#[macro_use]
mod internal_macros;

#[path = "num/shells/int_macros.rs"]
#[macro_use]
mod int_macros;

#[path = "num/shells/i128.rs"]
pub mod i128;
#[path = "num/shells/i16.rs"]
pub mod i16;
#[path = "num/shells/i32.rs"]
pub mod i32;
#[path = "num/shells/i64.rs"]
pub mod i64;
#[path = "num/shells/i8.rs"]
pub mod i8;
#[path = "num/shells/isize.rs"]
pub mod isize;

#[path = "num/shells/u128.rs"]
pub mod u128;
#[path = "num/shells/u16.rs"]
pub mod u16;
#[path = "num/shells/u32.rs"]
pub mod u32;
#[path = "num/shells/u64.rs"]
pub mod u64;
#[path = "num/shells/u8.rs"]
pub mod u8;
#[path = "num/shells/usize.rs"]
pub mod usize;

#[path = "num/f32.rs"]
pub mod f32;
#[path = "num/f64.rs"]
pub mod f64;

#[macro_use]
pub mod num;

/* The libcore prelude, not as all-encompassing as the libstd prelude */

pub mod prelude;

/* Core modules for ownership management */

pub mod hint;
pub mod intrinsics;
pub mod mem;
pub mod ptr;

/* Core language traits */

pub mod borrow;
pub mod clone;
pub mod cmp;
pub mod convert;
pub mod default;
pub mod marker;
pub mod ops;

/* Core types and methods on primitives */

pub mod any;
pub mod array;
pub mod ascii;
pub mod cell;
pub mod char;
pub mod ffi;
pub mod iter;
#[unstable(feature = "once_cell", issue = "74465")]
pub mod lazy;
pub mod option;
pub mod panic;
pub mod panicking;
pub mod pin;
pub mod result;
#[unstable(feature = "async_stream", issue = "79024")]
pub mod stream;
pub mod sync;

pub mod fmt;
pub mod hash;
pub mod slice;
pub mod str;
pub mod time;

pub mod unicode;

/* Async */
pub mod future;
pub mod task;

/* Heap memory allocator trait */
#[allow(missing_docs)]
pub mod alloc;

// note: does not need to be public
mod bool;
mod tuple;
mod unit;

#[stable(feature = "core_primitive", since = "1.43.0")]
pub mod primitive;

// Pull in the `core_arch` crate directly into libcore. The contents of
// `core_arch` are in a different repository: rust-lang/stdarch.
//
// `core_arch` depends on libcore, but the contents of this module are
// set up in such a way that directly pulling it here works such that the
// crate uses the this crate as its libcore.
#[path = "../../stdarch/crates/core_arch/src/mod.rs"]
#[allow(
    missing_docs,
    missing_debug_implementations,
    dead_code,
    unused_imports,
    unsafe_op_in_unsafe_fn
)]
#[allow(rustdoc::bare_urls)]
// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is
// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet.
#[allow(clashing_extern_declarations)]
#[unstable(feature = "stdsimd", issue = "48556")]
mod core_arch;

#[doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")]
#[stable(feature = "simd_arch", since = "1.27.0")]
pub mod arch {
    #[stable(feature = "simd_arch", since = "1.27.0")]
    pub use crate::core_arch::arch::*;

    /// Inline assembly.
    ///
    /// Read the [unstable book] for the usage.
    ///
    /// [unstable book]: ../../unstable-book/library-features/asm.html
    #[unstable(
        feature = "asm",
        issue = "72016",
        reason = "inline assembly is not stable enough for use and is subject to change"
    )]
    #[rustc_builtin_macro]
    pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
        /* compiler built-in */
    }

    /// Module-level inline assembly.
    #[unstable(
        feature = "global_asm",
        issue = "35119",
        reason = "`global_asm!` is not stable enough for use and is subject to change"
    )]
    #[rustc_builtin_macro]
    pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) {
        /* compiler built-in */
    }
}
<|endoftext|>


<commit_before>use azure::cosmos::authorization_token::{TokenType, AuthorizationToken};
use azure::core::HTTPMethod;

use azure::cosmos::database::Database;

use azure::core::errors::{AzureError, check_status_extract_body, check_status};

use azure::cosmos::request_response::{ListDatabasesResponse, CreateDatabaseRequest};

use url;

use std::io::{Read, Cursor};

use crypto::hmac::Hmac;
use crypto::mac::Mac;
use crypto::sha2::Sha256;

use base64;
use hyper;
use serde_json;
use hyper::header::{ContentLength, Headers};
use hyper::status::StatusCode;
use hyper_native_tls;

use chrono;

use url::percent_encoding::utf8_percent_encode;

const AZURE_VERSION: &'static str = "2017-02-22";
const VERSION: &'static str = "1.0";
const TIME_FORMAT: &'static str = "%a, %d %h %Y %T GMT";

header! { (XMSVersion, "x-ms-version") => [String] }
header! { (XMSDate, "x-ms-date") => [String] }
header! { (Authorization, "Authorization") => [String] }

define_encode_set! {
    pub COMPLETE_ENCODE_SET = [url::percent_encoding::USERINFO_ENCODE_SET] | {
        '+', '-', '&'
    }
}

#[derive(Clone,Copy)]
pub enum ResourceType {
    Databases,
    Collections,
    Documents,
}

pub struct Client<'a> {
    hyper_client: hyper::client::Client,
    authorization_token: &'a AuthorizationToken<'a>,
}

impl<'a> Client<'a> {
    pub fn new(authorization_token: &'a AuthorizationToken<'a>)
               -> Result<Client<'a>, hyper_native_tls::native_tls::Error> {
        let ssl = hyper_native_tls::NativeTlsClient::new()?;
        let connector = hyper::net::HttpsConnector::new(ssl);
        let client = hyper::Client::with_connector(connector);

        Ok(Client {
               hyper_client: client,
               authorization_token: authorization_token,
           })
    }

    pub fn set_authorization_token(&mut self, at: &'a AuthorizationToken<'a>) {
        self.authorization_token = at;
    }

    fn perform_request(&self,
                       url: &url::Url,
                       http_method: HTTPMethod,
                       request_body: Option<(&mut Read, u64)>,
                       resource_type: ResourceType,
                       headers: Option<Headers>)
                       -> Result<hyper::client::Response, AzureError> {
        let dt = chrono::UTC::now();
        let time = format!("{}", dt.format(TIME_FORMAT));

        // to do: calculate resource link
        let resource_link = generate_resource_link(url);

        let auth = generate_authorization(self.authorization_token,
                                          http_method,
                                          resource_type,
                                          resource_link,
                                          &time);
        trace!("perform_request::auth == {:?}", auth);

        // we need to add custom headers. If the caller has passed its collection of
        // headers we will add to his. Otherwise we create one from scratch.
        let mut headers = if let Some(h) = headers {
            h
        } else {
            Headers::new()
        };

        if let Some((_, size)) = request_body {
            headers.set(ContentLength(size));
        }

        headers.set(XMSDate(time));
        headers.set(XMSVersion(AZURE_VERSION.to_owned()));
        headers.set(Authorization(auth));

        trace!("perform_request::headers == {:?}", headers);

        let mut builder = match http_method {
            HTTPMethod::Get => self.hyper_client.get(&url.to_string()),
            HTTPMethod::Put => self.hyper_client.put(&url.to_string()),
            HTTPMethod::Post => self.hyper_client.post(&url.to_string()),
            HTTPMethod::Delete => self.hyper_client.delete(&url.to_string()),
        };

        if let Some((mut rb, size)) = request_body {
            let b = hyper::client::Body::SizedBody(rb, size);
            builder = builder.body(b);
        }

        let res = builder.headers(headers).send()?;

        Ok(res)
    }

    pub fn list_databases(&self) -> Result<Vec<Database>, AzureError> {
        trace!("list_databases called");

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs",
                                          self.authorization_token.account()))
                .unwrap();

        // No specific headers are required, list databases only needs standard headers
        // which will be provied by perform_request
        let mut resp =
            self.perform_request(&url, HTTPMethod::Get, None, ResourceType::Databases, None)?;

        let body = check_status_extract_body(&mut resp, StatusCode::Ok)?;
        let db: ListDatabasesResponse = serde_json::from_str(&body)?;

        Ok(db.databases)
    }

    pub fn create_database(&self, database_name: &str) -> Result<Database, AzureError> {
        trace!("create_databases called (database_name == {})",
               database_name);

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs",
                                          self.authorization_token.account()))
                .unwrap();

        // No specific headers are required, create databases only needs standard headers
        // which will be provied by perform_request
        // for the body, we will serialize the appropriate structure

        let req = CreateDatabaseRequest { id: database_name };
        let req = serde_json::to_string(&req)?;
        let mut curs = Cursor::new(&req);

        let mut resp = self.perform_request(&url,
                                            HTTPMethod::Post,
                                            Some((&mut curs, req.len() as u64)),
                                            ResourceType::Databases,
                                            None)?;

        let body = check_status_extract_body(&mut resp, StatusCode::Created)?;
        let db: Database = serde_json::from_str(&body)?;

        Ok(db)
    }

    pub fn get_database(&self, database_name: &str) -> Result<Database, AzureError> {
        trace!("get_database called (database_name == {})", database_name);

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs/{}",
                                          self.authorization_token.account(),
                                          database_name))
                .unwrap();

        // No specific headers are required, get database only needs standard headers
        // which will be provied by perform_request

        let mut resp =
            self.perform_request(&url, HTTPMethod::Get, None, ResourceType::Databases, None)?;

        let body = check_status_extract_body(&mut resp, StatusCode::Ok)?;
        let db: Database = serde_json::from_str(&body)?;

        Ok(db)
    }

    pub fn delete_database(&self, database_name: &str) -> Result<(), AzureError> {
        trace!("delete_database called (database_name == {})",
               database_name);

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs/{}",
                                          self.authorization_token.account(),
                                          database_name))
                .unwrap();

        // No specific headers are required, delete database only needs standard headers
        // which will be provied by perform_request

        let mut resp = self.perform_request(&url,
                                            HTTPMethod::Delete,
                                            None,
                                            ResourceType::Databases,
                                            None)?;

        check_status(&mut resp, StatusCode::NoContent)?;

        Ok(())
    }
}

fn generate_authorization(authorization_token: &AuthorizationToken,
                          http_method: HTTPMethod,
                          resource_type: ResourceType,
                          resource_link: &str,
                          time: &str)
                          -> String {
    let string_to_sign = string_to_sign(http_method, resource_type, resource_link, time);
    trace!("generate_authorization::string_to_sign == {:?}",
           string_to_sign);

    let str_unencoded = format!("type={}&ver={}&sig={}",
                                match authorization_token.token_type() {
                                    TokenType::Master => "master",
                                    TokenType::Resource => "resource",
                                },
                                VERSION,
                                encode_str_to_sign(&string_to_sign, authorization_token));

    trace!("generate_authorization::str_unencoded == {:?}",
           str_unencoded);

    utf8_percent_encode(&str_unencoded, COMPLETE_ENCODE_SET).collect::<String>()
}

fn encode_str_to_sign(str_to_sign: &str, authorization_token: &AuthorizationToken) -> String {
    let mut hmac = Hmac::new(Sha256::new(), authorization_token.binary_form());
    hmac.input(str_to_sign.as_bytes());

    base64::encode(hmac.result().code())
}

fn string_to_sign(http_method: HTTPMethod,
                  rt: ResourceType,
                  resource_link: &str,
                  time: &str)
                  -> String {
    // From official docs:
    // StringToSign = Verb.toLowerCase() + "\n" + ResourceType.toLowerCase() + "\n" + ResourceLink + "\n" + Date.toLowerCase() + "\n" + "" + "\n";
    // Notice the empty string at the end so we need to add two carriage returns

    format!("{}\n{}\n{}\n{}\n\n",
            match http_method {
                HTTPMethod::Get => "get",
                HTTPMethod::Put => "put",
                HTTPMethod::Post => "post",
                HTTPMethod::Delete => "delete",
            },
            match rt {
                ResourceType::Databases => "dbs",
                ResourceType::Collections => "colls",
                ResourceType::Documents => "docs",
            },
            resource_link,
            time.to_lowercase())

}

fn generate_resource_link(u: &url::Url) -> &str {
    let mut ps = u.path_segments().unwrap();

    if let Some(segment) = ps.next() {
        match segment {
            "" => "",
            "dbs" => {
                if ps.next() == None {
                    ""
                } else {
                    &u.path()[1..]
                }
            }
            "colls" => {
                if ps.next() == None {
                    ""
                } else {
                    &u.path()[1..]
                }
            }
            "docs" => {
                if ps.next() == None {
                    ""
                } else {
                    &u.path()[1..]
                }
            }
            _ => u.path(),
        }
    } else {
        ""
    }
}

#[cfg(test)]
mod tests {
    use azure::cosmos::client::*;
    use azure::cosmos::authorization_token;
    use url::Url;

    #[test]
    fn string_to_sign_00() {
        let time = chrono::DateTime::parse_from_rfc3339("1900-01-01T01:00:00.000000000+00:00")
            .unwrap();
        let time = time.with_timezone(&chrono::UTC);
        let time = format!("{}", time.format(TIME_FORMAT));

        let ret = string_to_sign(HTTPMethod::Get,
                                 ResourceType::Databases,
                                 "dbs/MyDatabase/colls/MyCollection",
                                 &time);
        assert_eq!(ret,
                   "get
dbs
dbs/MyDatabase/colls/MyCollection
mon, 01 jan 1900 01:00:00 gmt

");
    }

    #[test]
    fn generate_authorization_00() {
        let time = chrono::DateTime::parse_from_rfc3339("1900-01-01T01:00:00.000000000+00:00")
            .unwrap();
        let time = time.with_timezone(&chrono::UTC);
        let time = format!("{}", time.format(TIME_FORMAT));

        let authorization_token =
            authorization_token::AuthorizationToken::new("mindflavor", authorization_token::TokenType::Master,
                                                         "8F8xXXOptJxkblM1DBXW7a6NMI5oE8NnwPGYBmwxLCKfejOK7B7yhcCHMGvN3PBrlMLIOeol1Hv9RCdzAZR5sg==".to_owned()).unwrap();

        let ret = generate_authorization(&authorization_token,
                                         HTTPMethod::Get,
                                         ResourceType::Databases,
                                         "dbs/MyDatabase/colls/MyCollection",
                                         &time);
        assert_eq!(ret,
                   "type%3Dmaster%26ver%3D1.0%26sig%3DQkz%2Fr%2B1N2%2BPEnNijxGbGB%2FADvLsLBQmZ7uBBMuIwf4I%3D");
    }

    #[test]
    fn generate_authorization_01() {
        let time = chrono::DateTime::parse_from_rfc3339("2017-04-27T00:51:12.000000000+00:00")
            .unwrap();
        let time = time.with_timezone(&chrono::UTC);
        let time = format!("{}", time.format(TIME_FORMAT));

        let authorization_token =
            authorization_token::AuthorizationToken::new("mindflavor", authorization_token::TokenType::Master,
                                                         "dsZQi3KtZmCv1ljt3VNWNm7sQUF1y5rJfC6kv5JiwvW0EndXdDku/dkKBp8/ufDToSxL".to_owned()).unwrap();

        let ret = generate_authorization(&authorization_token,
                                         HTTPMethod::Get,
                                         ResourceType::Databases,
                                         "dbs/ToDoList",
                                         &time);

        // This is the result shown in the MSDN page. Clearly is wrong :)
        // below is the right one.
        //assert_eq!(ret,
        //           "type%3dmaster%26ver%3d1.0%26sig%3dc09PEVJrgp2uQRkr934kFbTqhByc7TVr3O");

        assert_eq!(ret,
                   "type%3Dmaster%26ver%3D1.0%26sig%3DKvBM8vONofkv3yKm%2F8zD9MEGlbu6jjHDJBp4E9c2ZZI%3D");
    }

    #[test]
    fn generate_resource_link_00() {
        let u = Url::parse("https://mindflavor.raldld.r4eee.sss/dbs/second").unwrap();
        assert_eq!(generate_resource_link(&u), "dbs/second");
        let u = Url::parse("https://mindflavor.raldld.r4eee.sss/dbs").unwrap();
        assert_eq!(generate_resource_link(&u), "");
        let u = Url::parse("https://mindflavor.raldld.r4eee.sss/colls/second/third").unwrap();
        assert_eq!(generate_resource_link(&u), "colls/second/third");
    }
}
<commit_msg>corrected replicated code in match arms<commit_after>use azure::cosmos::authorization_token::{TokenType, AuthorizationToken};
use azure::core::HTTPMethod;

use azure::cosmos::database::Database;

use azure::core::errors::{AzureError, check_status_extract_body, check_status};

use azure::cosmos::request_response::{ListDatabasesResponse, CreateDatabaseRequest};

use url;

use std::io::{Read, Cursor};

use crypto::hmac::Hmac;
use crypto::mac::Mac;
use crypto::sha2::Sha256;

use base64;
use hyper;
use serde_json;
use hyper::header::{ContentLength, Headers};
use hyper::status::StatusCode;
use hyper_native_tls;

use chrono;

use url::percent_encoding::utf8_percent_encode;

const AZURE_VERSION: &'static str = "2017-02-22";
const VERSION: &'static str = "1.0";
const TIME_FORMAT: &'static str = "%a, %d %h %Y %T GMT";

header! { (XMSVersion, "x-ms-version") => [String] }
header! { (XMSDate, "x-ms-date") => [String] }
header! { (Authorization, "Authorization") => [String] }

define_encode_set! {
    pub COMPLETE_ENCODE_SET = [url::percent_encoding::USERINFO_ENCODE_SET] | {
        '+', '-', '&'
    }
}

#[derive(Clone,Copy)]
pub enum ResourceType {
    Databases,
    Collections,
    Documents,
}

pub struct Client<'a> {
    hyper_client: hyper::client::Client,
    authorization_token: &'a AuthorizationToken<'a>,
}

impl<'a> Client<'a> {
    pub fn new(authorization_token: &'a AuthorizationToken<'a>)
               -> Result<Client<'a>, hyper_native_tls::native_tls::Error> {
        let ssl = hyper_native_tls::NativeTlsClient::new()?;
        let connector = hyper::net::HttpsConnector::new(ssl);
        let client = hyper::Client::with_connector(connector);

        Ok(Client {
               hyper_client: client,
               authorization_token: authorization_token,
           })
    }

    pub fn set_authorization_token(&mut self, at: &'a AuthorizationToken<'a>) {
        self.authorization_token = at;
    }

    fn perform_request(&self,
                       url: &url::Url,
                       http_method: HTTPMethod,
                       request_body: Option<(&mut Read, u64)>,
                       resource_type: ResourceType,
                       headers: Option<Headers>)
                       -> Result<hyper::client::Response, AzureError> {
        let dt = chrono::UTC::now();
        let time = format!("{}", dt.format(TIME_FORMAT));

        // to do: calculate resource link
        let resource_link = generate_resource_link(url);

        let auth = generate_authorization(self.authorization_token,
                                          http_method,
                                          resource_type,
                                          resource_link,
                                          &time);
        trace!("perform_request::auth == {:?}", auth);

        // we need to add custom headers. If the caller has passed its collection of
        // headers we will add to his. Otherwise we create one from scratch.
        let mut headers = if let Some(h) = headers {
            h
        } else {
            Headers::new()
        };

        if let Some((_, size)) = request_body {
            headers.set(ContentLength(size));
        }

        headers.set(XMSDate(time));
        headers.set(XMSVersion(AZURE_VERSION.to_owned()));
        headers.set(Authorization(auth));

        trace!("perform_request::headers == {:?}", headers);

        let mut builder = match http_method {
            HTTPMethod::Get => self.hyper_client.get(&url.to_string()),
            HTTPMethod::Put => self.hyper_client.put(&url.to_string()),
            HTTPMethod::Post => self.hyper_client.post(&url.to_string()),
            HTTPMethod::Delete => self.hyper_client.delete(&url.to_string()),
        };

        if let Some((mut rb, size)) = request_body {
            let b = hyper::client::Body::SizedBody(rb, size);
            builder = builder.body(b);
        }

        let res = builder.headers(headers).send()?;

        Ok(res)
    }

    pub fn list_databases(&self) -> Result<Vec<Database>, AzureError> {
        trace!("list_databases called");

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs",
                                          self.authorization_token.account()))
                .unwrap();

        // No specific headers are required, list databases only needs standard headers
        // which will be provied by perform_request
        let mut resp =
            self.perform_request(&url, HTTPMethod::Get, None, ResourceType::Databases, None)?;

        let body = check_status_extract_body(&mut resp, StatusCode::Ok)?;
        let db: ListDatabasesResponse = serde_json::from_str(&body)?;

        Ok(db.databases)
    }

    pub fn create_database(&self, database_name: &str) -> Result<Database, AzureError> {
        trace!("create_databases called (database_name == {})",
               database_name);

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs",
                                          self.authorization_token.account()))
                .unwrap();

        // No specific headers are required, create databases only needs standard headers
        // which will be provied by perform_request
        // for the body, we will serialize the appropriate structure

        let req = CreateDatabaseRequest { id: database_name };
        let req = serde_json::to_string(&req)?;
        let mut curs = Cursor::new(&req);

        let mut resp = self.perform_request(&url,
                                            HTTPMethod::Post,
                                            Some((&mut curs, req.len() as u64)),
                                            ResourceType::Databases,
                                            None)?;

        let body = check_status_extract_body(&mut resp, StatusCode::Created)?;
        let db: Database = serde_json::from_str(&body)?;

        Ok(db)
    }

    pub fn get_database(&self, database_name: &str) -> Result<Database, AzureError> {
        trace!("get_database called (database_name == {})", database_name);

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs/{}",
                                          self.authorization_token.account(),
                                          database_name))
                .unwrap();

        // No specific headers are required, get database only needs standard headers
        // which will be provied by perform_request

        let mut resp =
            self.perform_request(&url, HTTPMethod::Get, None, ResourceType::Databases, None)?;

        let body = check_status_extract_body(&mut resp, StatusCode::Ok)?;
        let db: Database = serde_json::from_str(&body)?;

        Ok(db)
    }

    pub fn delete_database(&self, database_name: &str) -> Result<(), AzureError> {
        trace!("delete_database called (database_name == {})",
               database_name);

        let url = url::Url::parse(&format!("https://{}.documents.azure.com/dbs/{}",
                                          self.authorization_token.account(),
                                          database_name))
                .unwrap();

        // No specific headers are required, delete database only needs standard headers
        // which will be provied by perform_request

        let mut resp = self.perform_request(&url,
                                            HTTPMethod::Delete,
                                            None,
                                            ResourceType::Databases,
                                            None)?;

        check_status(&mut resp, StatusCode::NoContent)?;

        Ok(())
    }
}

fn generate_authorization(authorization_token: &AuthorizationToken,
                          http_method: HTTPMethod,
                          resource_type: ResourceType,
                          resource_link: &str,
                          time: &str)
                          -> String {
    let string_to_sign = string_to_sign(http_method, resource_type, resource_link, time);
    trace!("generate_authorization::string_to_sign == {:?}",
           string_to_sign);

    let str_unencoded = format!("type={}&ver={}&sig={}",
                                match authorization_token.token_type() {
                                    TokenType::Master => "master",
                                    TokenType::Resource => "resource",
                                },
                                VERSION,
                                encode_str_to_sign(&string_to_sign, authorization_token));

    trace!("generate_authorization::str_unencoded == {:?}",
           str_unencoded);

    utf8_percent_encode(&str_unencoded, COMPLETE_ENCODE_SET).collect::<String>()
}

fn encode_str_to_sign(str_to_sign: &str, authorization_token: &AuthorizationToken) -> String {
    let mut hmac = Hmac::new(Sha256::new(), authorization_token.binary_form());
    hmac.input(str_to_sign.as_bytes());

    base64::encode(hmac.result().code())
}

fn string_to_sign(http_method: HTTPMethod,
                  rt: ResourceType,
                  resource_link: &str,
                  time: &str)
                  -> String {
    // From official docs:
    // StringToSign = Verb.toLowerCase() + "\n" + ResourceType.toLowerCase() + "\n" + ResourceLink + "\n" + Date.toLowerCase() + "\n" + "" + "\n";
    // Notice the empty string at the end so we need to add two carriage returns

    format!("{}\n{}\n{}\n{}\n\n",
            match http_method {
                HTTPMethod::Get => "get",
                HTTPMethod::Put => "put",
                HTTPMethod::Post => "post",
                HTTPMethod::Delete => "delete",
            },
            match rt {
                ResourceType::Databases => "dbs",
                ResourceType::Collections => "colls",
                ResourceType::Documents => "docs",
            },
            resource_link,
            time.to_lowercase())

}

fn generate_resource_link(u: &url::Url) -> &str {
    let mut ps = u.path_segments().unwrap();

    if let Some(segment) = ps.next() {
        match segment {
            "" => "",
            "dbs" | "colls" | "docs" => {
                if ps.next() == None {
                    ""
                } else {
                    &u.path()[1..]
                }
            }
            _ => u.path(),
        }
    } else {
        ""
    }
}

#[cfg(test)]
mod tests {
    use azure::cosmos::client::*;
    use azure::cosmos::authorization_token;
    use url::Url;

    #[test]
    fn string_to_sign_00() {
        let time = chrono::DateTime::parse_from_rfc3339("1900-01-01T01:00:00.000000000+00:00")
            .unwrap();
        let time = time.with_timezone(&chrono::UTC);
        let time = format!("{}", time.format(TIME_FORMAT));

        let ret = string_to_sign(HTTPMethod::Get,
                                 ResourceType::Databases,
                                 "dbs/MyDatabase/colls/MyCollection",
                                 &time);
        assert_eq!(ret,
                   "get
dbs
dbs/MyDatabase/colls/MyCollection
mon, 01 jan 1900 01:00:00 gmt

");
    }

    #[test]
    fn generate_authorization_00() {
        let time = chrono::DateTime::parse_from_rfc3339("1900-01-01T01:00:00.000000000+00:00")
            .unwrap();
        let time = time.with_timezone(&chrono::UTC);
        let time = format!("{}", time.format(TIME_FORMAT));

        let authorization_token =
            authorization_token::AuthorizationToken::new("mindflavor", authorization_token::TokenType::Master,
                                                         "8F8xXXOptJxkblM1DBXW7a6NMI5oE8NnwPGYBmwxLCKfejOK7B7yhcCHMGvN3PBrlMLIOeol1Hv9RCdzAZR5sg==".to_owned()).unwrap();

        let ret = generate_authorization(&authorization_token,
                                         HTTPMethod::Get,
                                         ResourceType::Databases,
                                         "dbs/MyDatabase/colls/MyCollection",
                                         &time);
        assert_eq!(ret,
                   "type%3Dmaster%26ver%3D1.0%26sig%3DQkz%2Fr%2B1N2%2BPEnNijxGbGB%2FADvLsLBQmZ7uBBMuIwf4I%3D");
    }

    #[test]
    fn generate_authorization_01() {
        let time = chrono::DateTime::parse_from_rfc3339("2017-04-27T00:51:12.000000000+00:00")
            .unwrap();
        let time = time.with_timezone(&chrono::UTC);
        let time = format!("{}", time.format(TIME_FORMAT));

        let authorization_token =
            authorization_token::AuthorizationToken::new("mindflavor", authorization_token::TokenType::Master,
                                                         "dsZQi3KtZmCv1ljt3VNWNm7sQUF1y5rJfC6kv5JiwvW0EndXdDku/dkKBp8/ufDToSxL".to_owned()).unwrap();

        let ret = generate_authorization(&authorization_token,
                                         HTTPMethod::Get,
                                         ResourceType::Databases,
                                         "dbs/ToDoList",
                                         &time);

        // This is the result shown in the MSDN page. Clearly is wrong :)
        // below is the right one.
        //assert_eq!(ret,
        //           "type%3dmaster%26ver%3d1.0%26sig%3dc09PEVJrgp2uQRkr934kFbTqhByc7TVr3O");

        assert_eq!(ret,
                   "type%3Dmaster%26ver%3D1.0%26sig%3DKvBM8vONofkv3yKm%2F8zD9MEGlbu6jjHDJBp4E9c2ZZI%3D");
    }

    #[test]
    fn generate_resource_link_00() {
        let u = Url::parse("https://mindflavor.raldld.r4eee.sss/dbs/second").unwrap();
        assert_eq!(generate_resource_link(&u), "dbs/second");
        let u = Url::parse("https://mindflavor.raldld.r4eee.sss/dbs").unwrap();
        assert_eq!(generate_resource_link(&u), "");
        let u = Url::parse("https://mindflavor.raldld.r4eee.sss/colls/second/third").unwrap();
        assert_eq!(generate_resource_link(&u), "colls/second/third");
    }
}
<|endoftext|>