<commit_before>use crate::common::util::*;
// spell-checker:ignore checkfile, nonames, testf
macro_rules! get_hash(
    ($str:expr) => (
        $str.split(' ').collect::<Vec<&str>>()[0]
    );
);

macro_rules! test_digest {
    ($($id:ident $t:ident $size:expr)*) => ($(

    mod $id {
        use crate::common::util::*;
        static DIGEST_ARG: &'static str = concat!("--", stringify!($t));
        static BITS_ARG: &'static str = concat!("--bits=", stringify!($size));
        static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected");
        static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile");

        #[test]
        fn test_single_file() {
            let ts = TestScenario::new("hashsum");
            assert_eq!(ts.fixtures.read(EXPECTED_FILE),
                       get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("input.txt").succeeds().no_stderr().stdout_str()));
        }

        #[test]
        fn test_stdin() {
            let ts = TestScenario::new("hashsum");
            assert_eq!(ts.fixtures.read(EXPECTED_FILE),
                       get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture("input.txt").succeeds().no_stderr().stdout_str()));
        }

        #[test]
        fn test_nonames() {
            let ts = TestScenario::new("hashsum");
            // EXPECTED_FILE has no newline character at the end
            if DIGEST_ARG == "b3sum" {
                // Option only available on b3sum
                assert_eq!(format!("{0}\n{0}\n", ts.fixtures.read(EXPECTED_FILE)),
                       ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("--no-names").arg("input.txt").arg("-").pipe_in_fixture("input.txt")
                       .succeeds().no_stderr().stdout_str()
                       );
                }
        }

        #[test]
        fn test_check() {
            let ts = TestScenario::new("hashsum");
            ts.ucmd()
                .args(&[DIGEST_ARG, BITS_ARG, "--check", CHECK_FILE])
                .succeeds()
                .no_stderr()
                .stdout_is("input.txt: OK\n");
        }

        #[cfg(windows)]
        #[test]
        fn test_text_mode() {
            // TODO Replace this with hard-coded files that store the
            // expected output of text mode on an input file that has
            // "\r\n" line endings.
            let result = new_ucmd!()
                .args(&[DIGEST_ARG, BITS_ARG, "-b"])
                .pipe_in("a\nb\nc\n")
                .succeeds();
            let expected = result.no_stderr().stdout();
            // Replace the "*-\n" at the end of the output with " -\n".
            // The asterisk indicates that the digest was computed in
            // binary mode.
            let n = expected.len();
            let expected = [&expected[..n - 3], &[b' ', b'-', b'\n']].concat();
            new_ucmd!()
                .args(&[DIGEST_ARG, BITS_ARG, "-t"])
                .pipe_in("a\r\nb\r\nc\r\n")
                .succeeds()
                .no_stderr()
                .stdout_is(std::str::from_utf8(&expected).unwrap());
        }
    }
    )*)
}

test_digest! {
    md5 md5 128
    sha1 sha1 160
    sha224 sha224 224
    sha256 sha256 256
    sha384 sha384 384
    sha512 sha512 512
    sha3_224 sha3 224
    sha3_256 sha3 256
    sha3_384 sha3 384
    sha3_512 sha3 512
    shake128_256 shake128 256
    shake256_512 shake256 512
    b2sum b2sum 512
    b3sum b3sum 256
}

#[test]
fn test_check_sha1() {
    // To make sure that #3815 doesn't happen again
    let scene = TestScenario::new(util_name!());
    let at = &scene.fixtures;

    at.write("testf", "foobar\n");
    at.write(
        "testf.sha1",
        "988881adc9fc3655077dc2d4d757d480b5ea0e11  testf\n",
    );
    scene
        .ccmd("sha1sum")
        .arg("-c")
        .arg(at.subdir.join("testf.sha1"))
        .succeeds()
        .stdout_is("testf: OK\n")
        .stderr_is("");
}

#[test]
fn test_check_file_not_found_warning() {
    let scene = TestScenario::new(util_name!());
    let at = &scene.fixtures;

    at.write("testf", "foobar\n");
    at.write(
        "testf.sha1",
        "988881adc9fc3655077dc2d4d757d480b5ea0e11  testf\n",
    );
    at.remove("testf");
    scene
        .ccmd("sha1sum")
        .arg("-c")
        .arg(at.subdir.join("testf.sha1"))
        .succeeds()
        .stdout_is("sha1sum: testf: No such file or directory\ntestf: FAILED open or read\n")
        .stderr_is("sha1sum: warning: 1 listed file could not be read");
}

#[test]
fn test_invalid_arg() {
    new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
}
<commit_msg>ignore a word<commit_after>use crate::common::util::*;
// spell-checker:ignore checkfile, nonames, testf, ntestf
macro_rules! get_hash(
    ($str:expr) => (
        $str.split(' ').collect::<Vec<&str>>()[0]
    );
);

macro_rules! test_digest {
    ($($id:ident $t:ident $size:expr)*) => ($(

    mod $id {
        use crate::common::util::*;
        static DIGEST_ARG: &'static str = concat!("--", stringify!($t));
        static BITS_ARG: &'static str = concat!("--bits=", stringify!($size));
        static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected");
        static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile");

        #[test]
        fn test_single_file() {
            let ts = TestScenario::new("hashsum");
            assert_eq!(ts.fixtures.read(EXPECTED_FILE),
                       get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("input.txt").succeeds().no_stderr().stdout_str()));
        }

        #[test]
        fn test_stdin() {
            let ts = TestScenario::new("hashsum");
            assert_eq!(ts.fixtures.read(EXPECTED_FILE),
                       get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture("input.txt").succeeds().no_stderr().stdout_str()));
        }

        #[test]
        fn test_nonames() {
            let ts = TestScenario::new("hashsum");
            // EXPECTED_FILE has no newline character at the end
            if DIGEST_ARG == "b3sum" {
                // Option only available on b3sum
                assert_eq!(format!("{0}\n{0}\n", ts.fixtures.read(EXPECTED_FILE)),
                       ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("--no-names").arg("input.txt").arg("-").pipe_in_fixture("input.txt")
                       .succeeds().no_stderr().stdout_str()
                       );
                }
        }

        #[test]
        fn test_check() {
            let ts = TestScenario::new("hashsum");
            ts.ucmd()
                .args(&[DIGEST_ARG, BITS_ARG, "--check", CHECK_FILE])
                .succeeds()
                .no_stderr()
                .stdout_is("input.txt: OK\n");
        }

        #[cfg(windows)]
        #[test]
        fn test_text_mode() {
            // TODO Replace this with hard-coded files that store the
            // expected output of text mode on an input file that has
            // "\r\n" line endings.
            let result = new_ucmd!()
                .args(&[DIGEST_ARG, BITS_ARG, "-b"])
                .pipe_in("a\nb\nc\n")
                .succeeds();
            let expected = result.no_stderr().stdout();
            // Replace the "*-\n" at the end of the output with " -\n".
            // The asterisk indicates that the digest was computed in
            // binary mode.
            let n = expected.len();
            let expected = [&expected[..n - 3], &[b' ', b'-', b'\n']].concat();
            new_ucmd!()
                .args(&[DIGEST_ARG, BITS_ARG, "-t"])
                .pipe_in("a\r\nb\r\nc\r\n")
                .succeeds()
                .no_stderr()
                .stdout_is(std::str::from_utf8(&expected).unwrap());
        }
    }
    )*)
}

test_digest! {
    md5 md5 128
    sha1 sha1 160
    sha224 sha224 224
    sha256 sha256 256
    sha384 sha384 384
    sha512 sha512 512
    sha3_224 sha3 224
    sha3_256 sha3 256
    sha3_384 sha3 384
    sha3_512 sha3 512
    shake128_256 shake128 256
    shake256_512 shake256 512
    b2sum b2sum 512
    b3sum b3sum 256
}

#[test]
fn test_check_sha1() {
    // To make sure that #3815 doesn't happen again
    let scene = TestScenario::new(util_name!());
    let at = &scene.fixtures;

    at.write("testf", "foobar\n");
    at.write(
        "testf.sha1",
        "988881adc9fc3655077dc2d4d757d480b5ea0e11  testf\n",
    );
    scene
        .ccmd("sha1sum")
        .arg("-c")
        .arg(at.subdir.join("testf.sha1"))
        .succeeds()
        .stdout_is("testf: OK\n")
        .stderr_is("");
}

#[test]
fn test_check_file_not_found_warning() {
    let scene = TestScenario::new(util_name!());
    let at = &scene.fixtures;

    at.write("testf", "foobar\n");
    at.write(
        "testf.sha1",
        "988881adc9fc3655077dc2d4d757d480b5ea0e11  testf\n",
    );
    at.remove("testf");
    scene
        .ccmd("sha1sum")
        .arg("-c")
        .arg(at.subdir.join("testf.sha1"))
        .succeeds()
        .stdout_is("sha1sum: testf: No such file or directory\ntestf: FAILED open or read\n")
        .stderr_is("sha1sum: warning: 1 listed file could not be read");
}

#[test]
fn test_invalid_arg() {
    new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
}
<|endoftext|>


<commit_before>use std::usize;

#[derive(Eq, PartialEq, Clone, Hash)]
pub struct BitsVec {
    inner: Vec<usize>,
    units: usize,
    bits: usize,
    max_bits: usize,
    leftover: usize,
}

impl BitsVec {
    pub fn new(bits: usize) -> BitsVec {
        let max = usize::MAX.count_ones() as usize;
        if bits > max {
            // We can store more bits, but then we might need BigInt to get them out!
            panic!("cannot hold more than {} bits at a time", max);
        }

        BitsVec {
            inner: vec![0],
            units: 0,
            bits: bits,
            max_bits: max,
            leftover: max,
        }
    }

    pub fn push(&mut self, mut value: usize) {
        assert!(value >> self.bits == 0,
                "input size is more than allowed size ({} >= {})", value, 2usize.pow(self.bits as u32));

        let mut idx = self.inner.len() - 1;
        let shift_amount;

        if self.leftover < self.bits {
            let left = self.bits - self.leftover;
            self.inner[idx] |= value >> left;
            if self.leftover != 0 {     // special case, in which masking would result in zero!
                value &= (1 << left) - 1;
            }

            self.inner.push(0);
            self.leftover = self.max_bits - left;
            shift_amount = self.max_bits - left;
            idx += 1;
        } else {
            shift_amount = self.leftover - self.bits;
            self.leftover -= self.bits;
        }

        value <<= shift_amount;
        self.inner[idx] |= value;
        self.units += 1;
    }

    pub fn get(&self, i: usize) -> Option<usize> {
        if i >= self.units {
            return None
        }

        let idx = i * self.bits / self.max_bits;
        let bits = (i * self.bits) % self.max_bits;
        let mut val = self.inner[idx];
        if bits != 0 {
            val &= (1 << (self.max_bits - bits)) - 1;
        }

        let diff = self.max_bits - bits;
        if diff >= self.bits {
            Some(val >> (diff - self.bits))
        } else {
            let shift = self.bits - diff;
            Some((val << shift) | (self.inner[idx + 1] >> (self.max_bits - shift)))
        }
    }

    pub fn len(&self) -> usize {
        self.units
    }

    pub fn inner_len(&self) -> usize {
        self.inner.len()
    }
}
<commit_msg>Basic impls: Debug and iterators<commit_after>use std::fmt;
use std::ops::Range;
use std::usize;

#[derive(Clone, Hash)]
pub struct BitsVec {
    inner: Vec<usize>,
    units: usize,
    bits: usize,
    max_bits: usize,
    leftover: usize,
}

impl BitsVec {
    pub fn new(bits: usize) -> BitsVec {
        let max = usize::MAX.count_ones() as usize;
        // We can store more bits, but then we might need BigInt to get them out!
        assert!(bits < max, "cannot hold more than {} bits at a time", max - 1);

        BitsVec {
            inner: vec![0],
            units: 0,
            bits: bits,
            max_bits: max,
            leftover: max,
        }
    }

    pub fn push(&mut self, mut value: usize) {
        assert!(value >> self.bits == 0,
                "input size is more than allowed size ({} >= {})", value, 2usize.pow(self.bits as u32));

        let mut idx = self.inner.len() - 1;
        let shift_amount;

        if self.leftover < self.bits {
            let left = self.bits - self.leftover;
            self.inner[idx] |= value >> left;
            if self.leftover != 0 {     // special case, in which masking would result in zero!
                value &= (1 << left) - 1;
            }

            self.inner.push(0);
            self.leftover = self.max_bits - left;
            shift_amount = self.max_bits - left;
            idx += 1;
        } else {
            shift_amount = self.leftover - self.bits;
            self.leftover -= self.bits;
        }

        value <<= shift_amount;
        self.inner[idx] |= value;
        self.units += 1;
    }

    pub fn get(&self, i: usize) -> Option<usize> {
        if i >= self.units {
            return None
        }

        let idx = i * self.bits / self.max_bits;
        let bits = (i * self.bits) % self.max_bits;
        let mut val = self.inner[idx];
        if bits != 0 {
            val &= (1 << (self.max_bits - bits)) - 1;
        }

        let diff = self.max_bits - bits;
        if diff >= self.bits {
            Some(val >> (diff - self.bits))
        } else {
            let shift = self.bits - diff;
            Some((val << shift) | (self.inner[idx + 1] >> (self.max_bits - shift)))
        }
    }

    pub fn len(&self) -> usize {
        self.units
    }

    pub fn inner_len(&self) -> usize {
        self.inner.len()
    }

    pub fn iter(&self) -> Iter {
        Iter { vec: self, range: 0..self.units }
    }

    pub fn into_iter(self) -> IntoIter {
        IntoIter { range: 0..self.units, vec: self }
    }
}

impl fmt::Debug for BitsVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_list().entries(self.iter()).finish()
    }
}

pub struct Iter<'a> {
    vec: &'a BitsVec,
    range: Range<usize>,
}

impl<'a> IntoIterator for &'a BitsVec {
    type Item = usize;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

impl<'a> Iterator for Iter<'a> {
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        self.range.next().and_then(|i| self.vec.get(i))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.range.size_hint()
    }
}

impl<'a> DoubleEndedIterator for Iter<'a> {
    fn next_back(&mut self) -> Option<usize> {
        self.range.next_back().and_then(|i| self.vec.get(i))
    }
}

impl<'a> ExactSizeIterator for Iter<'a> {}

pub struct IntoIter {
    vec: BitsVec,
    range: Range<usize>,
}

impl IntoIterator for BitsVec {
    type Item = usize;
    type IntoIter = IntoIter;

    fn into_iter(self) -> IntoIter {
        self.into_iter()
    }
}

impl Iterator for IntoIter {
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        self.range.next().and_then(|i| self.vec.get(i))
    }
}

impl DoubleEndedIterator for IntoIter {
    fn next_back(&mut self) -> Option<usize> {
        self.range.next_back().and_then(|i| self.vec.get(i))
    }
}

impl ExactSizeIterator for IntoIter {}
<|endoftext|>


<commit_before>//! Matrix rooms.

use std::collections::{HashMap, HashSet};
use std::convert::{TryFrom, TryInto};

use diesel::{Connection, ExecuteDsl, ExpressionMethods, FilterDsl, FindDsl, LoadDsl, OrderDsl, insert};
use diesel::pg::PgConnection;
use diesel::pg::data_types::PgTimestamp;
use diesel::pg::expression::dsl::any;
use diesel::result::Error as DieselError;
use ruma_events::EventType;
use ruma_events::room::create::{CreateEvent, CreateEventContent};
use ruma_events::room::history_visibility::{
    HistoryVisibility,
    HistoryVisibilityEvent,
    HistoryVisibilityEventContent,
};
use ruma_events::room::join_rules::{
    JoinRule,
    JoinRulesEvent,
    JoinRulesEventContent,
};
use ruma_events::room::name::{NameEvent, NameEventContent};
use ruma_events::room::power_levels::{PowerLevelsEvent, PowerLevelsEventContent};
use ruma_events::room::topic::{TopicEvent, TopicEventContent};
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};

use error::ApiError;
use models::event::{Event, NewEvent};
use models::room_alias::{NewRoomAlias, RoomAlias};
use models::room_membership::{RoomMembership, RoomMembershipOptions};
use models::user::User;
use schema::{events, rooms, users};

/// Options provided by the user to customize the room upon creation.
pub struct CreationOptions {
    /// An initial alias for the room.
    pub alias: Option<String>,
    /// Whehter or not the room should be federated.
    pub federate: bool,
    /// A list of users to invite to the room.
    pub invite_list: Option<Vec<String>>,
    /// An initial name for the room.
    pub name: Option<String>,
    /// A convenience parameter for setting a few default state events.
    pub preset: RoomPreset,
    /// An initial topic for the room.
    pub topic: Option<String>,
}

/// A new Matrix room, not yet saved.
#[derive(Debug, Insertable)]
#[table_name = "rooms"]
pub struct NewRoom {
    /// The room's unique ID.
    pub id: RoomId,
    /// The ID of the user creating the room.
    pub user_id: UserId,
    /// Whether or not the room is visible in the directory.
    pub public: bool,
}

/// A Matrix room.
#[derive(Debug, Queryable)]
pub struct Room {
    /// The room's unique ID.
    pub id: RoomId,
    /// The ID of the user who created the room.
    pub user_id: UserId,
    /// Whether or not the room is visible in the directory.
    pub public: bool,
    /// The time the room was created.
    pub created_at: PgTimestamp,
}

/// A convenience parameter for setting a few default state events.
#[derive(Clone, Copy, Debug, Deserialize)]
pub enum RoomPreset {
    /// `join_rules` is set to `invite` and `history_visibility` is set to `shared`.
    PrivateChat,
    /// `join_rules` is set to `public` and `history_visibility` is set to `shared`.
    PublicChat,
    /// Same as `PrivateChat`, but all initial invitees get the same power level as the creator.
    TrustedPrivateChat,
}

impl Room {
    /// Creates a new room in the database.
    pub fn create(
        connection: &PgConnection,
        new_room: &NewRoom,
        homeserver_domain: &str,
        creation_options: &CreationOptions,
    ) -> Result<Room, ApiError> {
        connection.transaction::<Room, ApiError, _>(|| {
            let room: Room = insert(new_room)
                .into(rooms::table)
                .get_result(connection)
                .map_err(ApiError::from)?;

            if let Some(ref alias) = creation_options.alias {
                let new_room_alias = NewRoomAlias {
                    alias: RoomAliasId::try_from(&format!("#{}:{}", alias, homeserver_domain))?,
                    room_id: room.id.clone(),
                    user_id: new_room.user_id.clone(),
                    servers: vec![homeserver_domain.to_string()],
                };

                RoomAlias::create(connection, homeserver_domain, &new_room_alias)?;
            }

            let mut new_events = Vec::new();

            let new_create_event: NewEvent = CreateEvent {
                content: CreateEventContent {
                    creator: new_room.user_id.clone(),
                    federate: creation_options.federate,
                },
                event_id: EventId::new(homeserver_domain)?,
                event_type: EventType::RoomCreate,
                prev_content: None,
                room_id: room.id.clone(),
                state_key: "".to_string(),
                unsigned: None,
                user_id: new_room.user_id.clone(),
            }.try_into()?;

            new_events.push(new_create_event);

            if let Some(ref name) = creation_options.name {
                let new_name_event: NewEvent = NameEvent {
                    content: NameEventContent {
                        name: name.to_string(),
                    },
                    event_id: EventId::new(homeserver_domain)?,
                    event_type: EventType::RoomName,
                    prev_content: None,
                    room_id: room.id.clone(),
                    state_key: "".to_string(),
                    unsigned: None,
                    user_id: new_room.user_id.clone(),
                }.try_into()?;

                new_events.push(new_name_event);
            }

            if let Some(ref topic) = creation_options.topic {
                let new_topic_event: NewEvent = TopicEvent {
                    content: TopicEventContent {
                        topic: topic.to_string(),
                    },
                    event_id: EventId::new(homeserver_domain)?,
                    event_type: EventType::RoomTopic,
                    prev_content: None,
                    room_id: room.id.clone(),
                    state_key: "".to_string(),
                    unsigned: None,
                    user_id: new_room.user_id.clone(),
                }.try_into()?;

                new_events.push(new_topic_event);
            }

            let new_history_visibility_event: NewEvent = HistoryVisibilityEvent {
                content: HistoryVisibilityEventContent {
                    history_visibility: HistoryVisibility::Shared,
                },
                event_id: EventId::new(homeserver_domain)?,
                event_type: EventType::RoomHistoryVisibility,
                prev_content: None,
                room_id: room.id.clone(),
                state_key: "".to_string(),
                unsigned: None,
                user_id: new_room.user_id.clone(),
            }.try_into()?;

            new_events.push(new_history_visibility_event);

            match creation_options.preset {
                RoomPreset::PrivateChat => {
                    let new_join_rules_event: NewEvent = JoinRulesEvent {
                        content: JoinRulesEventContent { join_rule: JoinRule::Invite },
                        event_id: EventId::new(homeserver_domain)?,
                        event_type: EventType::RoomJoinRules,
                        prev_content: None,
                        room_id: room.id.clone(),
                        state_key: "".to_string(),
                        unsigned: None,
                        user_id: new_room.user_id.clone(),
                    }.try_into()?;

                    new_events.push(new_join_rules_event);
                }
                RoomPreset::PublicChat => {
                    let new_join_rules_event: NewEvent = JoinRulesEvent {
                        content: JoinRulesEventContent { join_rule: JoinRule::Public },
                        event_id: EventId::new(homeserver_domain)?,
                        event_type: EventType::RoomJoinRules,
                        prev_content: None,
                        room_id: room.id.clone(),
                        state_key: "".to_string(),
                        unsigned: None,
                        user_id: new_room.user_id.clone(),
                    }.try_into()?;

                    new_events.push(new_join_rules_event);
                }
                RoomPreset::TrustedPrivateChat => {
                    let new_join_rules_event: NewEvent = JoinRulesEvent {
                        content: JoinRulesEventContent { join_rule: JoinRule::Invite },
                        event_id: EventId::new(homeserver_domain)?,
                        event_type: EventType::RoomJoinRules,
                        prev_content: None,
                        room_id: room.id.clone(),
                        state_key: "".to_string(),
                        unsigned: None,
                        user_id: new_room.user_id.clone(),
                    }.try_into()?;

                    new_events.push(new_join_rules_event);
                }
            }

            insert(&new_events)
                .into(events::table)
                .execute(connection)
                .map_err(ApiError::from)?;

            if let Some(ref invite_list) = creation_options.invite_list {
                let mut user_ids = HashSet::with_capacity(invite_list.len());

                for invitee in invite_list {
                    let user_id = UserId::try_from(invitee)?;

                    if user_id.hostname().to_string() != homeserver_domain {
                        return Err(
                            ApiError::unimplemented("Federation is not yet supported.".to_string())
                        );
                    }

                    user_ids.insert(user_id);
                }

                let users: Vec<User> = users::table
                    .filter(users::id.eq(any(
                        user_ids.iter().cloned().collect::<Vec<UserId>>()))
                    )
                    .get_results(connection)
                    .map_err(ApiError::from)?;

                let loaded_user_ids: HashSet<UserId> = users
                    .iter()
                    .map(|user| user.id.clone())
                    .collect();

                let missing_user_ids: Vec<UserId> = user_ids
                    .difference(&loaded_user_ids)
                    .cloned()
                    .collect();

                if missing_user_ids.len() > 0 {
                    return Err(
                        ApiError::bad_json(format!(
                            "Unknown users in invite list: {}",
                            &missing_user_ids
                                .iter()
                                .map(|user_id| user_id.to_string())
                                .collect::<Vec<String>>()
                                .join(", ")
                        ))
                    )
                }

                for user in users {
                    let options = RoomMembershipOptions {
                        room_id: room.id.clone(),
                        user_id: user.id.clone(),
                        sender: room.user_id.clone(),
                        membership: "invite".to_string(),
                    };

                    RoomMembership::create(connection, &homeserver_domain, options)?;
                }
            }

            Ok(room)
        }).map_err(ApiError::from)
    }

    /// Looks up the most recent power levels event for the room.
    ///
    /// If the room does not have a power levels event, a default one is created according to the
    /// specification.
    pub fn current_power_levels(&self, connection: &PgConnection)
    -> Result<PowerLevelsEventContent, ApiError> {
        match events::table
            .filter(events::room_id.eq(self.id.clone()))
            .filter(events::state_key.eq(EventType::RoomPowerLevels.to_string()))
            .order(events::ordering.desc())
            .first::<Event>(connection)
        {
            Ok(event) => {
                let power_levels_event: PowerLevelsEvent = event.try_into()?;

                Ok(power_levels_event.content)
            }
            Err(error) => match error {
                DieselError::NotFound => Ok(PowerLevelsEventContent {
                    ban: 50,
                    events: HashMap::new(),
                    events_default: 0,
                    invite: 50,
                    kick: 50,
                    redact: 50,
                    state_default: 0,
                    users: HashMap::new(),
                    users_default: 0,
                }),
                _ => Err(error.into()),
            },
        }
    }

    /// Look up a `Room` given the `RoomId`.
    pub fn find(connection: &PgConnection, room_id: &RoomId)
    -> Result<Room, ApiError> {
        rooms::table
            .find(room_id)
            .get_result(connection)
            .map(Room::from)
            .map_err(|err| {
                match err {
                    DieselError::NotFound => ApiError::not_found(
                        "The room was not found on this server".to_string()
                    ),
                    _ => ApiError::from(err)
                }
            })
    }
}
<commit_msg>Use the event_type field to find power_level events<commit_after>//! Matrix rooms.

use std::collections::{HashMap, HashSet};
use std::convert::{TryFrom, TryInto};

use diesel::{Connection, ExecuteDsl, ExpressionMethods, FilterDsl, FindDsl, LoadDsl, OrderDsl, insert};
use diesel::pg::PgConnection;
use diesel::pg::data_types::PgTimestamp;
use diesel::pg::expression::dsl::any;
use diesel::result::Error as DieselError;
use ruma_events::EventType;
use ruma_events::room::create::{CreateEvent, CreateEventContent};
use ruma_events::room::history_visibility::{
    HistoryVisibility,
    HistoryVisibilityEvent,
    HistoryVisibilityEventContent,
};
use ruma_events::room::join_rules::{
    JoinRule,
    JoinRulesEvent,
    JoinRulesEventContent,
};
use ruma_events::room::name::{NameEvent, NameEventContent};
use ruma_events::room::power_levels::{PowerLevelsEvent, PowerLevelsEventContent};
use ruma_events::room::topic::{TopicEvent, TopicEventContent};
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};

use error::ApiError;
use models::event::{Event, NewEvent};
use models::room_alias::{NewRoomAlias, RoomAlias};
use models::room_membership::{RoomMembership, RoomMembershipOptions};
use models::user::User;
use schema::{events, rooms, users};

/// Options provided by the user to customize the room upon creation.
pub struct CreationOptions {
    /// An initial alias for the room.
    pub alias: Option<String>,
    /// Whehter or not the room should be federated.
    pub federate: bool,
    /// A list of users to invite to the room.
    pub invite_list: Option<Vec<String>>,
    /// An initial name for the room.
    pub name: Option<String>,
    /// A convenience parameter for setting a few default state events.
    pub preset: RoomPreset,
    /// An initial topic for the room.
    pub topic: Option<String>,
}

/// A new Matrix room, not yet saved.
#[derive(Debug, Insertable)]
#[table_name = "rooms"]
pub struct NewRoom {
    /// The room's unique ID.
    pub id: RoomId,
    /// The ID of the user creating the room.
    pub user_id: UserId,
    /// Whether or not the room is visible in the directory.
    pub public: bool,
}

/// A Matrix room.
#[derive(Debug, Queryable)]
pub struct Room {
    /// The room's unique ID.
    pub id: RoomId,
    /// The ID of the user who created the room.
    pub user_id: UserId,
    /// Whether or not the room is visible in the directory.
    pub public: bool,
    /// The time the room was created.
    pub created_at: PgTimestamp,
}

/// A convenience parameter for setting a few default state events.
#[derive(Clone, Copy, Debug, Deserialize)]
pub enum RoomPreset {
    /// `join_rules` is set to `invite` and `history_visibility` is set to `shared`.
    PrivateChat,
    /// `join_rules` is set to `public` and `history_visibility` is set to `shared`.
    PublicChat,
    /// Same as `PrivateChat`, but all initial invitees get the same power level as the creator.
    TrustedPrivateChat,
}

impl Room {
    /// Creates a new room in the database.
    pub fn create(
        connection: &PgConnection,
        new_room: &NewRoom,
        homeserver_domain: &str,
        creation_options: &CreationOptions,
    ) -> Result<Room, ApiError> {
        connection.transaction::<Room, ApiError, _>(|| {
            let room: Room = insert(new_room)
                .into(rooms::table)
                .get_result(connection)
                .map_err(ApiError::from)?;

            if let Some(ref alias) = creation_options.alias {
                let new_room_alias = NewRoomAlias {
                    alias: RoomAliasId::try_from(&format!("#{}:{}", alias, homeserver_domain))?,
                    room_id: room.id.clone(),
                    user_id: new_room.user_id.clone(),
                    servers: vec![homeserver_domain.to_string()],
                };

                RoomAlias::create(connection, homeserver_domain, &new_room_alias)?;
            }

            let mut new_events = Vec::new();

            let new_create_event: NewEvent = CreateEvent {
                content: CreateEventContent {
                    creator: new_room.user_id.clone(),
                    federate: creation_options.federate,
                },
                event_id: EventId::new(homeserver_domain)?,
                event_type: EventType::RoomCreate,
                prev_content: None,
                room_id: room.id.clone(),
                state_key: "".to_string(),
                unsigned: None,
                user_id: new_room.user_id.clone(),
            }.try_into()?;

            new_events.push(new_create_event);

            if let Some(ref name) = creation_options.name {
                let new_name_event: NewEvent = NameEvent {
                    content: NameEventContent {
                        name: name.to_string(),
                    },
                    event_id: EventId::new(homeserver_domain)?,
                    event_type: EventType::RoomName,
                    prev_content: None,
                    room_id: room.id.clone(),
                    state_key: "".to_string(),
                    unsigned: None,
                    user_id: new_room.user_id.clone(),
                }.try_into()?;

                new_events.push(new_name_event);
            }

            if let Some(ref topic) = creation_options.topic {
                let new_topic_event: NewEvent = TopicEvent {
                    content: TopicEventContent {
                        topic: topic.to_string(),
                    },
                    event_id: EventId::new(homeserver_domain)?,
                    event_type: EventType::RoomTopic,
                    prev_content: None,
                    room_id: room.id.clone(),
                    state_key: "".to_string(),
                    unsigned: None,
                    user_id: new_room.user_id.clone(),
                }.try_into()?;

                new_events.push(new_topic_event);
            }

            let new_history_visibility_event: NewEvent = HistoryVisibilityEvent {
                content: HistoryVisibilityEventContent {
                    history_visibility: HistoryVisibility::Shared,
                },
                event_id: EventId::new(homeserver_domain)?,
                event_type: EventType::RoomHistoryVisibility,
                prev_content: None,
                room_id: room.id.clone(),
                state_key: "".to_string(),
                unsigned: None,
                user_id: new_room.user_id.clone(),
            }.try_into()?;

            new_events.push(new_history_visibility_event);

            match creation_options.preset {
                RoomPreset::PrivateChat => {
                    let new_join_rules_event: NewEvent = JoinRulesEvent {
                        content: JoinRulesEventContent { join_rule: JoinRule::Invite },
                        event_id: EventId::new(homeserver_domain)?,
                        event_type: EventType::RoomJoinRules,
                        prev_content: None,
                        room_id: room.id.clone(),
                        state_key: "".to_string(),
                        unsigned: None,
                        user_id: new_room.user_id.clone(),
                    }.try_into()?;

                    new_events.push(new_join_rules_event);
                }
                RoomPreset::PublicChat => {
                    let new_join_rules_event: NewEvent = JoinRulesEvent {
                        content: JoinRulesEventContent { join_rule: JoinRule::Public },
                        event_id: EventId::new(homeserver_domain)?,
                        event_type: EventType::RoomJoinRules,
                        prev_content: None,
                        room_id: room.id.clone(),
                        state_key: "".to_string(),
                        unsigned: None,
                        user_id: new_room.user_id.clone(),
                    }.try_into()?;

                    new_events.push(new_join_rules_event);
                }
                RoomPreset::TrustedPrivateChat => {
                    let new_join_rules_event: NewEvent = JoinRulesEvent {
                        content: JoinRulesEventContent { join_rule: JoinRule::Invite },
                        event_id: EventId::new(homeserver_domain)?,
                        event_type: EventType::RoomJoinRules,
                        prev_content: None,
                        room_id: room.id.clone(),
                        state_key: "".to_string(),
                        unsigned: None,
                        user_id: new_room.user_id.clone(),
                    }.try_into()?;

                    new_events.push(new_join_rules_event);
                }
            }

            insert(&new_events)
                .into(events::table)
                .execute(connection)
                .map_err(ApiError::from)?;

            if let Some(ref invite_list) = creation_options.invite_list {
                let mut user_ids = HashSet::with_capacity(invite_list.len());

                for invitee in invite_list {
                    let user_id = UserId::try_from(invitee)?;

                    if user_id.hostname().to_string() != homeserver_domain {
                        return Err(
                            ApiError::unimplemented("Federation is not yet supported.".to_string())
                        );
                    }

                    user_ids.insert(user_id);
                }

                let users: Vec<User> = users::table
                    .filter(users::id.eq(any(
                        user_ids.iter().cloned().collect::<Vec<UserId>>()))
                    )
                    .get_results(connection)
                    .map_err(ApiError::from)?;

                let loaded_user_ids: HashSet<UserId> = users
                    .iter()
                    .map(|user| user.id.clone())
                    .collect();

                let missing_user_ids: Vec<UserId> = user_ids
                    .difference(&loaded_user_ids)
                    .cloned()
                    .collect();

                if missing_user_ids.len() > 0 {
                    return Err(
                        ApiError::bad_json(format!(
                            "Unknown users in invite list: {}",
                            &missing_user_ids
                                .iter()
                                .map(|user_id| user_id.to_string())
                                .collect::<Vec<String>>()
                                .join(", ")
                        ))
                    )
                }

                for user in users {
                    let options = RoomMembershipOptions {
                        room_id: room.id.clone(),
                        user_id: user.id.clone(),
                        sender: room.user_id.clone(),
                        membership: "invite".to_string(),
                    };

                    RoomMembership::create(connection, &homeserver_domain, options)?;
                }
            }

            Ok(room)
        }).map_err(ApiError::from)
    }

    /// Looks up the most recent power levels event for the room.
    ///
    /// If the room does not have a power levels event, a default one is created according to the
    /// specification.
    pub fn current_power_levels(&self, connection: &PgConnection)
    -> Result<PowerLevelsEventContent, ApiError> {
        match events::table
            .filter(events::room_id.eq(self.id.clone()))
            .filter(events::event_type.eq(EventType::RoomPowerLevels.to_string()))
            .order(events::ordering.desc())
            .first::<Event>(connection)
        {
            Ok(event) => {
                let power_levels_event: PowerLevelsEvent = event.try_into()?;

                Ok(power_levels_event.content)
            }
            Err(error) => match error {
                DieselError::NotFound => Ok(PowerLevelsEventContent {
                    ban: 50,
                    events: HashMap::new(),
                    events_default: 0,
                    invite: 50,
                    kick: 50,
                    redact: 50,
                    state_default: 0,
                    users: HashMap::new(),
                    users_default: 0,
                }),
                _ => Err(error.into()),
            },
        }
    }

    /// Look up a `Room` given the `RoomId`.
    pub fn find(connection: &PgConnection, room_id: &RoomId)
    -> Result<Room, ApiError> {
        rooms::table
            .find(room_id)
            .get_result(connection)
            .map(Room::from)
            .map_err(|err| {
                match err {
                    DieselError::NotFound => ApiError::not_found(
                        "The room was not found on this server".to_string()
                    ),
                    _ => ApiError::from(err)
                }
            })
    }
}
<|endoftext|>


<commit_before>use super::*;
use std::collections::{BTreeMap, VecDeque};

pub fn identify_terminals<A>(tree_map: &GornTree<Composition<A>>)
        -> (GornTree<Composition<(Vec<usize>, usize)>>, BTreeMap<(Vec<usize>, usize), A>)
    where A: Clone,
{
    let mut identified_tree_map = GornTree::new();
    let mut terminal_map = BTreeMap::new();

    for (address, composition) in tree_map {
        let vec_compos = &composition.composition;
        let mut identified_compos = Vec::new();
        let mut compos_var_pos = 0;

        for component in vec_compos {
            let mut identified_compon = Vec::new();

            for variable in component {
                match variable {
                    &VarT::Var(x, y) => {
                        identified_compon.push(VarT::Var(x, y));
                    },
                    &VarT::T(ref terminal) => {
                        let terminal_id = (address.clone(), compos_var_pos);
                        identified_compon.push(VarT::T(terminal_id.clone()));
                        terminal_map.insert(terminal_id, terminal.clone());
                    },
                };

                compos_var_pos += 1;
            }

            identified_compos.push(identified_compon);
        }

        identified_tree_map.insert(address.clone(), Composition::from(identified_compos));
    }

    (identified_tree_map, terminal_map)
}

pub fn to_negra<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>, sentence_num: usize)
        -> String
    where H: Clone + ToString,
          T: Clone + ToString,
{
    let negra_vector = to_negra_vector(&tree_map);
    let mut output = format!("#BOS {}\n", sentence_num);

    for (symbol1, symbol2, number) in negra_vector {
        output.push_str(&format!("{}\t{}\t{}\n", symbol1, symbol2, number));
    }

    output.push_str(&format!("#EOS {}", sentence_num));
    output
}

pub fn to_negra_vector<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> Vec<(String, String, usize)>
    where H: Clone + ToString,
          T: Clone + ToString,
{
    let (term_map, nonterminal_map) = to_term(&tree_map);
    // TODO: Enforce negra grammar restrictions
    let (identified_tree_map, terminal_map) = identify_terminals(&term_map);
    let evaluated_compos = evaluate(&identified_tree_map);

    let mut negra_vector = Vec::new();
    let mut rule_queue = VecDeque::new();
    let mut finished_map = GornTree::new();
    let mut rule_counter = 0;

    for component in evaluated_compos.composition {
        for variable in component {
            match variable {
                VarT::Var(_, _) => {
                    panic!("Nonterminals must not appear in a fully evaluated configuration!");
                },
                VarT::T(terminal_id) => {
                    let terminal_symbol = terminal_map.get(&terminal_id).unwrap();
                    let ref address = terminal_id.0;
                    let rule_label = nonterminal_map.get(address).unwrap();
                    let rule_number = get_rule_number(address, &mut rule_queue, &finished_map,
                                                      &mut rule_counter);
                    negra_vector.push((terminal_symbol.to_string(), rule_label.to_string(), rule_number));
                }
            }
        }
    }

    while let Some((address, rule_number)) = rule_queue.pop_front() {
        let mut parent_address = address.clone();

        let parent_number = if let None = parent_address.pop() {
            0
        } else {
            let number = get_rule_number(&parent_address, &mut rule_queue, &finished_map, &mut rule_counter);
            finished_map.insert(parent_address.clone(), number);
            number
        };

        let rule_label = nonterminal_map.get(&address).unwrap();
        negra_vector.push((rule_number.to_string(), rule_label.to_string(), parent_number));
    }

    negra_vector
}

fn get_rule_number(address: &Vec<usize>, rule_queue: &mut VecDeque<(Vec<usize>, usize)>, finished_map: &GornTree<usize>, rule_counter: &mut usize)
        -> usize
{
    if let Some(rule_number) = finished_map.get(address) {
        return *rule_number
    }

    let rule_number = *rule_counter;
    *rule_counter = *rule_counter + 1;
    rule_queue.push_back((address.clone(), rule_number));
    rule_number
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::VarT::{Var, T};

    #[test]
    fn test_identify_terminals() {
        let mut tree_map = GornTree::new();
        tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T("a"), Var(0, 1), T("b")]
        ]));
        tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T("c")]
        ]));
        tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T("d")]
        ]));

        let mut identified_tree_map = GornTree::new();
        identified_tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T((vec![], 1)), Var(0, 1), T((vec![], 3))]
        ]));
        identified_tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T((vec![0], 1))]
        ]));
        identified_tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T((vec![0, 1], 0))]
        ]));

        let mut terminal_map = BTreeMap::new();
        terminal_map.insert((vec![], 1), "a");
        terminal_map.insert((vec![], 3), "b");
        terminal_map.insert((vec![0], 1), "c");
        terminal_map.insert((vec![0, 1], 0), "d");

        assert_eq!((identified_tree_map, terminal_map), identify_terminals(&tree_map));
    }
}
<commit_msg>src/pmcfg/negra.rs: added TermId struct<commit_after>use super::*;
use std::collections::{BTreeMap, VecDeque};

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct TermId {
    address: Vec<usize>,
    compos_var_pos: usize,
}

impl From<(Vec<usize>, usize)> for TermId {
    fn from((address, compos_var_pos): (Vec<usize>, usize)) -> TermId {
        TermId { address, compos_var_pos }
    }
}

pub fn identify_terminals<A>(tree_map: &GornTree<Composition<A>>)
        -> (GornTree<Composition<TermId>>, BTreeMap<TermId, A>)
    where A: Clone,
{
    let mut identified_tree_map = GornTree::new();
    let mut terminal_map = BTreeMap::new();

    for (address, composition) in tree_map {
        let vec_compos = &composition.composition;
        let mut identified_compos = Vec::new();
        let mut compos_var_pos = 0;

        for component in vec_compos {
            let mut identified_compon = Vec::new();

            for variable in component {
                match variable {
                    &VarT::Var(x, y) => {
                        identified_compon.push(VarT::Var(x, y));
                    },
                    &VarT::T(ref terminal) => {
                        let terminal_id = TermId { address: address.clone(), compos_var_pos };
                        identified_compon.push(VarT::T(terminal_id.clone()));
                        terminal_map.insert(terminal_id, terminal.clone());
                    },
                };

                compos_var_pos += 1;
            }

            identified_compos.push(identified_compon);
        }

        identified_tree_map.insert(address.clone(), Composition::from(identified_compos));
    }

    (identified_tree_map, terminal_map)
}

pub fn to_negra<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>, sentence_num: usize)
        -> String
    where H: Clone + ToString,
          T: Clone + ToString,
{
    let negra_vector = to_negra_vector(&tree_map);
    let mut output = format!("#BOS {}\n", sentence_num);

    for (symbol1, symbol2, number) in negra_vector {
        output.push_str(&format!("{}\t{}\t{}\n", symbol1, symbol2, number));
    }

    output.push_str(&format!("#EOS {}", sentence_num));
    output
}

pub fn to_negra_vector<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> Vec<(String, String, usize)>
    where H: Clone + ToString,
          T: Clone + ToString,
{
    let (term_map, nonterminal_map) = to_term(&tree_map);
    // TODO: Enforce negra grammar restrictions
    let (identified_tree_map, terminal_map) = identify_terminals(&term_map);
    let evaluated_compos = evaluate(&identified_tree_map);

    let mut negra_vector = Vec::new();
    let mut rule_queue = VecDeque::new();
    let mut finished_map = GornTree::new();
    let mut rule_counter = 0;

    for component in evaluated_compos.composition {
        for variable in component {
            match variable {
                VarT::Var(_, _) => {
                    panic!("Nonterminals must not appear in a fully evaluated configuration!");
                },
                VarT::T(terminal_id) => {
                    let terminal_symbol = terminal_map.get(&terminal_id).unwrap();
                    let ref address = terminal_id.address;
                    let rule_label = nonterminal_map.get(address).unwrap();
                    let rule_number = get_rule_number(address, &mut rule_queue, &finished_map,
                                                      &mut rule_counter);
                    negra_vector.push((terminal_symbol.to_string(), rule_label.to_string(), rule_number));
                }
            }
        }
    }

    while let Some((address, rule_number)) = rule_queue.pop_front() {
        let mut parent_address = address.clone();

        let parent_number = if let None = parent_address.pop() {
            0
        } else {
            let number = get_rule_number(&parent_address, &mut rule_queue, &finished_map, &mut rule_counter);
            finished_map.insert(parent_address.clone(), number);
            number
        };

        let rule_label = nonterminal_map.get(&address).unwrap();
        negra_vector.push((rule_number.to_string(), rule_label.to_string(), parent_number));
    }

    negra_vector
}

fn get_rule_number(address: &Vec<usize>, rule_queue: &mut VecDeque<(Vec<usize>, usize)>, finished_map: &GornTree<usize>, rule_counter: &mut usize)
        -> usize
{
    if let Some(rule_number) = finished_map.get(address) {
        return *rule_number
    }

    let rule_number = *rule_counter;
    *rule_counter = *rule_counter + 1;
    rule_queue.push_back((address.clone(), rule_number));
    rule_number
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::VarT::{Var, T};

    #[test]
    fn test_identify_terminals() {
        let mut tree_map = GornTree::new();
        tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T("a"), Var(0, 1), T("b")]
        ]));
        tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T("c")]
        ]));
        tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T("d")]
        ]));

        let mut identified_tree_map = GornTree::new();
        identified_tree_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T(TermId::from((vec![], 1))), Var(0, 1), T(TermId::from((vec![], 3)))]
        ]));
        identified_tree_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T(TermId::from((vec![0], 1)))]
        ]));
        identified_tree_map.insert(vec![0, 1], Composition::from(vec![
            vec![T(TermId::from((vec![0, 1], 0)))]
        ]));

        let mut terminal_map = BTreeMap::new();
        terminal_map.insert(TermId::from((vec![], 1)), "a");
        terminal_map.insert(TermId::from((vec![], 3)), "b");
        terminal_map.insert(TermId::from((vec![0], 1)), "c");
        terminal_map.insert(TermId::from((vec![0, 1], 0)), "d");

        assert_eq!((identified_tree_map, terminal_map), identify_terminals(&tree_map));
    }
}
<|endoftext|>


<commit_before>//! Encoding of PPM Images
use std::io;
use std::io::Write;

use color::{ColorType, num_components};
use super::{HeaderRecord, PNMHeader, PNMSubtype, SampleEncoding};
use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};

enum HeaderStrategy {
    Dynamic,
    Subtype(PNMSubtype),
    Chosen(PNMHeader),
}

pub struct PNMEncoder<'a> {
    writer: &'a mut Write,
    header: HeaderStrategy,
}

enum TupleEncoding {
    PbmBits {
        width: u32,
        height: u32,
    },
    U8 {
        encoding: SampleEncoding,
        width: u32,
        height: u32,
        components: u32,
    },
}

impl<'a> PNMEncoder<'a> {
    /// Create new PNMEncoder from the `writer`.
    ///
    /// The encoded images will have some `pnm` format. If more control over the image type is
    /// required, use either one of `with_subtype` or `with_header`. For more information on the
    /// behaviour, see `with_dynamic_header`.
    pub fn new(writer: &'a mut Write) -> Self {
        PNMEncoder {
            writer,
            header: HeaderStrategy::Dynamic,
        }
    }

    /// Encode a specific pnm subtype image.
    ///
    /// The magic number and encoding type will be chosen as provided while the rest of the header
    /// data will be generated dynamically. Trying to encode incompatible images (e.g. encoding an
    /// RGB image as Graymap) will result in an error.
    ///
    /// This will overwrite the effect of earlier calls to `with_header` and `with_dynamic_header`.
    pub fn with_subtype(self, subtype: PNMSubtype) -> Self {
        PNMEncoder {
            writer: self.writer,
            header: HeaderStrategy::Subtype(subtype),
        }
    }

    /// Enforce the use of a chosen header.
    ///
    /// While this option gives the most control over the actual written data, the encoding process
    /// will error in case the header data and image parameters do not agree. It is the users
    /// obligation to ensure that the width and height are set accordingly, for example.
    ///
    /// Choose this option if you want a lossless decoding/encoding round trip.
    ///
    /// This will overwrite the effect of earlier calls to `with_subtype` and `with_dynamic_header`.
    pub fn with_header(self, header: PNMHeader) -> Self {
        PNMEncoder {
            writer: self.writer,
            header: HeaderStrategy::Chosen(header)
        }
    }

    /// Create the header dynamically for each image.
    ///
    /// This is the default option upon creation of the encoder. With this, most images should be
    /// encodable but the specific format chosen is out of the users control. The pnm subtype is
    /// chosen arbitrarily by the library.
    ///
    /// This will overwrite the effect of earlier calls to `with_subtype` and `with_header`.
    pub fn with_dynamic_header(self) -> Self {
        PNMEncoder {
            writer: self.writer,
            header: HeaderStrategy::Dynamic,
        }
    }

    pub fn encode(&mut self, image: &[u8], width: u32, height: u32, color: ColorType)
        -> io::Result<()>
    {
        check_buffer_dimensions(image, width, height, color)?;
        let tuple_encoding = self.write_header(image, width, height, color)?;
        self.write_image(tuple_encoding, image)
    }

    /// Write the header and report back how the pixels should be written.
    fn write_header(&mut self, image: &[u8], width: u32, height: u32, c: ColorType)
        -> io::Result<TupleEncoding>
    {
        match self.header {
            HeaderStrategy::Dynamic
                => self.write_dynamic_header(width, height, c),
            HeaderStrategy::Subtype(subtype)
                => self.write_subtyped_header(subtype, width, height, c),
            HeaderStrategy::Chosen(ref header)
                => self.write_chosen_header(header, width, height, c),
        }
    }

    /// Choose any valid pnm format that the image can be expressed in and write its header.
    ///
    /// Returns how the body should be written if successful.
    fn write_dynamic_header(&mut self, width: u32, height: u32, color: ColorType)
        -> io::Result<TupleEncoding>
    {
        let depth = num_components(color) as u32;
        let (maxval, tupltype) = match color {
            ColorType::Gray(1) => (1, ArbitraryTuplType::BlackAndWhite),
            ColorType::GrayA(1) => (1, ArbitraryTuplType::BlackAndWhiteAlpha),
            ColorType::Gray(8) => (255, ArbitraryTuplType::Grayscale),
            ColorType::GrayA(8) => (255, ArbitraryTuplType::GrayscaleAlpha),
            ColorType::RGB(8) => (255, ArbitraryTuplType::RBG),
            ColorType::RGBA(8) => (255, ArbitraryTuplType::RGBAlpha),
            _ => return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    &format!("Writing colour type {:?} is not supported", color)[..]))
        };

        self.write_arbitrary_header(&ArbitraryHeader {
            width,
            height,
            depth,
            maxval,
            tupltype: Some(tupltype),
        })?;

        Ok(TupleEncoding::U8 {
            encoding: SampleEncoding::Binary,
            width,
            height,
            components: depth,
        })
    }

    /// Try to encode the image with the chosen format, give its corresponding pixel encoding type.
    fn write_subtyped_header(
        &mut self,
        subtype: PNMSubtype,
        width: u32,
        height: u32,
        color: ColorType)
        -> io::Result<TupleEncoding>
    {
        match (subtype, color) {
            (PNMSubtype::ArbitraryMap, color) => self.write_dynamic_header(width, height, color),
            (PNMSubtype::Pixmap(encoding), ColorType::RGB(8)) => {
                self.write_pixmap_header(&PixmapHeader {
                    encoding,
                    width,
                    height,
                    maxval: 255,
                }).map(|()| TupleEncoding::U8 {
                    encoding,
                    width,
                    height,
                    components: 3, // num_components(RGB(8)) == 3
                })
            },
            (PNMSubtype::Graymap(encoding), ColorType::Gray(8)) => {
                self.write_graymap_header(&GraymapHeader {
                    encoding,
                    width,
                    height,
                    maxwhite: 255,
                }).map(|()| TupleEncoding::U8 {
                    encoding,
                    width,
                    height,
                    components: 1, // num_components(Gray(8)) == 1
                })
            },
            (PNMSubtype::Bitmap(encoding), ColorType::Gray(8))
            | (PNMSubtype::Bitmap(encoding), ColorType::Gray(1)) => {
                self.write_bitmap_header(&BitmapHeader {
                    encoding: encoding,
                    width,
                    height,
                })?;
                match encoding {
                    SampleEncoding::Binary => Ok(TupleEncoding::PbmBits {
                        width,
                        height,
                    }),
                    SampleEncoding::Ascii => Ok(TupleEncoding::U8 {
                        encoding: SampleEncoding::Ascii,
                        width,
                        height,
                        components: 1,
                    })
                }
            },
            (_, _) => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Color type can not be represented in the chosen format"))
        }
    }

    /// Try to encode the image with the chosen header, checking if values are correct.
    ///
    /// Returns how the body should be written if successful.
    fn write_chosen_header(
        &mut self,
        header: &PNMHeader,
        width: u32,
        height: u32,
        color: ColorType)
        -> io::Result<TupleEncoding>
    {
        let tupltype = check_chosen_header(&header, width, height, color)?;

        match header {
            &PNMHeader { encoded: Some(ref content), .. }
                => self.writer.write_all(content)?,
            &PNMHeader { decoded: HeaderRecord::Bitmap(ref header), .. }
                => self.write_bitmap_header(header)?,
            &PNMHeader { decoded: HeaderRecord::Graymap(ref header), .. }
                => self.write_graymap_header(header)?,
            &PNMHeader { decoded: HeaderRecord::Pixmap(ref header), .. }
                => self.write_pixmap_header(header)?,
            &PNMHeader { decoded: HeaderRecord::Arbitrary(ref header), .. }
                => self.write_arbitrary_header(header)?,
        };

        Ok(tupltype)
    }

    fn write_bitmap_header(&mut self, &BitmapHeader{ encoding, width, height }: &BitmapHeader)
        -> io::Result<()>
    {
        self.writer.write_all(PNMSubtype::Bitmap(encoding).magic_constant())?;
        write!(self.writer, "\n{} {}\n", width, height)
    }

    fn write_graymap_header(
        &mut self,
        &GraymapHeader{ encoding, width, height, maxwhite}: &GraymapHeader)
        -> io::Result<()>
    {
        self.writer.write_all(PNMSubtype::Graymap(encoding).magic_constant())?;
        write!(self.writer, "\n{} {} {}\n", width, height, maxwhite)
    }

    fn write_pixmap_header(
        &mut self,
        &PixmapHeader{ encoding, width, height, maxval}: &PixmapHeader)
        -> io::Result<()>
    {
        self.writer.write_all(PNMSubtype::Pixmap(encoding).magic_constant())?;
        write!(self.writer, "\n{} {} {}\n", width, height, maxval)
    }

    fn write_arbitrary_header(&mut self, &ArbitraryHeader{ width, height, depth, maxval, tupltype}: &ArbitraryHeader)
        -> io::Result<()>
    {
        let custom_fallback = String::new();
        let tupltype = match tupltype {
            None => "",
            Some(ArbitraryTuplType::BlackAndWhite) => "TUPLTYPE BLACKANDWHITE\n",
            Some(ArbitraryTuplType::BlackAndWhiteAlpha) => "TUPLTYPE BLACKANDWHITE_ALPHA\n",
            Some(ArbitraryTuplType::Grayscale) => "TUPLTYPE GRAYSCALE\n",
            Some(ArbitraryTuplType::GrayscaleAlpha) => "TUPLTYPE GRAYSCALE_ALPHA\n",
            Some(ArbitraryTuplType::RBG) => "TUPLTYPE RGB\n",
            Some(ArbitraryTuplType::RGBAlpha) => "TUPLTYPE RGB_ALPHA\n",
            Some(ArbitraryTuplType::Custom(ref custom)) => {
                custom_fallback = format!("TUPLTYPE {}", custom);
                &custom_fallback
            }
        };

        write!(self.writer, "P7\nWIDTH {}\nHEIGHT {}\nDEPTH {}\nMAXVAL {}\n{}ENDHDR",
            width, height, depth, maxval, tupltype)
    }

    fn write_image(&mut self, encoding: TupleEncoding, image: &[u8]) -> io::Result<()> {
        match encoding {
            TupleEncoding::U8 { encoding: SampleEncoding::Binary, .. }
                => self.writer.write_all(image),
            TupleEncoding::U8 { encoding: SampleEncoding::Ascii, .. } => {
                // Standard dictates to insert line breaks after 70 characters. Assumes that no
                // line breaks are written.
                struct AutoBreak<'a> {
                    wrapped: io::LineWriter<&'a mut Write>,
                    line_capacity: usize,
                    buffer: Vec<u8>,
                    needs_flush: bool,
                }

                impl<'a> Write for AutoBreak<'a> {
                    fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
                        if self.needs_flush {
                            self.flush()?;
                            assert!(self.buffer.len() == 0);
                        }

                        // Buffer may be longer than the capacity but there's no way around
                        if self.buffer.len() == 0 {
                            self.buffer.extend_from_slice(buffer);
                            return Ok(buffer.len());
                        }

                        if buffer.len() + self.buffer.len() > self.line_capacity {
                            self.needs_flush = true;
                            self.buffer.push(b'\n');
                            self.flush()?;
                            assert!(self.buffer.len() == 0);
                        }

                        self.buffer.extend_from_slice(buffer);
                        Ok(buffer.len())
                    }

                    fn flush(&mut self) -> io::Result<()> {
                        let written = self.wrapped.write(self.buffer.as_slice())?;

                        self.buffer.drain(0..written);
                        if self.buffer.len() > 0 {
                            return Err(io::Error::new(
                                io::ErrorKind::Interrupted,
                                "Line was not fully flushed"))
                        }

                        self.needs_flush = false;
                        self.wrapped.flush()
                    }
                }

                let auto_break_writer = AutoBreak {
                    wrapped: io::LineWriter::new(self.writer),
                    line_capacity: 70,
                    buffer: Vec::with_capacity(71),
                    needs_flush: false,
                };

                for value in image.iter() {
                    write!(auto_break_writer, "{} ", value)?;
                }
                Ok(())
            },
            TupleEncoding::PbmBits { width, height } => {
                /// These should have thrown errors previously.
                assert_eq!(width*height, image.len());
                assert!(image.len() > 0);

                let line_width = (width - 1)/8 + 1;

                // We'll be writing single bytes, so buffer
                let line_buffer = Vec::with_capacity(line_width as usize);

                for line in image.chunks(width as usize) {
                    for byte_bits in line.chunks(8) {
                        let byte = 0u8;
                        for i in 0..8 {
                            // Black pixels are encoded as 1s
                            if let Some(&0u8) = byte_bits.get(i) {
                                byte |= 1u8 << (7 - i)
                            }
                        }
                        line_buffer.push(byte)
                    }
                    self.writer.write_all(line_buffer.as_slice());
                    line_buffer.clear();
                }

                Ok(())
            },
            // _ => panic!()
        }

    }
}

fn check_buffer_dimensions(image: &[u8], width: u32, height: u32, color: ColorType)
    -> io::Result<()>
{
    let components = num_components(color);
    let width = width as usize;
    let height = height as usize;
    match Some(components)
        .and_then(|v| v.checked_mul(width))
        .and_then(|v| v.checked_mul(height)) {
        None => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            &format!("Image dimensions invalid: {}x{}x{} (wxhxd)",
                width, height, components)[..])),
        Some(v) if v != image.len()
            => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                &format!("Image buffer does not correspond to size and colour")[..])),
        Some(v) => Ok(())
    }
}

fn check_chosen_header(header: &PNMHeader, width: u32, height: u32, color: ColorType)
    -> io::Result<TupleEncoding>
{
    if header.width() != width || header.height() != height {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Chosen header does not match Image dimensions"));
    }

    // FIXME check other conditions …
    // if header

    Ok(TupleEncoding::U8 {
        encoding: SampleEncoding::Binary,
        width,
        height,
        components: num_components(color) as u32,
    })
}
<commit_msg>Fix borrowing issues and cleanup some warnings<commit_after>//! Encoding of PPM Images
use std::io;
use std::io::Write;

use color::{ColorType, num_components};
use super::{HeaderRecord, PNMHeader, PNMSubtype, SampleEncoding};
use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};

enum HeaderStrategy {
    Dynamic,
    Subtype(PNMSubtype),
    Chosen(PNMHeader),
}

/// Encodes images to any of the `pnm` image formats.
pub struct PNMEncoder<'a> {
    writer: &'a mut Write,
    header: HeaderStrategy,
}

enum TupleEncoding {
    PbmBits {
        width: u32,
        height: u32,
    },
    U8 {
        encoding: SampleEncoding,
        // We don't need the fields since we just loop over all samples.
        // width: u32,
        // height: u32,
        // components: u32,
    },
}

impl<'a> PNMEncoder<'a> {
    /// Create new PNMEncoder from the `writer`.
    ///
    /// The encoded images will have some `pnm` format. If more control over the image type is
    /// required, use either one of `with_subtype` or `with_header`. For more information on the
    /// behaviour, see `with_dynamic_header`.
    pub fn new(writer: &'a mut Write) -> Self {
        PNMEncoder {
            writer,
            header: HeaderStrategy::Dynamic,
        }
    }

    /// Encode a specific pnm subtype image.
    ///
    /// The magic number and encoding type will be chosen as provided while the rest of the header
    /// data will be generated dynamically. Trying to encode incompatible images (e.g. encoding an
    /// RGB image as Graymap) will result in an error.
    ///
    /// This will overwrite the effect of earlier calls to `with_header` and `with_dynamic_header`.
    pub fn with_subtype(self, subtype: PNMSubtype) -> Self {
        PNMEncoder {
            writer: self.writer,
            header: HeaderStrategy::Subtype(subtype),
        }
    }

    /// Enforce the use of a chosen header.
    ///
    /// While this option gives the most control over the actual written data, the encoding process
    /// will error in case the header data and image parameters do not agree. It is the users
    /// obligation to ensure that the width and height are set accordingly, for example.
    ///
    /// Choose this option if you want a lossless decoding/encoding round trip.
    ///
    /// This will overwrite the effect of earlier calls to `with_subtype` and `with_dynamic_header`.
    pub fn with_header(self, header: PNMHeader) -> Self {
        PNMEncoder {
            writer: self.writer,
            header: HeaderStrategy::Chosen(header)
        }
    }

    /// Create the header dynamically for each image.
    ///
    /// This is the default option upon creation of the encoder. With this, most images should be
    /// encodable but the specific format chosen is out of the users control. The pnm subtype is
    /// chosen arbitrarily by the library.
    ///
    /// This will overwrite the effect of earlier calls to `with_subtype` and `with_header`.
    pub fn with_dynamic_header(self) -> Self {
        PNMEncoder {
            writer: self.writer,
            header: HeaderStrategy::Dynamic,
        }
    }

    /// Encode an image whose samples are represented as `u8`.
    ///
    /// Some `pnm` subtypes are incompatible with some color options, a chosen header most
    /// certainly with any deviation from the original decoded image.
    pub fn encode(&mut self, image: &[u8], width: u32, height: u32, color: ColorType)
        -> io::Result<()>
    {
        check_buffer_dimensions(image, width, height, color)?;
        let tuple_encoding = self.write_header(image, width, height, color)?;
        self.write_image(tuple_encoding, image)
    }

    /// Write the header and report back how the pixels should be written.
    fn write_header(&mut self, image: &[u8], width: u32, height: u32, color: ColorType)
        -> io::Result<TupleEncoding>
    {
        match self.header {
            HeaderStrategy::Dynamic
                => self.write_dynamic_header(width, height, color),
            HeaderStrategy::Subtype(subtype)
                => self.write_subtyped_header(subtype, width, height, color),
            HeaderStrategy::Chosen(ref header)
                => Self::write_chosen_header(&mut self.writer, header, width, height, color),
        }
    }

    /// Choose any valid pnm format that the image can be expressed in and write its header.
    ///
    /// Returns how the body should be written if successful.
    fn write_dynamic_header(&mut self, width: u32, height: u32, color: ColorType)
        -> io::Result<TupleEncoding>
    {
        let depth = num_components(color) as u32;
        let (maxval, tupltype) = match color {
            ColorType::Gray(1) => (1, ArbitraryTuplType::BlackAndWhite),
            ColorType::GrayA(1) => (1, ArbitraryTuplType::BlackAndWhiteAlpha),
            ColorType::Gray(8) => (255, ArbitraryTuplType::Grayscale),
            ColorType::GrayA(8) => (255, ArbitraryTuplType::GrayscaleAlpha),
            ColorType::RGB(8) => (255, ArbitraryTuplType::RBG),
            ColorType::RGBA(8) => (255, ArbitraryTuplType::RGBAlpha),
            _ => return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    &format!("Writing colour type {:?} is not supported", color)[..]))
        };

        Self::write_arbitrary_header(&mut self.writer, &ArbitraryHeader {
            width,
            height,
            depth,
            maxval,
            tupltype: Some(tupltype),
        })?;

        Ok(TupleEncoding::U8 {
            encoding: SampleEncoding::Binary,
        })
    }

    /// Try to encode the image with the chosen format, give its corresponding pixel encoding type.
    fn write_subtyped_header(
        &mut self,
        subtype: PNMSubtype,
        width: u32,
        height: u32,
        color: ColorType)
        -> io::Result<TupleEncoding>
    {
        match (subtype, color) {
            (PNMSubtype::ArbitraryMap, color) => self.write_dynamic_header(width, height, color),
            (PNMSubtype::Pixmap(encoding), ColorType::RGB(8)) => {
                Self::write_pixmap_header(&mut self.writer, &PixmapHeader {
                    encoding,
                    width,
                    height,
                    maxval: 255,
                }).map(|()| TupleEncoding::U8 {
                    encoding,
                })
            },
            (PNMSubtype::Graymap(encoding), ColorType::Gray(8)) => {
                Self::write_graymap_header(&mut self.writer, &GraymapHeader {
                    encoding,
                    width,
                    height,
                    maxwhite: 255,
                }).map(|()| TupleEncoding::U8 {
                    encoding,
                })
            },
            (PNMSubtype::Bitmap(encoding), ColorType::Gray(8))
            | (PNMSubtype::Bitmap(encoding), ColorType::Gray(1)) => {
                Self::write_bitmap_header(&mut self.writer, &BitmapHeader {
                    encoding: encoding,
                    width,
                    height,
                })?;
                match encoding {
                    SampleEncoding::Binary => Ok(TupleEncoding::PbmBits {
                        width,
                        height,
                    }),
                    SampleEncoding::Ascii => Ok(TupleEncoding::U8 {
                        encoding: SampleEncoding::Ascii,
                    })
                }
            },
            (_, _) => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Color type can not be represented in the chosen format"))
        }
    }

    /// Try to encode the image with the chosen header, checking if values are correct.
    ///
    /// Returns how the body should be written if successful.
    fn write_chosen_header(
        writer: &mut Write,
        header: &PNMHeader,
        width: u32,
        height: u32,
        color: ColorType)
        -> io::Result<TupleEncoding>
    {
        let tupltype = check_chosen_header(&header, width, height, color)?;

        match header {
            &PNMHeader { encoded: Some(ref content), .. }
                => writer.write_all(content)?,
            &PNMHeader { decoded: HeaderRecord::Bitmap(ref header), .. }
                => Self::write_bitmap_header(writer, header)?,
            &PNMHeader { decoded: HeaderRecord::Graymap(ref header), .. }
                => Self::write_graymap_header(writer, header)?,
            &PNMHeader { decoded: HeaderRecord::Pixmap(ref header), .. }
                => Self::write_pixmap_header(writer, header)?,
            &PNMHeader { decoded: HeaderRecord::Arbitrary(ref header), .. }
                => Self::write_arbitrary_header(writer, header)?,
        };

        Ok(tupltype)
    }

    fn write_bitmap_header(writer: &mut Write, &BitmapHeader{ encoding, width, height }: &BitmapHeader)
        -> io::Result<()>
    {
        writer.write_all(PNMSubtype::Bitmap(encoding).magic_constant())?;
        write!(writer, "\n{} {}\n", width, height)
    }

    fn write_graymap_header(
        writer: &mut Write,
        &GraymapHeader{ encoding, width, height, maxwhite}: &GraymapHeader)
        -> io::Result<()>
    {
        writer.write_all(PNMSubtype::Graymap(encoding).magic_constant())?;
        write!(writer, "\n{} {} {}\n", width, height, maxwhite)
    }

    fn write_pixmap_header(
        writer: &mut Write,
        &PixmapHeader{ encoding, width, height, maxval}: &PixmapHeader)
        -> io::Result<()>
    {
        writer.write_all(PNMSubtype::Pixmap(encoding).magic_constant())?;
        write!(writer, "\n{} {} {}\n", width, height, maxval)
    }

    fn write_arbitrary_header(writer: &mut Write, &ArbitraryHeader{ width, height, depth, maxval, ref tupltype}: &ArbitraryHeader)
        -> io::Result<()>
    {
        #[allow(unused_assignments)]
        // Declared here so its lifetime exceeds the matching. This is a trivial constructor, no
        // allocation takes place and in the custom case we must allocate regardless due to borrow.
        let mut custom_fallback = String::new();

        let tupltype = match tupltype {
            &None => "",
            &Some(ArbitraryTuplType::BlackAndWhite) => "TUPLTYPE BLACKANDWHITE\n",
            &Some(ArbitraryTuplType::BlackAndWhiteAlpha) => "TUPLTYPE BLACKANDWHITE_ALPHA\n",
            &Some(ArbitraryTuplType::Grayscale) => "TUPLTYPE GRAYSCALE\n",
            &Some(ArbitraryTuplType::GrayscaleAlpha) => "TUPLTYPE GRAYSCALE_ALPHA\n",
            &Some(ArbitraryTuplType::RBG) => "TUPLTYPE RGB\n",
            &Some(ArbitraryTuplType::RGBAlpha) => "TUPLTYPE RGB_ALPHA\n",
            &Some(ArbitraryTuplType::Custom(ref custom)) => {
                custom_fallback = format!("TUPLTYPE {}", custom);
                &custom_fallback
            }
        };

        write!(writer, "P7\nWIDTH {}\nHEIGHT {}\nDEPTH {}\nMAXVAL {}\n{}ENDHDR",
            width, height, depth, maxval, tupltype)
    }

    fn write_image(&mut self, encoding: TupleEncoding, image: &[u8]) -> io::Result<()> {
        match encoding {
            TupleEncoding::U8 { encoding: SampleEncoding::Binary, .. }
                => self.writer.write_all(image),
            TupleEncoding::U8 { encoding: SampleEncoding::Ascii, .. } => {
                // Standard dictates to insert line breaks after 70 characters. Assumes that no
                // line breaks are written.
                struct AutoBreak<'a> {
                    wrapped: io::LineWriter<&'a mut Write>,
                    line_capacity: usize,
                    buffer: Vec<u8>,
                    needs_flush: bool,
                }

                impl<'a> Write for AutoBreak<'a> {
                    fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
                        if self.needs_flush {
                            self.flush()?;
                            assert!(self.buffer.len() == 0);
                        }

                        // Buffer may be longer than the capacity but there's no way around
                        if self.buffer.len() == 0 {
                            self.buffer.extend_from_slice(buffer);
                            return Ok(buffer.len());
                        }

                        if buffer.len() + self.buffer.len() > self.line_capacity {
                            self.needs_flush = true;
                            self.buffer.push(b'\n');
                            self.flush()?;
                            assert!(self.buffer.len() == 0);
                        }

                        self.buffer.extend_from_slice(buffer);
                        Ok(buffer.len())
                    }

                    fn flush(&mut self) -> io::Result<()> {
                        let written = self.wrapped.write(self.buffer.as_slice())?;

                        self.buffer.drain(0..written);
                        if self.buffer.len() > 0 {
                            return Err(io::Error::new(
                                io::ErrorKind::Interrupted,
                                "Line was not fully flushed"))
                        }

                        self.needs_flush = false;
                        self.wrapped.flush()
                    }
                }

                let mut auto_break_writer = AutoBreak {
                    wrapped: io::LineWriter::new(self.writer),
                    line_capacity: 70,
                    buffer: Vec::with_capacity(71),
                    needs_flush: false,
                };

                for value in image.iter() {
                    write!(auto_break_writer, "{} ", value)?;
                }
                Ok(())
            },
            TupleEncoding::PbmBits { width, .. /* height implicitely given by buffer */ } => {
                /// These should have thrown errors previously.
                assert!(image.len() > 0);
                assert!(image.len() % (width as usize) == 0);

                // The length of an encoded scanline
                let line_width = (width - 1)/8 + 1;

                // We'll be writing single bytes, so buffer
                let mut line_buffer = Vec::with_capacity(line_width as usize);

                for line in image.chunks(width as usize) {
                    for byte_bits in line.chunks(8) {
                        let mut byte = 0u8;
                        for i in 0..8 {
                            // Black pixels are encoded as 1s
                            if let Some(&0u8) = byte_bits.get(i) {
                                byte |= 1u8 << (7 - i)
                            }
                        }
                        line_buffer.push(byte)
                    }
                    self.writer.write_all(line_buffer.as_slice())?;
                    line_buffer.clear();
                }

                Ok(())
            },
        }

    }
}

fn check_buffer_dimensions(image: &[u8], width: u32, height: u32, color: ColorType)
    -> io::Result<()>
{
    let components = num_components(color);
    let width = width as usize;
    let height = height as usize;
    match Some(components)
        .and_then(|v| v.checked_mul(width))
        .and_then(|v| v.checked_mul(height)) {
        None => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            &format!("Image dimensions invalid: {}×{}×{} (w×h×d)",
                width, height, components)[..])),
        Some(v) if v == image.len()
            => Ok(()),
        Some(_)
            => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                &format!("Image buffer does not correspond to size and colour")[..])),
    }
}

fn check_chosen_header(header: &PNMHeader, width: u32, height: u32, color: ColorType)
    -> io::Result<TupleEncoding>
{
    if header.width() != width || header.height() != height {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Chosen header does not match Image dimensions"));
    }

    // FIXME check component count, maxvalues and sample type …

    Ok(TupleEncoding::U8 {
        encoding: SampleEncoding::Binary,
    })
}
<|endoftext|>


<commit_before>//! Get project by id.
//!
//! https://docs.gitlab.com/ce/api/projects.html#get-single-project
//!
//! # Get single project
//!
//! Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME, which is owned by
//! the authenticated user.
//! If using namespaced projects call make sure that the NAMESPACE/PROJECT_NAME is URL-encoded,
//! eg. `/api/v3/projects/diaspora%2Fdiaspora` (where `/` is represented by `%2F`).
//!
//! ```text
//! GET /projects/ID
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |

use BuildQuery;

#[derive(Debug, Clone)]
pub enum ListingId {
    Id(i64),
    NamespaceProject(String),
}

#[derive(Default, Debug, Clone)]
pub struct Listing {
    /// The ID or NAMESPACE/PROJECT_NAME of the project
    id:  Option<ListingId>,
}

impl Listing {
    pub fn new(id: ListingId) -> Listing {
        Listing { id: Some(id) }
    }
}

impl BuildQuery for Listing {
    fn build_query(&self) -> String {

        let mut query = String::from("projects/");

        self.id.clone().map(|id| {
            query.push_str(&match id {
                ListingId::Id(id) => id.to_string(),
                ListingId::NamespaceProject(s) => s.replace("/", "%2F"),
            });
        });

        query
    }
}

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

    const TEST_PROJECT_ID: i64 = 123;
    const TEST_PROJECT_NAME: &'static str = "group/project";

    #[test]
    fn projects_build_query_id() {
        let expected_string = format!("projects/{}", TEST_PROJECT_ID);
        let query = Listing::new(ListingId::Id(123)).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}", TEST_PROJECT_NAME.to_string().replace("/", "%2F"));
        let query = Listing::new(ListingId::NamespaceProject(TEST_PROJECT_NAME.to_string())).build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Cleanup<commit_after>//! Get project by id.
//!
//! https://docs.gitlab.com/ce/api/projects.html#get-single-project
//!
//! # Get single project
//!
//! Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME, which is owned by
//! the authenticated user.
//! If using namespaced projects call make sure that the NAMESPACE/PROJECT_NAME is URL-encoded,
//! eg. `/api/v3/projects/diaspora%2Fdiaspora` (where `/` is represented by `%2F`).
//!
//! ```text
//! GET /projects/ID
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |

use BuildQuery;

#[derive(Debug, Clone)]
pub enum ListingId {
    Id(i64),
    NamespaceProject(String),
}

#[derive(Default, Debug, Clone)]
pub struct Listing {
    /// The ID or NAMESPACE/PROJECT_NAME of the project
    id:  Option<ListingId>,
}

impl Listing {
    pub fn new(id: ListingId) -> Listing {
        Listing { id: Some(id) }
    }
}

impl BuildQuery for Listing {
    fn build_query(&self) -> String {

        let mut query = String::from("projects/");

        self.id.clone().map(|id| {
            query.push_str(&match id {
                ListingId::Id(id) => id.to_string(),
                ListingId::NamespaceProject(s) => s.replace("/", "%2F"),
            });
        });

        query
    }
}

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

    const TEST_PROJECT_ID: i64 = 123;
    const TEST_PROJECT_NAME: &'static str = "group/project";

    #[test]
    fn projects_build_query_id() {
        let expected_string = format!("projects/{}", TEST_PROJECT_ID);
        let query = Listing::new(ListingId::Id(123)).build_query();
        assert_eq!(query, expected_string);

        let expected_string = format!("projects/{}", TEST_PROJECT_NAME.to_string().replace("/", "%2F"));
        let query = Listing::new(ListingId::NamespaceProject(TEST_PROJECT_NAME.to_string())).build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>