<commit_before>use std;
use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SpotifyAudioType {
    Track,
    Podcast,
    NonPlayable,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpotifyId {
    pub id: u128,
    pub audio_type: SpotifyAudioType,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SpotifyIdError;

const BASE62_DIGITS: &'static [u8] =
    b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE16_DIGITS: &'static [u8] = b"0123456789abcdef";

impl SpotifyId {
    fn as_track(n: u128) -> SpotifyId {
        SpotifyId {
            id: n.to_owned(),
            audio_type: SpotifyAudioType::Track,
        }
    }

    pub fn from_base16(id: &str) -> Result<SpotifyId, SpotifyIdError> {
        let data = id.as_bytes();

        let mut n = 0u128;
        for c in data {
            let d = match BASE16_DIGITS.iter().position(|e| e == c) {
                None => return Err(SpotifyIdError),
                Some(x) => x as u128,
            };
            n = n * 16;
            n = n + d;
        }

        Ok(SpotifyId::as_track(n))
    }

    pub fn from_base62(id: &str) -> Result<SpotifyId, SpotifyIdError> {
        let data = id.as_bytes();

        let mut n = 0u128;
        for c in data {
            let d = match BASE62_DIGITS.iter().position(|e| e == c) {
                None => return Err(SpotifyIdError),
                Some(x) => x as u128,
            };
            n = n * 62;
            n = n + d;
        }
        Ok(SpotifyId::as_track(n))
    }

    pub fn from_raw(data: &[u8]) -> Result<SpotifyId, SpotifyIdError> {
        if data.len() != 16 {
            return Err(SpotifyIdError);
        };

        let mut arr: [u8; 16] = Default::default();
        arr.copy_from_slice(&data[0..16]);

        Ok(SpotifyId::as_track(u128::from_be_bytes(arr)))
    }

    pub fn from_uri(uri: &str) -> Result<SpotifyId, SpotifyIdError> {
        let parts = uri.split(":").collect::<Vec<&str>>();
        let gid = parts.last().unwrap();
        if uri.contains(":episode:") {
            let mut spotify_id = SpotifyId::from_base62(gid).unwrap();
            let _ = std::mem::replace(&mut spotify_id.audio_type, SpotifyAudioType::Podcast);
            Ok(spotify_id)
        } else if uri.contains(":track:") {
            SpotifyId::from_base62(gid)
        } else {
            // show/playlist/artist/album/??
            let mut spotify_id = SpotifyId::from_base62(gid).unwrap();
            let _ = std::mem::replace(&mut spotify_id.audio_type, SpotifyAudioType::NonPlayable);
            Ok(spotify_id)
        }
    }

    pub fn to_base16(&self) -> String {
        format!("{:032x}", self.id)
    }

    pub fn to_base62(&self) -> String {
        let &SpotifyId { id: mut n, .. } = self;

        let mut data = [0u8; 22];
        for i in 0..22 {
            data[21 - i] = BASE62_DIGITS[(n % 62) as usize];
            n /= 62;
        }

        std::str::from_utf8(&data).unwrap().to_owned()
    }

    pub fn to_uri(&self) -> String {
        match self.audio_type {
            SpotifyAudioType::Track => format!("spotify:track:{}", self.to_base62()),
            SpotifyAudioType::Podcast => format!("spotify:episode:{}", self.to_base62()),
            SpotifyAudioType::NonPlayable => format!("spotify:unknown:{}", self.to_base62()),
        }
    }

    pub fn to_raw(&self) -> [u8; 16] {
        self.id.to_be_bytes()
    }
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub [u8; 20]);

impl FileId {
    pub fn to_base16(&self) -> String {
        self.0
            .iter()
            .map(|b| format!("{:02x}", b))
            .collect::<Vec<String>>()
            .concat()
    }
}

impl fmt::Debug for FileId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple("FileId").field(&self.to_base16()).finish()
    }
}

impl fmt::Display for FileId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.to_base16())
    }
}
<commit_msg>Refactor SpotifyId<commit_after>use std;
use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SpotifyAudioType {
    Track,
    Podcast,
    NonPlayable,
}

impl SpotifyAudioType {
    fn from_str(src: &str) -> SpotifyAudioType {
        match src {
            "track" => SpotifyAudioType::Track,
            "episode" => SpotifyAudioType::Podcast,
            _ => SpotifyAudioType::NonPlayable,
        }
    }

    fn to_str(self) -> &'static str {
        match self {
            SpotifyAudioType::Track => "track",
            SpotifyAudioType::Podcast => "episode",
            SpotifyAudioType::NonPlayable => "unknown",
        }
    }

    fn len(self) -> usize {
        match self {
            SpotifyAudioType::Track => 5,
            SpotifyAudioType::Podcast => 7,
            SpotifyAudioType::NonPlayable => 7,
        }
    }
}

impl std::convert::From<&str> for SpotifyAudioType {
    fn from(v: &str) -> Self {
        SpotifyAudioType::from_str(v)
    }
}

impl std::convert::Into<&str> for SpotifyAudioType {
    fn into(self) -> &'static str {
        self.to_str()
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpotifyId {
    pub id: u128,
    pub audio_type: SpotifyAudioType,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SpotifyIdError;

const BASE62_DIGITS: &[u8; 62] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE16_DIGITS: &[u8; 16] = b"0123456789abcdef";

impl SpotifyId {
    const SIZE: usize = 16;
    const SIZE_BASE16: usize = 32;
    const SIZE_BASE62: usize = 22;

    fn as_track(n: u128) -> SpotifyId {
        SpotifyId {
            id: n,
            audio_type: SpotifyAudioType::Track,
        }
    }

    /// Parses a base16 (hex) encoded [Spotify ID] into a `SpotifyId`.
    ///
    /// `src` is expected to be 32 bytes long and encoded using valid characters.
    ///
    /// [Spotify ID]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
    pub fn from_base16(src: &str) -> Result<SpotifyId, SpotifyIdError> {
        let mut dst: u128 = 0;

        for c in src.as_bytes() {
            let p = match c {
                b'0'..=b'9' => c - b'0',
                b'a'..=b'f' => c - b'a' + 10,
                _ => return Err(SpotifyIdError),
            } as u128;

            dst <<= 4;
            dst += p;
        }

        Ok(SpotifyId::as_track(dst))
    }

    /// Parses a base62 encoded [Spotify ID] into a `SpotifyId`.
    ///
    /// `src` is expected to be 22 bytes long and encoded using valid characters.
    ///
    /// [Spotify ID]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
    pub fn from_base62(src: &str) -> Result<SpotifyId, SpotifyIdError> {
        let mut dst: u128 = 0;

        for c in src.as_bytes() {
            let p = match c {
                b'0'..=b'9' => c - b'0',
                b'a'..=b'z' => c - b'a' + 10,
                b'A'..=b'Z' => c - b'A' + 36,
                _ => return Err(SpotifyIdError),
            } as u128;

            dst *= 62;
            dst += p;
        }

        Ok(SpotifyId::as_track(dst))
    }

    /// Creates a `SpotifyId` from a copy of `SpotifyId::SIZE` (16) bytes in big-endian order.
    ///
    /// The resulting `SpotifyId` will default to a `SpotifyAudioType::TRACK`.
    pub fn from_raw(src: &[u8]) -> Result<SpotifyId, SpotifyIdError> {
        if src.len() != SpotifyId::SIZE {
            return Err(SpotifyIdError);
        };

        let mut dst = [0u8; SpotifyId::SIZE];
        dst.copy_from_slice(src);

        Ok(SpotifyId::as_track(u128::from_be_bytes(dst)))
    }

    /// Parses a [Spotify URI] into a `SpotifyId`.
    ///
    /// `uri` is expected to be in the canonical form `spotify:{type}:{id}`, where `{type}`
    /// can be arbitrary while `{id}` is a 22-character long, base62 encoded Spotify ID.
    ///
    /// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
    pub fn from_uri(src: &str) -> Result<SpotifyId, SpotifyIdError> {
        // We expect the ID to be the last colon-delimited item in the URI.
        let b = src.as_bytes();
        let id_i = b.len() - SpotifyId::SIZE_BASE62;
        if b[id_i - 1] != b':' {
            return Err(SpotifyIdError);
        }

        let mut id = match SpotifyId::from_base62(&src[id_i..]) {
            Ok(v) => v,
            Err(e) => return Err(e),
        };

        // Slice offset by 8 as we are skipping the "spotify:" prefix.
        id.audio_type = src[8..id_i - 1].into();

        Ok(id)
    }

    /// Returns the `SpotifyId` as a base16 (hex) encoded, `SpotifyId::SIZE_BASE62` (22)
    /// character long `String`.
    pub fn to_base16(&self) -> String {
        to_base16(&self.to_raw(), &mut [0u8; SpotifyId::SIZE_BASE16])
    }

    /// Returns the `SpotifyId` as a [canonically] base62 encoded, `SpotifyId::SIZE_BASE62` (22)
    /// character long `String`.
    ///
    /// [canonically]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
    pub fn to_base62(&self) -> String {
        let mut dst = [0u8; 22];
        let mut i = 0;
        let n = self.id;

        // The algorithm is based on:
        // https://github.com/trezor/trezor-crypto/blob/c316e775a2152db255ace96b6b65ac0f20525ec0/base58.c
        //
        // We are not using naive division of self.id as it is an u128 and div + mod are software
        // emulated at runtime (and unoptimized into mul + shift) on non-128bit platforms,
        // making them very expensive.
        //
        // Trezor's algorithm allows us to stick to arithmetic on native registers making this
        // an order of magnitude faster. Additionally, as our sizes are known, instead of
        // dealing with the ID on a byte by byte basis, we decompose it into four u32s and
        // use 64-bit arithmetic on them for an additional speedup.
        for shift in &[96, 64, 32, 0] {
            let mut carry = (n >> shift) as u32 as u64;

            for b in &mut dst[..i] {
                carry += (*b as u64) << 32;
                *b = (carry % 62) as u8;
                carry /= 62;
            }

            while carry > 0 {
                dst[i] = (carry % 62) as u8;
                carry /= 62;
                i += 1;
            }
        }

        for b in &mut dst {
            *b = BASE62_DIGITS[*b as usize];
        }

        dst.reverse();

        unsafe {
            // Safety: We are only dealing with ASCII characters.
            String::from_utf8_unchecked(dst.to_vec())
        }
    }

    /// Returns a copy of the `SpotifyId` as an array of `SpotifyId::SIZE` (16) bytes in
    /// big-endian order.
    pub fn to_raw(&self) -> [u8; SpotifyId::SIZE] {
        self.id.to_be_bytes()
    }

    /// Returns the `SpotifyId` as a [Spotify URI] in the canonical form `spotify:{type}:{id}`,
    /// where `{type}` is an arbitrary string and `{id}` is a 22-character long, base62 encoded
    /// Spotify ID.
    ///
    /// If the `SpotifyId` has an associated type unrecognized by the library, `{type}` will
    /// be encoded as `unknown`.
    ///
    /// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
    pub fn to_uri(&self) -> String {
        // 8 chars for the "spotify:" prefix + 1 colon + 22 chars base62 encoded ID  = 31
        // + unknown size audio_type.
        let mut dst = String::with_capacity(31 + self.audio_type.len());
        dst.push_str("spotify:");
        dst.push_str(self.audio_type.into());
        dst.push_str(":");
        dst.push_str(&self.to_base62());

        dst
    }
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub [u8; 20]);

impl FileId {
    pub fn to_base16(&self) -> String {
        to_base16(&self.0, &mut [0u8; 40])
    }
}

impl fmt::Debug for FileId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple("FileId").field(&self.to_base16()).finish()
    }
}

impl fmt::Display for FileId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.to_base16())
    }
}

#[inline]
fn to_base16(src: &[u8], buf: &mut [u8]) -> String {
    let mut i = 0;
    for v in src {
        buf[i] = BASE16_DIGITS[(v >> 4) as usize];
        buf[i + 1] = BASE16_DIGITS[(v & 0x0f) as usize];
        i += 2;
    }

    unsafe {
        // Safety: We are only dealing with ASCII characters.
        String::from_utf8_unchecked(buf.to_vec())
    }
}

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

    struct ConversionCase {
        id: u128,
        kind: SpotifyAudioType,
        uri: &'static str,
        base16: &'static str,
        base62: &'static str,
        raw: &'static [u8],
    }

    static CONV_VALID: [ConversionCase; 4] = [
        ConversionCase {
            id: 238762092608182713602505436543891614649,
            kind: SpotifyAudioType::Track,
            uri: "spotify:track:5sWHDYs0csV6RS48xBl0tH",
            base16: "b39fe8081e1f4c54be38e8d6f9f12bb9",
            base62: "5sWHDYs0csV6RS48xBl0tH",
            raw: &[
                179, 159, 232, 8, 30, 31, 76, 84, 190, 56, 232, 214, 249, 241, 43, 185,
            ],
        },
        ConversionCase {
            id: 204841891221366092811751085145916697048,
            kind: SpotifyAudioType::Track,
            uri: "spotify:track:4GNcXTGWmnZ3ySrqvol3o4",
            base16: "9a1b1cfbc6f244569ae0356c77bbe9d8",
            base62: "4GNcXTGWmnZ3ySrqvol3o4",
            raw: &[
                154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216,
            ],
        },
        ConversionCase {
            id: 204841891221366092811751085145916697048,
            kind: SpotifyAudioType::Podcast,
            uri: "spotify:episode:4GNcXTGWmnZ3ySrqvol3o4",
            base16: "9a1b1cfbc6f244569ae0356c77bbe9d8",
            base62: "4GNcXTGWmnZ3ySrqvol3o4",
            raw: &[
                154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216,
            ],
        },
        ConversionCase {
            id: 204841891221366092811751085145916697048,
            kind: SpotifyAudioType::NonPlayable,
            uri: "spotify:unknown:4GNcXTGWmnZ3ySrqvol3o4",
            base16: "9a1b1cfbc6f244569ae0356c77bbe9d8",
            base62: "4GNcXTGWmnZ3ySrqvol3o4",
            raw: &[
                154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216,
            ],
        },
    ];

    static CONV_INVALID: [ConversionCase; 2] = [
        ConversionCase {
            id: 0,
            kind: SpotifyAudioType::NonPlayable,
            // Invalid ID in the URI.
            uri: "spotify:arbitrarywhatever:5sWHDYs0Bl0tH",
            base16: "ZZZZZ8081e1f4c54be38e8d6f9f12bb9",
            base62: "!!!!!Ys0csV6RS48xBl0tH",
            raw: &[
                // Invalid length.
                154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 5, 3, 108, 119, 187, 233, 216, 255,
            ],
        },
        ConversionCase {
            id: 0,
            kind: SpotifyAudioType::NonPlayable,
            // Missing colon between ID and type.
            uri: "spotify:arbitrarywhatever5sWHDYs0csV6RS48xBl0tH",
            base16: "--------------------",
            base62: "....................",
            raw: &[
                // Invalid length.
                154, 27, 28, 251,
            ],
        },
    ];

    #[test]
    fn from_base62() {
        for c in &CONV_VALID {
            assert_eq!(SpotifyId::from_base62(c.base62).unwrap().id, c.id);
        }

        for c in &CONV_INVALID {
            assert_eq!(SpotifyId::from_base62(c.base62), Err(SpotifyIdError));
        }
    }

    #[test]
    fn to_base62() {
        for c in &CONV_VALID {
            let id = SpotifyId {
                id: c.id,
                audio_type: c.kind,
            };

            assert_eq!(id.to_base62(), c.base62);
        }
    }

    #[test]
    fn from_base16() {
        for c in &CONV_VALID {
            assert_eq!(SpotifyId::from_base16(c.base16).unwrap().id, c.id);
        }

        for c in &CONV_INVALID {
            assert_eq!(SpotifyId::from_base16(c.base16), Err(SpotifyIdError));
        }
    }

    #[test]
    fn to_base16() {
        for c in &CONV_VALID {
            let id = SpotifyId {
                id: c.id,
                audio_type: c.kind,
            };

            assert_eq!(id.to_base16(), c.base16);
        }
    }

    #[test]
    fn from_uri() {
        for c in &CONV_VALID {
            let actual = SpotifyId::from_uri(c.uri).unwrap();

            assert_eq!(actual.id, c.id);
            assert_eq!(actual.audio_type, c.kind);
        }

        for c in &CONV_INVALID {
            assert_eq!(SpotifyId::from_uri(c.uri), Err(SpotifyIdError));
        }
    }

    #[test]
    fn to_uri() {
        for c in &CONV_VALID {
            let id = SpotifyId {
                id: c.id,
                audio_type: c.kind,
            };

            assert_eq!(id.to_uri(), c.uri);
        }
    }

    #[test]
    fn from_raw() {
        for c in &CONV_VALID {
            assert_eq!(SpotifyId::from_raw(c.raw).unwrap().id, c.id);
        }

        for c in &CONV_INVALID {
            assert_eq!(SpotifyId::from_raw(c.raw), Err(SpotifyIdError));
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    account, deploy,
    shared::{self, MAIN_PKG_PATH},
};
use anyhow::{anyhow, Context, Result};
use diem_config::config::NodeConfig;
use diem_crypto::PrivateKey;
use diem_sdk::{
    client::{AccountAddress, BlockingClient},
    transaction_builder::TransactionFactory,
    types::LocalAccount,
};
use diem_types::{chain_id::ChainId, transaction::authenticator::AuthenticationKey};
use move_cli::package::cli::{self, UnitTestResult};
use move_package::BuildConfig;
use move_unit_test::UnitTestingConfig;
use shared::Home;
use std::{
    path::{Path, PathBuf},
    process::Command,
    str::FromStr,
};
use structopt::StructOpt;
use url::Url;

pub async fn run_e2e_tests(home: &Home, project_path: &Path, network: Url) -> Result<()> {
    let _config = shared::read_project_config(project_path)?;
    shared::generate_typescript_libraries(project_path)?;

    let config = NodeConfig::load(&home.get_validator_config_path()).with_context(|| {
        format!(
            "Failed to load NodeConfig from file: {:?}",
            home.get_validator_config_path()
        )
    })?;
    println!("Connecting to {}...", network.as_str());
    let client = BlockingClient::new(network.as_str());
    let factory = TransactionFactory::new(ChainId::test());

    let test_account = create_account(
        home.get_root_key_path(),
        home.get_test_key_path(),
        &client,
        &factory,
    )?;
    let _receiver_account = create_account(
        home.get_root_key_path(),
        home.get_test_key_path(), // TODO: update to a different key to sender
        &client,
        &factory,
    )?;
    deploy::handle(home, project_path, network.clone()).await?;

    run_deno_test(
        home,
        project_path,
        &Url::from_str(config.json_rpc.address.to_string().as_str())?,
        &network,
        home.get_test_key_path(),
        test_account.address(),
    )
}

fn create_account(
    root_key_path: &Path,
    account_key_path: &Path,
    client: &BlockingClient,
    factory: &TransactionFactory,
) -> Result<LocalAccount> {
    let mut treasury_account = account::get_treasury_account(client, root_key_path);
    // TODO: generate random key by using let account_key = generate_key::generate_key();
    let account_key = generate_key::load_key(account_key_path);
    let public_key = account_key.public_key();
    let derived_address = AuthenticationKey::ed25519(&public_key).derived_address();
    let new_account = LocalAccount::new(derived_address, account_key, 0);
    account::create_account_onchain(&mut treasury_account, &new_account, factory, client)?;
    Ok(new_account)
}

// Run shuffle test using deno
pub fn run_deno_test(
    home: &Home,
    project_path: &Path,
    json_rpc_url: &Url,
    dev_api_url: &Url,
    key_path: &Path,
    sender_address: AccountAddress,
) -> Result<()> {
    let tests_path_string = project_path
        .join("e2e")
        .as_path()
        .to_string_lossy()
        .to_string();

    let filtered_envs = shared::get_filtered_envs_for_deno(
        home,
        project_path,
        dev_api_url,
        key_path,
        sender_address,
    );
    Command::new("deno")
        .args([
            "test",
            "--unstable",
            tests_path_string.as_str(),
            "--allow-env=PROJECT_PATH,SHUFFLE_HOME,SHUFFLE_NETWORK,PRIVATE_KEY_PATH,SENDER_ADDRESS",
            "--allow-read",
            format!(
                "--allow-net={},{}",
                host_and_port(dev_api_url)?,
                host_and_port(json_rpc_url)?,
            )
            .as_str(),
        ])
        .envs(&filtered_envs)
        .spawn()
        .expect("deno failed to start, is it installed? brew install deno")
        .wait()?;
    Ok(())
}

fn host_and_port(url: &Url) -> Result<String> {
    Ok(format!(
        "{}:{}",
        url.host_str()
            .ok_or_else(|| anyhow!("url should have domain host"))?,
        url.port_or_known_default()
            .ok_or_else(|| anyhow!("url should have port or default"))?,
    ))
}

pub fn run_move_unit_tests(project_path: &Path) -> Result<UnitTestResult> {
    let unit_test_config = UnitTestingConfig {
        report_storage_on_error: true,
        ..UnitTestingConfig::default_with_bound(None)
    };

    cli::run_move_unit_tests(
        &project_path.join(MAIN_PKG_PATH),
        generate_build_config_for_testing()?,
        unit_test_config,
        diem_vm::natives::diem_natives(),
        false,
    )
}

fn generate_build_config_for_testing() -> Result<BuildConfig> {
    Ok(BuildConfig {
        dev_mode: true,
        test_mode: true,
        generate_docs: false,
        generate_abis: true,
        install_dir: None,
        force_recompilation: false,
    })
}

#[derive(Debug, StructOpt)]
pub enum TestCommand {
    #[structopt(about = "Runs end to end test in shuffle")]
    E2e {
        #[structopt(short, long)]
        project_path: Option<PathBuf>,

        #[structopt(short, long)]
        network: Option<String>,
    },

    #[structopt(about = "Runs move move unit tests in project folder")]
    Unit {
        #[structopt(short, long)]
        project_path: Option<PathBuf>,
    },

    #[structopt(
        about = "Runs both end to end test in shuffle and move move unit tests in project folder"
    )]
    All {
        #[structopt(short, long)]
        project_path: Option<PathBuf>,

        #[structopt(short, long)]
        network: Option<String>,
    },
}

pub async fn handle(home: &Home, cmd: TestCommand) -> Result<()> {
    match cmd {
        TestCommand::E2e {
            project_path,
            network,
        } => {
            // TODO: std::process::exit with error code on any error
            run_e2e_tests(
                home,
                shared::normalized_project_path(project_path)?.as_path(),
                shared::normalized_network(home, network)?,
            )
            .await
        }
        TestCommand::Unit { project_path } => {
            let result =
                run_move_unit_tests(shared::normalized_project_path(project_path)?.as_path())?;
            match result {
                UnitTestResult::Failure => std::process::exit(1),
                _ => Ok(()),
            }
        }
        TestCommand::All {
            project_path,
            network,
        } => {
            let normalized_path = shared::normalized_project_path(project_path)?;
            // TODO: std::process::exit with error code on any error
            run_move_unit_tests(normalized_path.as_path())?;
            run_e2e_tests(
                home,
                normalized_path.as_path(),
                shared::normalized_network(home, network)?,
            )
            .await
        }
    }
}
<commit_msg>[shuffle] shuffle e2e test bug fix<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    account, deploy,
    shared::{self, MAIN_PKG_PATH},
};
use anyhow::{anyhow, Context, Result};
use diem_config::config::NodeConfig;
use diem_crypto::PrivateKey;
use diem_sdk::{
    client::{AccountAddress, BlockingClient},
    transaction_builder::TransactionFactory,
    types::LocalAccount,
};
use diem_types::{chain_id::ChainId, transaction::authenticator::AuthenticationKey};
use move_cli::package::cli::{self, UnitTestResult};
use move_package::BuildConfig;
use move_unit_test::UnitTestingConfig;
use shared::Home;
use std::{
    path::{Path, PathBuf},
    process::Command,
    str::FromStr,
};
use structopt::StructOpt;
use url::Url;

pub async fn run_e2e_tests(home: &Home, project_path: &Path, network: Url) -> Result<()> {
    let _config = shared::read_project_config(project_path)?;
    shared::generate_typescript_libraries(project_path)?;

    let config = NodeConfig::load(&home.get_validator_config_path()).with_context(|| {
        format!(
            "Failed to load NodeConfig from file: {:?}",
            home.get_validator_config_path()
        )
    })?;
    println!("Connecting to {}...", network.as_str());
    let client = BlockingClient::new(network.as_str());
    let factory = TransactionFactory::new(ChainId::test());

    let test_account = create_account(
        home.get_root_key_path(),
        home.get_test_key_path(),
        &client,
        &factory,
    )?;
    let _receiver_account = create_account(
        home.get_root_key_path(),
        home.get_test_key_path(), // TODO: update to a different key to sender
        &client,
        &factory,
    )?;
    deploy::handle(home, project_path, network.clone()).await?;

    run_deno_test(
        home,
        project_path,
        &Url::from_str(
            ("http://".to_owned() + config.json_rpc.address.to_string().as_str()).as_str(),
        )?,
        &network,
        home.get_test_key_path(),
        test_account.address(),
    )
}

fn create_account(
    root_key_path: &Path,
    account_key_path: &Path,
    client: &BlockingClient,
    factory: &TransactionFactory,
) -> Result<LocalAccount> {
    let mut treasury_account = account::get_treasury_account(client, root_key_path);
    // TODO: generate random key by using let account_key = generate_key::generate_key();
    let account_key = generate_key::load_key(account_key_path);
    let public_key = account_key.public_key();
    let derived_address = AuthenticationKey::ed25519(&public_key).derived_address();
    let new_account = LocalAccount::new(derived_address, account_key, 0);
    account::create_account_onchain(&mut treasury_account, &new_account, factory, client)?;
    Ok(new_account)
}

// Run shuffle test using deno
pub fn run_deno_test(
    home: &Home,
    project_path: &Path,
    json_rpc_url: &Url,
    dev_api_url: &Url,
    key_path: &Path,
    sender_address: AccountAddress,
) -> Result<()> {
    let tests_path_string = project_path
        .join("e2e")
        .as_path()
        .to_string_lossy()
        .to_string();

    let filtered_envs = shared::get_filtered_envs_for_deno(
        home,
        project_path,
        dev_api_url,
        key_path,
        sender_address,
    );
    Command::new("deno")
        .args([
            "test",
            "--unstable",
            tests_path_string.as_str(),
            "--allow-env=PROJECT_PATH,SHUFFLE_HOME,SHUFFLE_NETWORK,PRIVATE_KEY_PATH,SENDER_ADDRESS",
            "--allow-read",
            format!(
                "--allow-net={},{}",
                host_and_port(dev_api_url)?,
                host_and_port(json_rpc_url)?,
            )
            .as_str(),
        ])
        .envs(&filtered_envs)
        .spawn()
        .expect("deno failed to start, is it installed? brew install deno")
        .wait()?;
    Ok(())
}

fn host_and_port(url: &Url) -> Result<String> {
    Ok(format!(
        "{}:{}",
        url.host_str()
            .ok_or_else(|| anyhow!("url should have domain host"))?,
        url.port_or_known_default()
            .ok_or_else(|| anyhow!("url should have port or default"))?,
    ))
}

pub fn run_move_unit_tests(project_path: &Path) -> Result<UnitTestResult> {
    let unit_test_config = UnitTestingConfig {
        report_storage_on_error: true,
        ..UnitTestingConfig::default_with_bound(None)
    };

    cli::run_move_unit_tests(
        &project_path.join(MAIN_PKG_PATH),
        generate_build_config_for_testing()?,
        unit_test_config,
        diem_vm::natives::diem_natives(),
        false,
    )
}

fn generate_build_config_for_testing() -> Result<BuildConfig> {
    Ok(BuildConfig {
        dev_mode: true,
        test_mode: true,
        generate_docs: false,
        generate_abis: true,
        install_dir: None,
        force_recompilation: false,
    })
}

#[derive(Debug, StructOpt)]
pub enum TestCommand {
    #[structopt(about = "Runs end to end test in shuffle")]
    E2e {
        #[structopt(short, long)]
        project_path: Option<PathBuf>,

        #[structopt(short, long)]
        network: Option<String>,
    },

    #[structopt(about = "Runs move move unit tests in project folder")]
    Unit {
        #[structopt(short, long)]
        project_path: Option<PathBuf>,
    },

    #[structopt(
        about = "Runs both end to end test in shuffle and move move unit tests in project folder"
    )]
    All {
        #[structopt(short, long)]
        project_path: Option<PathBuf>,

        #[structopt(short, long)]
        network: Option<String>,
    },
}

pub async fn handle(home: &Home, cmd: TestCommand) -> Result<()> {
    match cmd {
        TestCommand::E2e {
            project_path,
            network,
        } => {
            // TODO: std::process::exit with error code on any error
            run_e2e_tests(
                home,
                shared::normalized_project_path(project_path)?.as_path(),
                shared::normalized_network(home, network)?,
            )
            .await
        }
        TestCommand::Unit { project_path } => {
            let result =
                run_move_unit_tests(shared::normalized_project_path(project_path)?.as_path())?;
            match result {
                UnitTestResult::Failure => std::process::exit(1),
                _ => Ok(()),
            }
        }
        TestCommand::All {
            project_path,
            network,
        } => {
            let normalized_path = shared::normalized_project_path(project_path)?;
            // TODO: std::process::exit with error code on any error
            run_move_unit_tests(normalized_path.as_path())?;
            run_e2e_tests(
                home,
                normalized_path.as_path(),
                shared::normalized_network(home, network)?,
            )
            .await
        }
    }
}
<|endoftext|>


<commit_before>use redox::{self, cmp, env, BMPFile, Color};
use redox::collections::BTreeMap;
use redox::event::{self, EventOption, MouseEvent};
use redox::fs::File;
use redox::io::{Read, Seek, SeekFrom};
use redox::orbital::Window;
use redox::time::{self, Duration};
use redox::vec::Vec;
use redox::string::{String, ToString};

pub struct FileType {
    description: String,
    icon: BMPFile,
}

impl FileType {
    pub fn new(desc: &str, icon: &str) -> FileType {
        FileType { description: desc.to_string(), icon: load_icon(icon) }
    }

}

pub struct FileManager {
    file_types: BTreeMap<String, FileType>,
    files: Vec<String>,
    file_sizes: Vec<String>,
    selected: isize,
    last_mouse_event: MouseEvent,
    click_time: Duration,
}

fn load_icon(path: &str) -> BMPFile {
    let mut vec: Vec<u8> = Vec::new();
    if let Some(mut file) = File::open(&("file:///ui/mimetypes/".to_string() + path + ".bmp")) {
        file.read_to_end(&mut vec);
    }
    BMPFile::from_data(&vec)
}

impl FileManager {
    pub fn new() -> Self {
        FileManager {
            file_types: {
                let mut file_types = BTreeMap::<String, FileType>::new();
                file_types.insert("/".to_string(),
                                  FileType::new("Folder", "inode-directory"));
                file_types.insert("wav".to_string(),
                                  FileType::new("WAV audio", "audio-x-wav"));
                file_types.insert("bin".to_string(),
                                  FileType::new("Executable", "application-x-executable"));
                file_types.insert("bmp".to_string(),
                                  FileType::new("Bitmap Image", "image-x-generic"));
                file_types.insert("rs".to_string(),
                                  FileType::new("Rust source code", "text-x-makefile"));
                file_types.insert("crate".to_string(),
                                  FileType::new("Rust crate", "image-x-makefile"));
                file_types.insert("asm".to_string(),
                                  FileType::new("Assembly source", "text-x-makefile"));
                file_types.insert("list".to_string(),
                                  FileType::new("Disassembly source", "text-x-makefile"));
                file_types.insert("c".to_string(),
                                  FileType::new("C source code", "text-x-makefile"));
                file_types.insert("cpp".to_string(),
                                  FileType::new("C++ source code", "text-x-makefile"));
                file_types.insert("sh".to_string(),
                                  FileType::new("Shell script", "text-x-script"));
                file_types.insert("lua".to_string(),
                                  FileType::new("Lua script", "text-x-script"));
                file_types.insert("txt".to_string(),
                                  FileType::new("plain text document", "text-x-generic"));
                file_types.insert("md".to_string(),
                                  FileType::new("Markdown", "text-x-generic"));
                file_types.insert("REDOX".to_string(),
                                  FileType::new("Redox package", "text-x-generic"));
                file_types.insert(String::new(),
                                  FileType::new("Unknown file", "unknown"));
                file_types
            },
            files: Vec::new(),
            file_sizes: Vec::new(),
            selected: -1,
            last_mouse_event: MouseEvent {
                x: 0,
                y: 0,
                left_button: false,
                middle_button: false,
                right_button: false,
            },
            click_time: Duration::new(0, 0),
        }
    }

    fn load_icon_with(&self, file_name: &str, row: isize, window: &mut Window) {
        if file_name.ends_with('/') {
            window.image(0,
                         32 * row as isize,
                         self.file_types["/"].icon.width(),
                         self.file_types["/"].icon.height(),
                         self.file_types["/"].icon.as_slice());
        } else {
            let pos = file_name.rfind('.').unwrap_or(0) + 1;
            match self.file_types.get(&file_name[pos..]) {
                Some(file_type) => {
                    window.image(0,
                                 32 * row,
                                 file_type.icon.width(),
                                 file_type.icon.height(),
                                 file_type.icon.as_slice());
                }
                None => {
                    window.image(0,
                                 32 * row,
                                 self.file_types[""].icon.width(),
                                 self.file_types[""].icon.height(),
                                 self.file_types[""].icon.as_slice());
                }
            }
        }
    }

    fn get_description(&self, file_name: &str) -> String {
        if file_name.ends_with('/') {
            self.file_types["/"].description.clone()
        } else {
            let pos = file_name.rfind('.').unwrap_or(0) + 1;
            match self.file_types.get(&file_name[pos..]) {
                Some(file_type) => file_type.description.clone(),
                None => self.file_types[""].description.clone(),
            }
        }
    }

    fn draw_content(&mut self, window: &mut Window) {
        window.set(Color::WHITE);

        let mut i = 0;
        let mut row = 0;
        let column = {
            let mut tmp = [0, 0];
            for string in self.files.iter() {
                if tmp[0] < string.len() {
                    tmp[0] = string.len();
                }
            }

            tmp[0] += 1;

            for file_size in self.file_sizes.iter() {
                if tmp[1] < file_size.len() {
                    tmp[1] = file_size.len();
                }
            }

            tmp[1] += tmp[0] + 1;
            tmp
        };
        for (file_name, file_size) in self.files.iter().zip(self.file_sizes.iter()) {
            if i == self.selected {
                let width = window.width();
                window.rect(0, 32 * row as isize, width, 32, Color::rgba(224, 224, 224, 255));
            }

            self.load_icon_with(&file_name, row as isize, window);

            let mut col = 0;
            for c in file_name.chars() {
                if c == '\n' {
                    col = 0;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col < window.width() / 8 && row < window.height() / 32 {
                        window.char(8 * col as isize + 40,
                                    32 * row as isize + 8,
                                    c,
                                    Color::BLACK);
                        col += 1;
                    }
                }
                if col >= window.width() / 8 {
                    col = 0;
                    row += 1;
                }
            }

            col = column[0];

            for c in file_size.chars() {
                if c == '\n' {
                    col = 0;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col < window.width() / 8 && row < window.height() / 32 {
                        window.char(8 * col as isize + 40,
                                    32 * row as isize + 8,
                                    c,
                                    Color::BLACK);
                        col += 1;
                    }
                }
                if col >= window.width() / 8 {
                    col = 0;
                    row += 1;
                }
            }

            col = column[1];

            for c in self.get_description(file_name).chars() {
                if c == '\n' {
                    col = 0;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col < window.width() / 8 && row < window.height() / 32 {
                        window.char(8 * col as isize + 40,
                                    32 * row as isize + 8,
                                    c,
                                    Color::BLACK);
                        col += 1;
                    }
                }
                if col >= window.width() / 8 {
                    col = 0;
                    row += 1;
                }
            }

            row += 1;
            i += 1;
        }

        window.sync();
    }

    fn main(&mut self, path: &str) {
        let mut width = [48, 48, 48];
        let mut height = 0;
        if let Some(mut file) = File::open(path) {
            let mut list = String::new();
            file.read_to_string(&mut list);

            for entry in list.split('\n') {
                self.files.push(entry.to_string());
                self.file_sizes.push(
                    match File::open(&(path.to_string() + entry)) {
                        Some(mut file) => {
                            // When the entry is a folder
                            if entry.ends_with('/') {
                                let mut string = String::new();
                                file.read_to_string(&mut string);

                                let count = string.split('\n').count();
                                if count == 1 {
                                    "1 entry".to_string()
                                } else {
                                    format!("{} entries", count)
                                }
                            } else {
                                match file.seek(SeekFrom::End(0)) {
                                    Some(size) => {
                                        if size >= 1_000_000_000 {
                                            format!("{:.1} GB", (size as f64)/1_000_000_000.0)
                                        } else if size >= 1_000_000 {
                                            format!("{:.1} MB", (size as f64)/1_000_000.0)
                                        } else if size >= 1_000 {
                                            format!("{:.1} KB", (size as f64)/1_000.0)
                                        } else {
                                            format!("{:.1} bytes", size)
                                        }
                                    }
                                    None => "Failed to seek".to_string()
                                }
                            }
                        },
                        None => "Failed to open".to_string(),
                    }
                );
                // Unwrapping the last file size will not panic since it has
                // been at least pushed once in the vector
                width[0] = cmp::max(width[0], 48 + (entry.len()) * 8);
                width[1] = cmp::max(width[1], 8 + (self.file_sizes.last().unwrap().len()) * 8);
                width[2] = cmp::max(width[2], 8 + (self.get_description(entry).len()) * 8);
            }

            if height < self.files.len() * 32 {
                height = self.files.len() * 32;
            }
        }

        let mut window = Window::new((redox::rand() % 400 + 50) as isize,
                                     (redox::rand() % 300 + 50) as isize,
                                     width.iter().sum(),
                                     height,
                                     &path).unwrap();

        self.draw_content(&mut window);

        while let Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        match key_event.scancode {
                            event::K_ESC => break,
                            event::K_HOME => self.selected = 0,
                            event::K_UP => if self.selected > 0 {
                                self.selected -= 1;
                            },
                            event::K_END => self.selected = self.files.len() as isize - 1,
                            event::K_DOWN => if self.selected < self.files.len() as isize - 1 {
                                self.selected += 1;
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                '\n' => {
                                    if self.selected >= 0 &&
                                       self.selected < self.files.len() as isize {
                                        match self.files.get(self.selected as usize) {
                                            Some(file) => {
                                                File::exec(&(path.to_string() + &file));
                                            },
                                            None => (),
                                        }
                                    }
                                }
                                _ => {
                                    let mut i = 0;
                                    for file in self.files.iter() {
                                        if file.starts_with(key_event.character) {
                                            self.selected = i;
                                            break;
                                        }
                                        i += 1;
                                    }
                                }
                            },
                        }

                        self.draw_content(&mut window);
                    }
                }
                EventOption::Mouse(mouse_event) => {
                    let mut redraw = false;
                    let mut i = 0;
                    let mut row = 0;
                    for file in self.files.iter() {
                        let mut col = 0;
                        for c in file.chars() {
                            if mouse_event.y >= 32 * row as isize &&
                               mouse_event.y < 32 * row as isize + 32 {
                                self.selected = i;
                                redraw = true;
                            }

                            if c == '\n' {
                                col = 0;
                                row += 1;
                            } else if c == '\t' {
                                col += 8 - col % 8;
                            } else {
                                if col < window.width() / 8 && row < window.height() / 32 {
                                    col += 1;
                                }
                            }
                            if col >= window.width() / 8 {
                                col = 0;
                                row += 1;
                            }
                        }
                        row += 1;
                        i += 1;
                    }

                    if redraw {
                        self.draw_content(&mut window);
                    }

                    //Check for double click
                    if mouse_event.left_button {
                        let click_time = Duration::realtime();

                        if click_time - self.click_time < Duration::new(0, 500 * time::NANOS_PER_MILLI)
                            && self.last_mouse_event.x == mouse_event.x
                            && self.last_mouse_event.y == mouse_event.y {
                            if self.selected >= 0 && self.selected < self.files.len() as isize {
                                if let Some(file) = self.files.get(self.selected as usize) {
                                    File::exec(&(path.to_string() + &file));
                                }
                            }
                            self.click_time = Duration::new(0, 0);
                        } else {
                            self.click_time = click_time;
                        }
                    }
                    self.last_mouse_event = mouse_event;
                }
                EventOption::Quit(quit_event) => break,
                _ => (),
            }
        }
    }
}

pub fn main() {
    match env::args().get(1) {
        Some(arg) => FileManager::new().main(arg),
        None => FileManager::new().main("file:///"),
    }
}
<commit_msg>Add more file types, update descriptions, change icons in file manager<commit_after>use redox::{self, cmp, env, BMPFile, Color};
use redox::collections::BTreeMap;
use redox::event::{self, EventOption, MouseEvent};
use redox::fs::File;
use redox::io::{Read, Seek, SeekFrom};
use redox::orbital::Window;
use redox::time::{self, Duration};
use redox::vec::Vec;
use redox::string::{String, ToString};

pub struct FileType {
    description: String,
    icon: BMPFile,
}

impl FileType {
    pub fn new(desc: &str, icon: &str) -> FileType {
        FileType { description: desc.to_string(), icon: load_icon(icon) }
    }

}

pub struct FileManager {
    file_types: BTreeMap<String, FileType>,
    files: Vec<String>,
    file_sizes: Vec<String>,
    selected: isize,
    last_mouse_event: MouseEvent,
    click_time: Duration,
}

fn load_icon(path: &str) -> BMPFile {
    let mut vec: Vec<u8> = Vec::new();
    if let Some(mut file) = File::open(&("file:///ui/mimetypes/".to_string() + path + ".bmp")) {
        file.read_to_end(&mut vec);
    }
    BMPFile::from_data(&vec)
}

impl FileManager {
    pub fn new() -> Self {
        FileManager {
            file_types: {
                let mut file_types = BTreeMap::<String, FileType>::new();
                file_types.insert("/".to_string(),
                                  FileType::new("Folder", "inode-directory"));
                file_types.insert("wav".to_string(),
                                  FileType::new("WAV audio", "audio-x-wav"));
                file_types.insert("bin".to_string(),
                                  FileType::new("Executable", "application-x-executable"));
                file_types.insert("bmp".to_string(),
                                  FileType::new("Bitmap Image", "image-x-generic"));
                file_types.insert("rs".to_string(),
                                  FileType::new("Rust source code", "text-x-makefile"));
                file_types.insert("crate".to_string(),
                                  FileType::new("Rust crate", "application-x-archive"));
                file_types.insert("rlib".to_string(),
                                  FileType::new("Static Rust library", "application-x-object"));
                file_types.insert("asm".to_string(),
                                  FileType::new("Assembly source", "text-x-makefile"));
                file_types.insert("list".to_string(),
                                  FileType::new("Disassembly source", "text-x-makefile"));
                file_types.insert("c".to_string(),
                                  FileType::new("C source code", "text-x-csrc"));
                file_types.insert("cpp".to_string(),
                                  FileType::new("C++ source code", "text-x-c++src"));
                file_types.insert("h".to_string(),
                                  FileType::new("C header", "text-x-chdr"));
                file_types.insert("sh".to_string(),
                                  FileType::new("Shell script", "text-x-script"));
                file_types.insert("lua".to_string(),
                                  FileType::new("Lua script", "text-x-script"));
                file_types.insert("txt".to_string(),
                                  FileType::new("Plain text document", "text-x-generic"));
                file_types.insert("md".to_string(),
                                  FileType::new("Markdown document", "text-x-generic"));
                file_types.insert("toml".to_string(),
                                  FileType::new("TOML document", "text-x-generic"));
                file_types.insert("json".to_string(),
                                  FileType::new("JSON document", "text-x-generic"));
                file_types.insert("REDOX".to_string(),
                                  FileType::new("Redox package", "text-x-generic"));
                file_types.insert(String::new(),
                                  FileType::new("Unknown file", "unknown"));
                file_types
            },
            files: Vec::new(),
            file_sizes: Vec::new(),
            selected: -1,
            last_mouse_event: MouseEvent {
                x: 0,
                y: 0,
                left_button: false,
                middle_button: false,
                right_button: false,
            },
            click_time: Duration::new(0, 0),
        }
    }

    fn load_icon_with(&self, file_name: &str, row: isize, window: &mut Window) {
        if file_name.ends_with('/') {
            window.image(0,
                         32 * row as isize,
                         self.file_types["/"].icon.width(),
                         self.file_types["/"].icon.height(),
                         self.file_types["/"].icon.as_slice());
        } else {
            let pos = file_name.rfind('.').unwrap_or(0) + 1;
            match self.file_types.get(&file_name[pos..]) {
                Some(file_type) => {
                    window.image(0,
                                 32 * row,
                                 file_type.icon.width(),
                                 file_type.icon.height(),
                                 file_type.icon.as_slice());
                }
                None => {
                    window.image(0,
                                 32 * row,
                                 self.file_types[""].icon.width(),
                                 self.file_types[""].icon.height(),
                                 self.file_types[""].icon.as_slice());
                }
            }
        }
    }

    fn get_description(&self, file_name: &str) -> String {
        if file_name.ends_with('/') {
            self.file_types["/"].description.clone()
        } else {
            let pos = file_name.rfind('.').unwrap_or(0) + 1;
            match self.file_types.get(&file_name[pos..]) {
                Some(file_type) => file_type.description.clone(),
                None => self.file_types[""].description.clone(),
            }
        }
    }

    fn draw_content(&mut self, window: &mut Window) {
        window.set(Color::WHITE);

        let mut i = 0;
        let mut row = 0;
        let column = {
            let mut tmp = [0, 0];
            for string in self.files.iter() {
                if tmp[0] < string.len() {
                    tmp[0] = string.len();
                }
            }

            tmp[0] += 1;

            for file_size in self.file_sizes.iter() {
                if tmp[1] < file_size.len() {
                    tmp[1] = file_size.len();
                }
            }

            tmp[1] += tmp[0] + 1;
            tmp
        };
        for (file_name, file_size) in self.files.iter().zip(self.file_sizes.iter()) {
            if i == self.selected {
                let width = window.width();
                window.rect(0, 32 * row as isize, width, 32, Color::rgba(224, 224, 224, 255));
            }

            self.load_icon_with(&file_name, row as isize, window);

            let mut col = 0;
            for c in file_name.chars() {
                if c == '\n' {
                    col = 0;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col < window.width() / 8 && row < window.height() / 32 {
                        window.char(8 * col as isize + 40,
                                    32 * row as isize + 8,
                                    c,
                                    Color::BLACK);
                        col += 1;
                    }
                }
                if col >= window.width() / 8 {
                    col = 0;
                    row += 1;
                }
            }

            col = column[0];

            for c in file_size.chars() {
                if c == '\n' {
                    col = 0;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col < window.width() / 8 && row < window.height() / 32 {
                        window.char(8 * col as isize + 40,
                                    32 * row as isize + 8,
                                    c,
                                    Color::BLACK);
                        col += 1;
                    }
                }
                if col >= window.width() / 8 {
                    col = 0;
                    row += 1;
                }
            }

            col = column[1];

            for c in self.get_description(file_name).chars() {
                if c == '\n' {
                    col = 0;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col < window.width() / 8 && row < window.height() / 32 {
                        window.char(8 * col as isize + 40,
                                    32 * row as isize + 8,
                                    c,
                                    Color::BLACK);
                        col += 1;
                    }
                }
                if col >= window.width() / 8 {
                    col = 0;
                    row += 1;
                }
            }

            row += 1;
            i += 1;
        }

        window.sync();
    }

    fn main(&mut self, path: &str) {
        let mut width = [48, 48, 48];
        let mut height = 0;
        if let Some(mut file) = File::open(path) {
            let mut list = String::new();
            file.read_to_string(&mut list);

            for entry in list.split('\n') {
                self.files.push(entry.to_string());
                self.file_sizes.push(
                    match File::open(&(path.to_string() + entry)) {
                        Some(mut file) => {
                            // When the entry is a folder
                            if entry.ends_with('/') {
                                let mut string = String::new();
                                file.read_to_string(&mut string);

                                let count = string.split('\n').count();
                                if count == 1 {
                                    "1 entry".to_string()
                                } else {
                                    format!("{} entries", count)
                                }
                            } else {
                                match file.seek(SeekFrom::End(0)) {
                                    Some(size) => {
                                        if size >= 1_000_000_000 {
                                            format!("{:.1} GB", (size as f64)/1_000_000_000.0)
                                        } else if size >= 1_000_000 {
                                            format!("{:.1} MB", (size as f64)/1_000_000.0)
                                        } else if size >= 1_000 {
                                            format!("{:.1} KB", (size as f64)/1_000.0)
                                        } else {
                                            format!("{:.1} bytes", size)
                                        }
                                    }
                                    None => "Failed to seek".to_string()
                                }
                            }
                        },
                        None => "Failed to open".to_string(),
                    }
                );
                // Unwrapping the last file size will not panic since it has
                // been at least pushed once in the vector
                width[0] = cmp::max(width[0], 48 + (entry.len()) * 8);
                width[1] = cmp::max(width[1], 8 + (self.file_sizes.last().unwrap().len()) * 8);
                width[2] = cmp::max(width[2], 8 + (self.get_description(entry).len()) * 8);
            }

            if height < self.files.len() * 32 {
                height = self.files.len() * 32;
            }
        }

        let mut window = Window::new((redox::rand() % 400 + 50) as isize,
                                     (redox::rand() % 300 + 50) as isize,
                                     width.iter().sum(),
                                     height,
                                     &path).unwrap();

        self.draw_content(&mut window);

        while let Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        match key_event.scancode {
                            event::K_ESC => break,
                            event::K_HOME => self.selected = 0,
                            event::K_UP => if self.selected > 0 {
                                self.selected -= 1;
                            },
                            event::K_END => self.selected = self.files.len() as isize - 1,
                            event::K_DOWN => if self.selected < self.files.len() as isize - 1 {
                                self.selected += 1;
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                '\n' => {
                                    if self.selected >= 0 &&
                                       self.selected < self.files.len() as isize {
                                        match self.files.get(self.selected as usize) {
                                            Some(file) => {
                                                File::exec(&(path.to_string() + &file));
                                            },
                                            None => (),
                                        }
                                    }
                                }
                                _ => {
                                    let mut i = 0;
                                    for file in self.files.iter() {
                                        if file.starts_with(key_event.character) {
                                            self.selected = i;
                                            break;
                                        }
                                        i += 1;
                                    }
                                }
                            },
                        }

                        self.draw_content(&mut window);
                    }
                }
                EventOption::Mouse(mouse_event) => {
                    let mut redraw = false;
                    let mut i = 0;
                    let mut row = 0;
                    for file in self.files.iter() {
                        let mut col = 0;
                        for c in file.chars() {
                            if mouse_event.y >= 32 * row as isize &&
                               mouse_event.y < 32 * row as isize + 32 {
                                self.selected = i;
                                redraw = true;
                            }

                            if c == '\n' {
                                col = 0;
                                row += 1;
                            } else if c == '\t' {
                                col += 8 - col % 8;
                            } else {
                                if col < window.width() / 8 && row < window.height() / 32 {
                                    col += 1;
                                }
                            }
                            if col >= window.width() / 8 {
                                col = 0;
                                row += 1;
                            }
                        }
                        row += 1;
                        i += 1;
                    }

                    if redraw {
                        self.draw_content(&mut window);
                    }

                    //Check for double click
                    if mouse_event.left_button {
                        let click_time = Duration::realtime();

                        if click_time - self.click_time < Duration::new(0, 500 * time::NANOS_PER_MILLI)
                            && self.last_mouse_event.x == mouse_event.x
                            && self.last_mouse_event.y == mouse_event.y {
                            if self.selected >= 0 && self.selected < self.files.len() as isize {
                                if let Some(file) = self.files.get(self.selected as usize) {
                                    File::exec(&(path.to_string() + &file));
                                }
                            }
                            self.click_time = Duration::new(0, 0);
                        } else {
                            self.click_time = click_time;
                        }
                    }
                    self.last_mouse_event = mouse_event;
                }
                EventOption::Quit(quit_event) => break,
                _ => (),
            }
        }
    }
}

pub fn main() {
    match env::args().get(1) {
        Some(arg) => FileManager::new().main(arg),
        None => FileManager::new().main("file:///"),
    }
}
<|endoftext|>


<commit_before>use super::{Color, FontId, FontMap, HorizontalAlign, VerticalAlign};
use full_rusttype::{point, vector, PositionedGlyph, VMetrics};
use linebreak::LineBreaker;
use std::iter::{FusedIterator, Iterator, Peekable};
use words::{RelativePositionedGlyph, Words, ZERO_V_METRICS};

/// A line of `Word`s limited to a max width bound.
pub(crate) struct Line<'font> {
    pub glyphs: Vec<(RelativePositionedGlyph<'font>, Color, FontId)>,
    pub max_v_metrics: VMetrics,
}

impl<'font> Line<'font> {
    #[inline]
    pub(crate) fn line_height(&self) -> f32 {
        self.max_v_metrics.ascent - self.max_v_metrics.descent + self.max_v_metrics.line_gap
    }

    /// Returns line glyphs positioned on the screen and aligned.
    pub fn aligned_on_screen(
        self,
        screen_position: (f32, f32),
        h_align: HorizontalAlign,
        v_align: VerticalAlign,
    ) -> Vec<(PositionedGlyph<'font>, Color, FontId)> {
        if self.glyphs.is_empty() {
            return Vec::new();
        }

        // implement v-aligns when they're are supported
        let screen_left = match h_align {
            HorizontalAlign::Left => point(screen_position.0, screen_position.1),
            // - Right alignment attained from left by shifting the line
            //   leftwards by the rightmost x distance from render position
            // - Central alignment is attained from left by shifting the line
            //   leftwards by half the rightmost x distance from render position
            HorizontalAlign::Center | HorizontalAlign::Right => {
                let mut shift_left = {
                    let last_glyph = &self.glyphs.last().unwrap().0;
                    last_glyph
                        .bounds()
                        .map(|bounds| bounds.max.x.ceil())
                        .unwrap_or(last_glyph.relative.x)
                        + last_glyph.glyph.h_metrics().left_side_bearing
                };
                if h_align == HorizontalAlign::Center {
                    shift_left /= 2.0;
                }
                point(screen_position.0 - shift_left, screen_position.1)
            }
        };

        let screen_pos = match v_align {
            VerticalAlign::Top => screen_left,
            VerticalAlign::Center => {
                let mut screen_pos = screen_left;
                screen_pos.x -= self.line_height() / 2.0;
                screen_pos
            }
            VerticalAlign::Bottom => {
                let mut screen_pos = screen_left;
                screen_pos.x -= self.line_height();
                screen_pos
            }
        };

        self.glyphs
            .into_iter()
            .map(|(glyph, color, font_id)| (glyph.screen_positioned(screen_pos), color, font_id))
            .collect()
    }
}

/// `Line` iterator.
///
/// Will iterator through `Word` until the next word would break the `width_bound`.
///
/// Note: Will always have at least one word, if possible, even if the word itself
/// breaks the `width_bound`.
pub(crate) struct Lines<'a, 'b, 'font, L, F>
where
    'font: 'a + 'b,
    L: LineBreaker,
    F: FontMap<'font> + 'b,
{
    pub(crate) words: Peekable<Words<'a, 'b, 'font, L, F>>,
    pub(crate) width_bound: f32,
}

impl<'a, 'b, 'font, L: LineBreaker, F: FontMap<'font> + 'b> Iterator
    for Lines<'a, 'b, 'font, L, F>
{
    type Item = Line<'font>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut caret = vector(0.0, 0.0);
        let mut line = Line {
            glyphs: Vec::new(),
            max_v_metrics: ZERO_V_METRICS,
        };

        let mut progressed = false;

        // TODO use while-peek-next when nll lands
        loop {
            if let Some(word) = self.words.peek() {
                let word_max_x = word.bounds.map(|b| b.max.x).unwrap_or(word.layout_width);
                // only if `progressed` means the first word is allowed to overlap the bounds
                if progressed && (caret.x + word_max_x).ceil() > self.width_bound {
                    break;
                }
            } else {
                break;
            }

            let word = self.words.next().unwrap();
            progressed = true;

            if word.max_v_metrics.ascent > line.max_v_metrics.ascent {
                let diff_y = word.max_v_metrics.ascent - caret.y;
                caret.y += diff_y;

                // modify all smaller lined glyphs to occupy the new larger line
                for (glyph, ..) in &mut line.glyphs {
                    glyph.relative.y += diff_y;
                }

                line.max_v_metrics = word.max_v_metrics;
            }

            if word.bounds.is_some() {
                line.glyphs
                    .extend(word.glyphs.into_iter().map(|(mut g, color, font_id)| {
                        g.relative = g.relative + caret;
                        (g, color, font_id)
                    }));
            }

            caret.x += word.layout_width;

            if word.hard_break {
                break;
            }
        }

        Some(line).filter(|_| progressed)
    }
}

impl<'a, 'b, 'font, L: LineBreaker, F: FontMap<'font>> FusedIterator
    for Lines<'a, 'b, 'font, L, F>
{}
<commit_msg>Fix single line v-align bottom & center<commit_after>use super::{Color, FontId, FontMap, HorizontalAlign, VerticalAlign};
use full_rusttype::{point, vector, PositionedGlyph, VMetrics};
use linebreak::LineBreaker;
use std::iter::{FusedIterator, Iterator, Peekable};
use words::{RelativePositionedGlyph, Words, ZERO_V_METRICS};

/// A line of `Word`s limited to a max width bound.
pub(crate) struct Line<'font> {
    pub glyphs: Vec<(RelativePositionedGlyph<'font>, Color, FontId)>,
    pub max_v_metrics: VMetrics,
}

impl<'font> Line<'font> {
    #[inline]
    pub(crate) fn line_height(&self) -> f32 {
        self.max_v_metrics.ascent - self.max_v_metrics.descent + self.max_v_metrics.line_gap
    }

    /// Returns line glyphs positioned on the screen and aligned.
    pub fn aligned_on_screen(
        self,
        screen_position: (f32, f32),
        h_align: HorizontalAlign,
        v_align: VerticalAlign,
    ) -> Vec<(PositionedGlyph<'font>, Color, FontId)> {
        if self.glyphs.is_empty() {
            return Vec::new();
        }

        // implement v-aligns when they're are supported
        let screen_left = match h_align {
            HorizontalAlign::Left => point(screen_position.0, screen_position.1),
            // - Right alignment attained from left by shifting the line
            //   leftwards by the rightmost x distance from render position
            // - Central alignment is attained from left by shifting the line
            //   leftwards by half the rightmost x distance from render position
            HorizontalAlign::Center | HorizontalAlign::Right => {
                let mut shift_left = {
                    let last_glyph = &self.glyphs.last().unwrap().0;
                    last_glyph
                        .bounds()
                        .map(|bounds| bounds.max.x.ceil())
                        .unwrap_or(last_glyph.relative.x)
                        + last_glyph.glyph.h_metrics().left_side_bearing
                };
                if h_align == HorizontalAlign::Center {
                    shift_left /= 2.0;
                }
                point(screen_position.0 - shift_left, screen_position.1)
            }
        };

        let screen_pos = match v_align {
            VerticalAlign::Top => screen_left,
            VerticalAlign::Center => {
                let mut screen_pos = screen_left;
                screen_pos.y -= self.line_height() / 2.0;
                screen_pos
            }
            VerticalAlign::Bottom => {
                let mut screen_pos = screen_left;
                screen_pos.y -= self.line_height();
                screen_pos
            }
        };

        self.glyphs
            .into_iter()
            .map(|(glyph, color, font_id)| (glyph.screen_positioned(screen_pos), color, font_id))
            .collect()
    }
}

/// `Line` iterator.
///
/// Will iterator through `Word` until the next word would break the `width_bound`.
///
/// Note: Will always have at least one word, if possible, even if the word itself
/// breaks the `width_bound`.
pub(crate) struct Lines<'a, 'b, 'font, L, F>
where
    'font: 'a + 'b,
    L: LineBreaker,
    F: FontMap<'font> + 'b,
{
    pub(crate) words: Peekable<Words<'a, 'b, 'font, L, F>>,
    pub(crate) width_bound: f32,
}

impl<'a, 'b, 'font, L: LineBreaker, F: FontMap<'font> + 'b> Iterator
    for Lines<'a, 'b, 'font, L, F>
{
    type Item = Line<'font>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut caret = vector(0.0, 0.0);
        let mut line = Line {
            glyphs: Vec::new(),
            max_v_metrics: ZERO_V_METRICS,
        };

        let mut progressed = false;

        // TODO use while-peek-next when nll lands
        loop {
            if let Some(word) = self.words.peek() {
                let word_max_x = word.bounds.map(|b| b.max.x).unwrap_or(word.layout_width);
                // only if `progressed` means the first word is allowed to overlap the bounds
                if progressed && (caret.x + word_max_x).ceil() > self.width_bound {
                    break;
                }
            } else {
                break;
            }

            let word = self.words.next().unwrap();
            progressed = true;

            if word.max_v_metrics.ascent > line.max_v_metrics.ascent {
                let diff_y = word.max_v_metrics.ascent - caret.y;
                caret.y += diff_y;

                // modify all smaller lined glyphs to occupy the new larger line
                for (glyph, ..) in &mut line.glyphs {
                    glyph.relative.y += diff_y;
                }

                line.max_v_metrics = word.max_v_metrics;
            }

            if word.bounds.is_some() {
                line.glyphs
                    .extend(word.glyphs.into_iter().map(|(mut g, color, font_id)| {
                        g.relative = g.relative + caret;
                        (g, color, font_id)
                    }));
            }

            caret.x += word.layout_width;

            if word.hard_break {
                break;
            }
        }

        Some(line).filter(|_| progressed)
    }
}

impl<'a, 'b, 'font, L: LineBreaker, F: FontMap<'font>> FusedIterator
    for Lines<'a, 'b, 'font, L, F>
{}
<|endoftext|>


<commit_before>use std::env;
use std::rc::Rc;
use std::cell::RefCell;
use sfml::graphics::{RenderWindow, RenderTarget, Font};
use sfml::window::{ContextSettings, VideoMode, Close};
use sfml::window::event;
use sfml::window::keyboard::Key;
use sfml::window::mouse::MouseButton;
use sfml::graphics::Color;
use sfml::system::vector2::Vector2i;
use na::{Pnt2, Pnt3, Iso2};
use na;
use nphysics::world::World;
use nphysics::object::RigidBody;
use nphysics::detection::joint::{Fixed, Anchor};
use camera::Camera;
use fps::Fps;
use engine::GraphicsManager;
use draw_helper;

fn usage(exe_name: &str) {
    println!("Usage: {} [OPTION] ", exe_name);
    println!("");
    println!("Options:");
    println!("    --help  - prints this help message and exits.");
    println!("    --pause - do not start the simulation right away.");
    println!("");
    println!("The following keyboard commands are supported:");
    println!("    t     - pause/continue the simulation.");
    println!("    s     - pause then execute only one simulation step.");
    println!("    space - display/hide contacts.");
}

#[derive(PartialEq)]
enum RunMode {
    Running,
    Stop,
    Step
}

pub struct Testbed<'a> {
    world:    World,
    window:   RenderWindow,
    graphics: GraphicsManager<'a>
}

struct TestbedState<'a> {
    running: RunMode,
    draw_colls: bool,
    camera: Camera,
    fps: Fps<'a>,
    grabbed_object: Option<Rc<RefCell<RigidBody>>>,
    grabbed_object_joint: Option<Rc<RefCell<Fixed>>>,
}

impl<'a> TestbedState<'a> {
    fn new(fnt: &'a Font) -> TestbedState<'a> {
        TestbedState{
            running: RunMode::Running,
            draw_colls: false,
            camera: Camera::new(),
            fps: Fps::new(&fnt),
            grabbed_object: None,
            grabbed_object_joint: None,
        }
    }
}

impl<'a> Testbed<'a> {
    pub fn new_empty() -> Testbed<'a> {
        let mode    = VideoMode::new_init(800, 600, 32);
        let setting = ContextSettings {
            depth_bits:         10,
            stencil_bits:       10,
            antialiasing_level: 2,
            major_version:      0,
            minor_version:      1
        };
        let window =
            match RenderWindow::new(mode, "nphysics 2d demo", Close, &setting) {
                Some(rwindow) => rwindow,
                None          => panic!("Error on creating the sfml window.")
            };
        let graphics = GraphicsManager::new();

        Testbed {
            world:    World::new(),
            window:   window,
            graphics: graphics
        }
    }

    pub fn new(world: World) -> Testbed<'a> {
        let mut res = Testbed::new_empty();

        res.set_world(world);

        res
    }

    pub fn set_world(&mut self, world: World) {
        self.world = world;
        self.graphics.clear();

        for rb in self.world.bodies() {
            self.graphics.add(rb.clone());
        }
    }

    pub fn set_color(&mut self, body: &Rc<RefCell<RigidBody>>, color: Pnt3<f32>) {
        let color = Pnt3::new(
            (color.x * 255.0) as u8,
            (color.y * 255.0) as u8,
            (color.z * 255.0) as u8
        );

        self.graphics.set_color(body, color);
    }

    pub fn run(&mut self) {
        let font_mem = include_bytes!("Inconsolata.otf");
        let     fnt  = Font::new_from_memory(font_mem).unwrap();

        let mut state = TestbedState::new(&fnt);

        let mut args    = env::args();

        if args.len() > 1 {
            let exname = args.next().unwrap();
            for arg in args {
                if &arg[..] == "--help" || &arg[..] == "-h" {
                    usage(&exname[..]);
                    return;
                }
                else if &arg[..] == "--pause" {
                    state.running = RunMode::Stop;
                }
            }
        }

        self.window.set_framerate_limit(60);

        self.foo(state);

        self.window.close();
    }

    fn foo(&mut self, mut state: TestbedState) {
        while self.window.is_open() {
            self.process_events(&mut state);

            self.window.clear(&Color::black());

            state.fps.reset();

            self.progress_world(&mut state);

            state.fps.register_delta();

            self.graphics.draw(&mut self.window, &state.camera);

            state.camera.activate_scene(&mut self.window);
            self.draw_collisions(&mut state);

            state.camera.activate_ui(&mut self.window);
            state.fps.draw_registered(&mut self.window);

            self.window.display();
        }
    }

    fn process_events(&mut self, mut state: &mut TestbedState) {
        loop {
            match self.window.poll_event() {
                event::KeyPressed{code, ..} => self.process_key_press(&mut state, code),
                event::MouseButtonPressed{button, x, y} => self.process_mouse_press(&mut state, button, x, y),
                event::MouseButtonReleased{button, x, y} => self.process_mouse_release(&mut state, button, x, y),
                event::MouseMoved{x, y} => self.process_mouse_moved(&mut state, x, y),
                event::Closed  => self.window.close(),
                event::NoEvent => break,
                e              => state.camera.handle_event(&e)
            }
        }
    }

    fn process_key_press(&mut self, state: &mut TestbedState, code: Key) {
        match code {
            Key::Escape => self.window.close(),
            Key::S      => state.running = RunMode::Step,
            Key::Space  => state.draw_colls = !state.draw_colls,
            Key::T      => {
                if state.running == RunMode::Stop {
                    state.running = RunMode::Running;
                }
                else {
                    state.running = RunMode::Stop;
                }
            },
            _                => { }
        }
    }

    fn process_mouse_press(&mut self, state: &mut TestbedState, button: MouseButton, x: i32, y: i32) {
        match button {
            MouseButton::MouseLeft => {
                let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
                let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
                self.world.interferences_with_point(&mapped_point, |b| {
                    if b.borrow().can_move() {
                        state.grabbed_object = Some(b.clone())
                    }
                });

                match state.grabbed_object {
                    Some(ref b) => {
                        for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                            match state.grabbed_object_joint {
                                Some(ref j) => self.world.remove_fixed(j),
                                None        => { }
                            }

                            let _1: Iso2<f32> = na::one();
                            let attach2 = na::append_translation(&_1, mapped_point.as_vec());
                            let attach1 = na::inv(&na::transformation(b.borrow().position())).unwrap() * attach2;
                            let anchor1 = Anchor::new(Some(state.grabbed_object.as_ref().unwrap().clone()), attach1);
                            let anchor2 = Anchor::new(None, attach2);
                            let joint = Fixed::new(anchor1, anchor2);
                            state.grabbed_object_joint = Some(self.world.add_fixed(joint));
                            node.select()
                        }
                    },
                    None => { }
                }
            },
            _ => {
                state.camera.handle_event(&event::MouseButtonPressed{ button: button, x: x, y: y })
            }
        }
    }

    fn process_mouse_release(&mut self, state: &mut TestbedState, button: MouseButton, x: i32, y: i32) {
        match button {
            MouseButton::MouseLeft => {
                match state.grabbed_object {
                    Some(ref b) => {
                        for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                            node.unselect()
                        }
                    },
                    None => { }
                }

                match state.grabbed_object_joint {
                    Some(ref j) => self.world.remove_fixed(j),
                    None => { }
                }

                state.grabbed_object = None;
                state.grabbed_object_joint = None;
            },
            _ => {
                state.camera.handle_event(&event::MouseButtonReleased{ button: button, x: x, y: y })
            }
        }
    }

    fn process_mouse_moved(&mut self, state: &mut TestbedState, x: i32, y: i32) {
        let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
        let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
        let _1: Iso2<f32> = na::one();
        let attach2 = na::append_translation(&_1, (mapped_point).as_vec());
        match state.grabbed_object {
            Some(_) => {
                let joint = state.grabbed_object_joint.as_ref().unwrap();
                joint.borrow_mut().set_local2(attach2);
            },
            None => state.camera.handle_event(&event::MouseMoved{x: x, y: y})
        };
    }

    fn progress_world(&mut self, state: &mut TestbedState) {
        if state.running != RunMode::Stop {
            self.world.step(0.016);
        }

        if state.running == RunMode::Step {
            state.running = RunMode::Stop;
        }
    }

    fn draw_collisions(&mut self, state: &mut TestbedState) {
        if state.draw_colls {
            draw_helper::draw_colls(&mut self.window, &mut self.world);
        }
    }
}
<commit_msg>Slightly better name for main loop method<commit_after>use std::env;
use std::rc::Rc;
use std::cell::RefCell;
use sfml::graphics::{RenderWindow, RenderTarget, Font};
use sfml::window::{ContextSettings, VideoMode, Close};
use sfml::window::event;
use sfml::window::keyboard::Key;
use sfml::window::mouse::MouseButton;
use sfml::graphics::Color;
use sfml::system::vector2::Vector2i;
use na::{Pnt2, Pnt3, Iso2};
use na;
use nphysics::world::World;
use nphysics::object::RigidBody;
use nphysics::detection::joint::{Fixed, Anchor};
use camera::Camera;
use fps::Fps;
use engine::GraphicsManager;
use draw_helper;

fn usage(exe_name: &str) {
    println!("Usage: {} [OPTION] ", exe_name);
    println!("");
    println!("Options:");
    println!("    --help  - prints this help message and exits.");
    println!("    --pause - do not start the simulation right away.");
    println!("");
    println!("The following keyboard commands are supported:");
    println!("    t     - pause/continue the simulation.");
    println!("    s     - pause then execute only one simulation step.");
    println!("    space - display/hide contacts.");
}

#[derive(PartialEq)]
enum RunMode {
    Running,
    Stop,
    Step
}

pub struct Testbed<'a> {
    world:    World,
    window:   RenderWindow,
    graphics: GraphicsManager<'a>
}

struct TestbedState<'a> {
    running: RunMode,
    draw_colls: bool,
    camera: Camera,
    fps: Fps<'a>,
    grabbed_object: Option<Rc<RefCell<RigidBody>>>,
    grabbed_object_joint: Option<Rc<RefCell<Fixed>>>,
}

impl<'a> TestbedState<'a> {
    fn new(fnt: &'a Font) -> TestbedState<'a> {
        TestbedState{
            running: RunMode::Running,
            draw_colls: false,
            camera: Camera::new(),
            fps: Fps::new(&fnt),
            grabbed_object: None,
            grabbed_object_joint: None,
        }
    }
}

impl<'a> Testbed<'a> {
    pub fn new_empty() -> Testbed<'a> {
        let mode    = VideoMode::new_init(800, 600, 32);
        let setting = ContextSettings {
            depth_bits:         10,
            stencil_bits:       10,
            antialiasing_level: 2,
            major_version:      0,
            minor_version:      1
        };
        let window =
            match RenderWindow::new(mode, "nphysics 2d demo", Close, &setting) {
                Some(rwindow) => rwindow,
                None          => panic!("Error on creating the sfml window.")
            };
        let graphics = GraphicsManager::new();

        Testbed {
            world:    World::new(),
            window:   window,
            graphics: graphics
        }
    }

    pub fn new(world: World) -> Testbed<'a> {
        let mut res = Testbed::new_empty();

        res.set_world(world);

        res
    }

    pub fn set_world(&mut self, world: World) {
        self.world = world;
        self.graphics.clear();

        for rb in self.world.bodies() {
            self.graphics.add(rb.clone());
        }
    }

    pub fn set_color(&mut self, body: &Rc<RefCell<RigidBody>>, color: Pnt3<f32>) {
        let color = Pnt3::new(
            (color.x * 255.0) as u8,
            (color.y * 255.0) as u8,
            (color.z * 255.0) as u8
        );

        self.graphics.set_color(body, color);
    }

    pub fn run(&mut self) {
        let font_mem = include_bytes!("Inconsolata.otf");
        let     fnt  = Font::new_from_memory(font_mem).unwrap();

        let mut state = TestbedState::new(&fnt);

        let mut args    = env::args();

        if args.len() > 1 {
            let exname = args.next().unwrap();
            for arg in args {
                if &arg[..] == "--help" || &arg[..] == "-h" {
                    usage(&exname[..]);
                    return;
                }
                else if &arg[..] == "--pause" {
                    state.running = RunMode::Stop;
                }
            }
        }

        self.window.set_framerate_limit(60);

        self.run_loop(state);

        self.window.close();
    }

    fn run_loop(&mut self, mut state: TestbedState) {
        while self.window.is_open() {
            self.process_events(&mut state);

            self.window.clear(&Color::black());

            state.fps.reset();

            self.progress_world(&mut state);

            state.fps.register_delta();

            self.graphics.draw(&mut self.window, &state.camera);

            state.camera.activate_scene(&mut self.window);
            self.draw_collisions(&mut state);

            state.camera.activate_ui(&mut self.window);
            state.fps.draw_registered(&mut self.window);

            self.window.display();
        }
    }

    fn process_events(&mut self, mut state: &mut TestbedState) {
        loop {
            match self.window.poll_event() {
                event::KeyPressed{code, ..} => self.process_key_press(&mut state, code),
                event::MouseButtonPressed{button, x, y} => self.process_mouse_press(&mut state, button, x, y),
                event::MouseButtonReleased{button, x, y} => self.process_mouse_release(&mut state, button, x, y),
                event::MouseMoved{x, y} => self.process_mouse_moved(&mut state, x, y),
                event::Closed  => self.window.close(),
                event::NoEvent => break,
                e              => state.camera.handle_event(&e)
            }
        }
    }

    fn process_key_press(&mut self, state: &mut TestbedState, code: Key) {
        match code {
            Key::Escape => self.window.close(),
            Key::S      => state.running = RunMode::Step,
            Key::Space  => state.draw_colls = !state.draw_colls,
            Key::T      => {
                if state.running == RunMode::Stop {
                    state.running = RunMode::Running;
                }
                else {
                    state.running = RunMode::Stop;
                }
            },
            _                => { }
        }
    }

    fn process_mouse_press(&mut self, state: &mut TestbedState, button: MouseButton, x: i32, y: i32) {
        match button {
            MouseButton::MouseLeft => {
                let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
                let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
                self.world.interferences_with_point(&mapped_point, |b| {
                    if b.borrow().can_move() {
                        state.grabbed_object = Some(b.clone())
                    }
                });

                match state.grabbed_object {
                    Some(ref b) => {
                        for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                            match state.grabbed_object_joint {
                                Some(ref j) => self.world.remove_fixed(j),
                                None        => { }
                            }

                            let _1: Iso2<f32> = na::one();
                            let attach2 = na::append_translation(&_1, mapped_point.as_vec());
                            let attach1 = na::inv(&na::transformation(b.borrow().position())).unwrap() * attach2;
                            let anchor1 = Anchor::new(Some(state.grabbed_object.as_ref().unwrap().clone()), attach1);
                            let anchor2 = Anchor::new(None, attach2);
                            let joint = Fixed::new(anchor1, anchor2);
                            state.grabbed_object_joint = Some(self.world.add_fixed(joint));
                            node.select()
                        }
                    },
                    None => { }
                }
            },
            _ => {
                state.camera.handle_event(&event::MouseButtonPressed{ button: button, x: x, y: y })
            }
        }
    }

    fn process_mouse_release(&mut self, state: &mut TestbedState, button: MouseButton, x: i32, y: i32) {
        match button {
            MouseButton::MouseLeft => {
                match state.grabbed_object {
                    Some(ref b) => {
                        for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                            node.unselect()
                        }
                    },
                    None => { }
                }

                match state.grabbed_object_joint {
                    Some(ref j) => self.world.remove_fixed(j),
                    None => { }
                }

                state.grabbed_object = None;
                state.grabbed_object_joint = None;
            },
            _ => {
                state.camera.handle_event(&event::MouseButtonReleased{ button: button, x: x, y: y })
            }
        }
    }

    fn process_mouse_moved(&mut self, state: &mut TestbedState, x: i32, y: i32) {
        let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
        let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
        let _1: Iso2<f32> = na::one();
        let attach2 = na::append_translation(&_1, (mapped_point).as_vec());
        match state.grabbed_object {
            Some(_) => {
                let joint = state.grabbed_object_joint.as_ref().unwrap();
                joint.borrow_mut().set_local2(attach2);
            },
            None => state.camera.handle_event(&event::MouseMoved{x: x, y: y})
        };
    }

    fn progress_world(&mut self, state: &mut TestbedState) {
        if state.running != RunMode::Stop {
            self.world.step(0.016);
        }

        if state.running == RunMode::Step {
            state.running = RunMode::Stop;
        }
    }

    fn draw_collisions(&mut self, state: &mut TestbedState) {
        if state.draw_colls {
            draw_helper::draw_colls(&mut self.window, &mut self.world);
        }
    }
}
<|endoftext|>