<commit_before>use base::world::{Chunk, ChunkProvider, World};
use super::GameContext;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::mem::replace;
use std::thread;
use std::cell::{Ref, RefCell};
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
use base::world::{CHUNK_SIZE, ChunkIndex};
use base::math::*;
use world::WorldView;

#[derive(Clone)]
pub struct WorldManager {
    shared: Rc<RefCell<Shared>>,
    chunk_requests: Sender<ChunkIndex>,
    sent_requests: RefCell<HashSet<ChunkIndex>>,
    context: Rc<GameContext>,
}

struct Shared {
    world: World,
    world_view: WorldView,
    provided_chunks: Receiver<(ChunkIndex, Chunk)>,
    load_distance: f32,
    player_chunk: ChunkIndex,
}

impl WorldManager {
    pub fn new(provider: Box<ChunkProvider>, game_context: Rc<GameContext>) -> Self {
        // Create two channels to send chunk positions and receive chunks.
        let (chunk_request_sender, chunk_request_recv) = channel();
        let (chunk_sender, chunk_recv) = channel();

        // Spawn worker thread, which will detach from the main thread. But
        // that is no problem: once the last sender is destroyed, the worker
        // thread will quit.
        thread::spawn(move || {
            worker_thread(provider, chunk_request_recv, chunk_sender);
        });

        let this = WorldManager {
            shared: Rc::new(RefCell::new(Shared {
                world: World::empty(),
                world_view: WorldView::from_world(&World::empty(), game_context.get_facade()),
                provided_chunks: chunk_recv,
                // TODO: load this from the config!
                load_distance: 7.0,
                player_chunk: ChunkIndex(AxialPoint::new(0, 0)),
            })),
            chunk_requests: chunk_request_sender,
            sent_requests: RefCell::new(HashSet::new()),
            context: game_context,
        };

        this.update_player_chunk();
        this
    }

    /// Called when the player moves to a different chunk.
    ///
    /// This unloads all currently loaded chunks that are too far away from the
    /// player, and loads all chunks close enough to the player (if they aren't
    /// already requested).
    fn update_player_chunk(&self) {
        let mut shared = self.shared.borrow_mut();
        let player_chunk = shared.player_chunk.0;
        let radius = shared.load_distance as i32;

        // Load new range
        for qd in -radius..radius {
            for rd in -radius..radius {
                let chunk_pos = AxialPoint::new(player_chunk.q + qd, player_chunk.r + rd);
                if (chunk_pos - player_chunk).to_real().distance(Vector2::zero()) <
                   shared.load_distance {
                    let chunk_index = ChunkIndex(chunk_pos);

                    if !shared.world.chunks.contains_key(&chunk_index) {
                        self.request_chunk(chunk_index);
                    }
                }
            }
        }

        // Drop unneeded chunks from world
        let chunks = replace(&mut shared.world.chunks, HashMap::new());
        let mut new_chunks = HashMap::new();
        for (index, chunk) in chunks {
            let chunk_pos = index.0;
            if (chunk_pos - player_chunk).to_real().distance(Vector2::zero()) <
               shared.load_distance {
                // Still in range
                new_chunks.insert(index, chunk);
            } else {
                // Remove
                shared.world_view.remove_chunk(index);
            }
        }
        shared.world.chunks = new_chunks;
    }

    /// Sends a chunk request if that was not already done for this chunk.
    fn request_chunk(&self, index: ChunkIndex) {
        let mut req = self.sent_requests.borrow_mut();
        if !req.contains(&index) {
            self.chunk_requests.send(index).unwrap();
            req.insert(index);
        }
    }

    /// Returns an immutable reference to the world.
    ///
    /// *Note*: since the world manager uses a `RefCell` to save the world, a
    /// `Ref` is returned. But thanks to deref coercions you can use it like a
    /// standard reference.
    pub fn get_world(&self) -> Ref<World> {
        Ref::map(self.shared.borrow(), |shared| &shared.world)
    }

    /// Returns an immutable reference to the world view.
    pub fn get_view(&self) -> Ref<WorldView> {
        Ref::map(self.shared.borrow(), |shared| &shared.world_view)
    }

    /// Starts to generate all chunks within `load_distance` (config parameter)
    /// around `pos`.
    fn load_world_around(&self, pos: Point2f) {
        let axial_pos = AxialPoint::from_real(pos);
        let chunk_pos = AxialPoint::new(axial_pos.q / CHUNK_SIZE as i32,
                                        axial_pos.r / CHUNK_SIZE as i32);

        let mut shared = self.shared.borrow_mut();
        if shared.player_chunk.0 != chunk_pos {
            shared.player_chunk = ChunkIndex(chunk_pos);
            debug!("player moved to chunk {:?}", chunk_pos);
            drop(shared);

            self.update_player_chunk();
        }
    }

    /// Applies all queued updated to the actual world. Notably, all generated
    /// chunks are added.
    pub fn update_world(&self, player_pos: Point3f) {
        self.load_world_around(Point2f::new(player_pos.x, player_pos.y));

        let mut shared = self.shared.borrow_mut();

        loop {
            let (pos, chunk) = match shared.provided_chunks.try_recv() {
                Err(TryRecvError::Empty) => {
                    // No new chunks for us, we will check again next time ;-)
                    break;
                }
                Err(TryRecvError::Disconnected) => {
                    // The worker thread has shut down: this should never happen
                    // thus we can just panic, too.
                    error!("chunk providing worker thread shut down");
                    panic!("chunk providing worker thread shut down");
                }
                Ok(val) => val,
            };

            shared.world_view.refresh_chunk(pos, &chunk, self.context.get_facade());

            self.sent_requests.borrow_mut().remove(&pos);
            let res = shared.world.add_chunk(pos, chunk);
            if res.is_err() {
                warn!("chunk at {:?} already exists!", pos);
            }
        }
    }
}

fn worker_thread(provider: Box<ChunkProvider>,
                 commands: Receiver<ChunkIndex>,
                 chunks: Sender<(ChunkIndex, Chunk)>) {
    loop {
        let requested_chunk = match commands.recv() {
            Err(_) => {
                // The other side has hung up, so we can stop working, too.
                break;
            }
            Ok(index) => index,
        };

        debug!("chunk provider thread: received request to generate chunk {:?}",
               requested_chunk.0);

        match provider.load_chunk(requested_chunk) {
            Some(chunk) => {
                debug!("chunk provider thread: chunk at {:?} successfully loaded",
                       requested_chunk);
                chunks.send((requested_chunk, chunk)).expect("main thread has hung up");
            }
            None => {
                warn!("chunk provider thread: failed to load chunk at {:?}",
                      requested_chunk)
            }
        }
    }

    info!("chunk provider thread: now stopping ...")
}
<commit_msg>moove to shared<commit_after>use base::world::{Chunk, ChunkProvider, World};
use super::GameContext;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::mem::replace;
use std::thread;
use std::cell::{Ref, RefCell};
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
use base::world::{CHUNK_SIZE, ChunkIndex};
use base::math::*;
use world::WorldView;

#[derive(Clone)]
pub struct WorldManager {
    shared: Rc<RefCell<Shared>>,
    chunk_requests: Sender<ChunkIndex>,
    context: Rc<GameContext>,
}

struct Shared {
    world: World,
    world_view: WorldView,
    provided_chunks: Receiver<(ChunkIndex, Chunk)>,
    sent_requests: HashSet<ChunkIndex>,
    load_distance: f32,
    player_chunk: ChunkIndex,
}

impl WorldManager {
    pub fn new(provider: Box<ChunkProvider>, game_context: Rc<GameContext>) -> Self {
        // Create two channels to send chunk positions and receive chunks.
        let (chunk_request_sender, chunk_request_recv) = channel();
        let (chunk_sender, chunk_recv) = channel();

        // Spawn worker thread, which will detach from the main thread. But
        // that is no problem: once the last sender is destroyed, the worker
        // thread will quit.
        thread::spawn(move || {
            worker_thread(provider, chunk_request_recv, chunk_sender);
        });

        let this = WorldManager {
            shared: Rc::new(RefCell::new(Shared {
                world: World::empty(),
                world_view: WorldView::from_world(&World::empty(), game_context.get_facade()),
                sent_requests: HashSet::new(),
                provided_chunks: chunk_recv,
                // TODO: load this from the config!
                load_distance: 7.0,
                player_chunk: ChunkIndex(AxialPoint::new(0, 0)),
            })),
            chunk_requests: chunk_request_sender,
            context: game_context,
        };

        this.update_player_chunk();
        this
    }

    /// Called when the player moves to a different chunk.
    ///
    /// This unloads all currently loaded chunks that are too far away from the
    /// player, and loads all chunks close enough to the player (if they aren't
    /// already requested).
    fn update_player_chunk(&self) {
        let mut shared = self.shared.borrow_mut();
        let player_chunk = shared.player_chunk.0;
        let radius = shared.load_distance as i32;

        // Load new range
        for qd in -radius..radius {
            for rd in -radius..radius {
                let chunk_pos = AxialPoint::new(player_chunk.q + qd, player_chunk.r + rd);
                if (chunk_pos - player_chunk).to_real().distance(Vector2::zero()) <
                   shared.load_distance {
                    let chunk_index = ChunkIndex(chunk_pos);

                    if !shared.world.chunks.contains_key(&chunk_index) {
                        self.request_chunk(chunk_index, &mut shared.sent_requests);
                    }
                }
            }
        }

        // Drop unneeded chunks from world
        let chunks = replace(&mut shared.world.chunks, HashMap::new());
        let mut new_chunks = HashMap::new();
        for (index, chunk) in chunks {
            let chunk_pos = index.0;
            if (chunk_pos - player_chunk).to_real().distance(Vector2::zero()) <
               shared.load_distance {
                // Still in range
                new_chunks.insert(index, chunk);
            } else {
                // Remove
                shared.world_view.remove_chunk(index);
            }
        }
        shared.world.chunks = new_chunks;
    }

    /// Sends a chunk request if that was not already done for this chunk.
    fn request_chunk(&self, index: ChunkIndex, sent_requests: &mut HashSet<ChunkIndex>) {
        if !sent_requests.contains(&index) {
            self.chunk_requests.send(index).unwrap();
            sent_requests.insert(index);
        }
    }

    /// Returns an immutable reference to the world.
    ///
    /// *Note*: since the world manager uses a `RefCell` to save the world, a
    /// `Ref` is returned. But thanks to deref coercions you can use it like a
    /// standard reference.
    pub fn get_world(&self) -> Ref<World> {
        Ref::map(self.shared.borrow(), |shared| &shared.world)
    }

    /// Returns an immutable reference to the world view.
    pub fn get_view(&self) -> Ref<WorldView> {
        Ref::map(self.shared.borrow(), |shared| &shared.world_view)
    }

    /// Starts to generate all chunks within `load_distance` (config parameter)
    /// around `pos`.
    fn load_world_around(&self, pos: Point2f) {
        let axial_pos = AxialPoint::from_real(pos);
        let chunk_pos = AxialPoint::new(axial_pos.q / CHUNK_SIZE as i32,
                                        axial_pos.r / CHUNK_SIZE as i32);

        let mut shared = self.shared.borrow_mut();
        if shared.player_chunk.0 != chunk_pos {
            shared.player_chunk = ChunkIndex(chunk_pos);
            debug!("player moved to chunk {:?}", chunk_pos);
            drop(shared);

            self.update_player_chunk();
        }
    }

    /// Applies all queued updated to the actual world. Notably, all generated
    /// chunks are added.
    pub fn update_world(&self, player_pos: Point3f) {
        self.load_world_around(Point2f::new(player_pos.x, player_pos.y));

        let mut shared = self.shared.borrow_mut();

        loop {
            let (pos, chunk) = match shared.provided_chunks.try_recv() {
                Err(TryRecvError::Empty) => {
                    // No new chunks for us, we will check again next time ;-)
                    break;
                }
                Err(TryRecvError::Disconnected) => {
                    // The worker thread has shut down: this should never happen
                    // thus we can just panic, too.
                    error!("chunk providing worker thread shut down");
                    panic!("chunk providing worker thread shut down");
                }
                Ok(val) => val,
            };

            shared.world_view.refresh_chunk(pos, &chunk, self.context.get_facade());

            shared.sent_requests.remove(&pos);
            let res = shared.world.add_chunk(pos, chunk);
            if res.is_err() {
                warn!("chunk at {:?} already exists!", pos);
            }
        }
    }
}

fn worker_thread(provider: Box<ChunkProvider>,
                 commands: Receiver<ChunkIndex>,
                 chunks: Sender<(ChunkIndex, Chunk)>) {
    loop {
        let requested_chunk = match commands.recv() {
            Err(_) => {
                // The other side has hung up, so we can stop working, too.
                break;
            }
            Ok(index) => index,
        };

        debug!("chunk provider thread: received request to generate chunk {:?}",
               requested_chunk.0);

        match provider.load_chunk(requested_chunk) {
            Some(chunk) => {
                debug!("chunk provider thread: chunk at {:?} successfully loaded",
                       requested_chunk);
                chunks.send((requested_chunk, chunk)).expect("main thread has hung up");
            }
            None => {
                warn!("chunk provider thread: failed to load chunk at {:?}",
                      requested_chunk)
            }
        }
    }

    info!("chunk provider thread: now stopping ...")
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use Result;

use chrono::Duration;
use serde_ini;

use std::io;
use std::path::PathBuf;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[cfg(not(test))]
const SERVER_URL: &str = "https://api.updatehub.io";

// When running inside a test environment we default to the mock
// server
#[cfg(test)]
use mockito;
#[cfg(test)]
const SERVER_URL: &str = mockito::SERVER_URL;

#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    /// Loads the settings from the filesystem. If
    /// `/etc/updatehub.conf` does not exists, it uses the default
    /// settings.
    pub fn load() -> Result<Self> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(Settings::default())
        }
    }

    // This parses the configuration file, taking into account the
    // needed validations for all fields, and returns either `Self` or
    // `Err`.
    fn parse(content: &str) -> Result<Self> {
        let settings = serde_ini::from_str::<Settings>(content)?;

        if settings.polling.interval < Duration::seconds(60) {
            error!(
                "Invalid setting for polling interval. The interval cannot be less than 60 seconds"
            );
            return Err(SettingsError::InvalidInterval.into());
        }

        if !&settings.network.server_address.starts_with("http://")
            && !&settings.network.server_address.starts_with("https://")
        {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress.into());
        }

        Ok(settings)
    }
}

#[derive(Debug, Fail)]
pub enum SettingsError {
    #[cause]
    #[fail(display = "IO error")]
    Io(io::Error),
    #[cause]
    #[fail(display = "Invalid INI fail")]
    Ini(serde_ini::de::Error),
    #[fail(display = "Invalid interval")]
    InvalidInterval,
    #[fail(display = "Invalid server address")]
    InvalidServerAddress,
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::days(1),
            enabled: true,
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: PathBuf,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network {
            server_address: SERVER_URL.into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected = Settings {
        polling: Polling {
            interval: Duration::seconds(60),
            enabled: false,
        },
        storage: Storage {
            read_only: true,
            runtime_settings: "/run/updatehub/state".into(),
        },
        update: Update {
            download_dir: "/tmp/download".into(),
            install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
        },
        network: Network {
            server_address: "http://localhost".into(),
        },
        firmware: Firmware {
            metadata_path: "/tmp/metadata".into(),
        },
    };

    assert_eq!(
        serde_ini::from_str::<Settings>(ini)
            .map_err(|e| println!("{}", e))
            .ok(),
        Some(expected)
    );
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::default();
    let expected = Settings {
        polling: Polling {
            interval: Duration::days(1),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: SERVER_URL.into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert_eq!(Some(settings), Some(expected));
}
<commit_msg>settings: storage: Change default runtime_settings value<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use Result;

use chrono::Duration;
use serde_ini;

use std::io;
use std::path::PathBuf;

use serde_helpers::de;

const SYSTEM_SETTINGS_PATH: &str = "/etc/updatehub.conf";

#[cfg(not(test))]
const SERVER_URL: &str = "https://api.updatehub.io";

// When running inside a test environment we default to the mock
// server
#[cfg(test)]
use mockito;
#[cfg(test)]
const SERVER_URL: &str = mockito::SERVER_URL;

#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Settings {
    pub polling: Polling,
    pub storage: Storage,
    pub update: Update,
    pub network: Network,
    pub firmware: Firmware,
}

impl Settings {
    /// Loads the settings from the filesystem. If
    /// `/etc/updatehub.conf` does not exists, it uses the default
    /// settings.
    pub fn load() -> Result<Self> {
        use std::fs::File;
        use std::io::Read;
        use std::path::Path;

        let path = Path::new(SYSTEM_SETTINGS_PATH);

        if path.exists() {
            info!(
                "Loading system settings from '{}'...",
                path.to_string_lossy()
            );

            let mut content = String::new();
            File::open(path)?.read_to_string(&mut content)?;

            Ok(Settings::parse(&content)?)
        } else {
            debug!(
                "System settings file {} does not exists.",
                path.to_string_lossy()
            );
            info!("Using default system settings...");
            Ok(Settings::default())
        }
    }

    // This parses the configuration file, taking into account the
    // needed validations for all fields, and returns either `Self` or
    // `Err`.
    fn parse(content: &str) -> Result<Self> {
        let settings = serde_ini::from_str::<Settings>(content)?;

        if settings.polling.interval < Duration::seconds(60) {
            error!(
                "Invalid setting for polling interval. The interval cannot be less than 60 seconds"
            );
            return Err(SettingsError::InvalidInterval.into());
        }

        if !&settings.network.server_address.starts_with("http://")
            && !&settings.network.server_address.starts_with("https://")
        {
            error!("Invalid setting for server address. The server address must use the protocol prefix");
            return Err(SettingsError::InvalidServerAddress.into());
        }

        Ok(settings)
    }
}

#[derive(Debug, Fail)]
pub enum SettingsError {
    #[cause]
    #[fail(display = "IO error")]
    Io(io::Error),
    #[cause]
    #[fail(display = "Invalid INI fail")]
    Ini(serde_ini::de::Error),
    #[fail(display = "Invalid interval")]
    InvalidInterval,
    #[fail(display = "Invalid server address")]
    InvalidServerAddress,
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Polling {
    #[serde(deserialize_with = "de::duration_from_str")]
    pub interval: Duration,
    #[serde(deserialize_with = "de::bool_from_str")]
    pub enabled: bool,
}

impl Default for Polling {
    fn default() -> Self {
        Polling {
            interval: Duration::days(1),
            enabled: true,
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Storage {
    #[serde(deserialize_with = "de::bool_from_str")]
    pub read_only: bool,
    pub runtime_settings: String,
}

impl Default for Storage {
    fn default() -> Self {
        Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub/runtime_settings.conf".into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Update {
    pub download_dir: PathBuf,
    #[serde(rename = "SupportedInstallModes")]
    #[serde(deserialize_with = "de::vec_from_str")]
    pub install_modes: Vec<String>,
}

impl Default for Update {
    fn default() -> Self {
        Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Network {
    pub server_address: String,
}

impl Default for Network {
    fn default() -> Self {
        Network {
            server_address: SERVER_URL.into(),
        }
    }
}

#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct Firmware {
    pub metadata_path: PathBuf,
}

impl Default for Firmware {
    fn default() -> Self {
        Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        }
    }
}

#[test]
fn ok() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";

    let expected = Settings {
        polling: Polling {
            interval: Duration::seconds(60),
            enabled: false,
        },
        storage: Storage {
            read_only: true,
            runtime_settings: "/run/updatehub/state".into(),
        },
        update: Update {
            download_dir: "/tmp/download".into(),
            install_modes: ["mode1", "mode2"].iter().map(|i| i.to_string()).collect(),
        },
        network: Network {
            server_address: "http://localhost".into(),
        },
        firmware: Firmware {
            metadata_path: "/tmp/metadata".into(),
        },
    };

    assert_eq!(
        serde_ini::from_str::<Settings>(ini)
            .map_err(|e| println!("{}", e))
            .ok(),
        Some(expected)
    );
}

#[test]
fn invalid_polling_interval() {
    let ini = r"
[Polling]
Interval=59s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=http://localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn invalid_network_server_address() {
    let ini = r"
[Polling]
Interval=60s
Enabled=false

[Storage]
ReadOnly=true
RuntimeSettings=/run/updatehub/state

[Update]
DownloadDir=/tmp/download
SupportedInstallModes=mode1,mode2

[Network]
ServerAddress=localhost

[Firmware]
MetadataPath=/tmp/metadata
";
    assert!(Settings::parse(ini).is_err());
}

#[test]
fn default() {
    let settings = Settings::default();
    let expected = Settings {
        polling: Polling {
            interval: Duration::days(1),
            enabled: true,
        },
        storage: Storage {
            read_only: false,
            runtime_settings: "/var/lib/updatehub/runtime_settings.conf".into(),
        },
        update: Update {
            download_dir: "/tmp/updatehub".into(),
            install_modes: [
                "dry-run", "copy", "flash", "imxkobs", "raw", "tarball", "ubifs",
            ]
                .iter()
                .map(|i| i.to_string())
                .collect(),
        },
        network: Network {
            server_address: SERVER_URL.into(),
        },
        firmware: Firmware {
            metadata_path: "/usr/share/updatehub".into(),
        },
    };

    assert_eq!(Some(settings), Some(expected));
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
use rustc_serialize::{Encodable, Encoder};

#[derive(RustcEncodable)]
pub struct Location {
    pub address: String,
    pub lat: f64,
    pub lon: f64,
}

#[derive(RustcEncodable)]
pub struct State {
    pub open: bool,
    pub message: String,
}

#[derive(RustcEncodable)]
pub struct Contact {
    pub irc: String,
    pub twitter: String,
    pub foursquare: String,
    pub email: String,
}

#[derive(RustcEncodable)]
pub struct SpaceFED {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

pub struct Feed {
    pub _type: String,
    pub url: String,
}

/// Adapted from the generated code.
/// This is required to translate the `_type` field in the struct to a JSON field called `type`.
impl Encodable for Feed {
    fn encode<S: Encoder>(&self, encoder: &mut S) -> Result<(), S::Error> {
        encoder.emit_struct("Feed", 2usize, |enc| {
            try!(
                enc.emit_struct_field("type", 0usize, |enc| self._type.encode(enc))
            );
            enc.emit_struct_field("url", 1usize, |enc| self.url.encode(enc))
        })
    }
}

#[derive(RustcEncodable)]
pub struct Feeds {
    pub blog: Feed,
}

#[derive(RustcEncodable)]
pub struct Sensors {
    pub people_now_present: [PeopleNowPresentSensor; 1],
}

#[derive(RustcEncodable)]
pub struct PeopleNowPresentSensor {
    pub value: Option<u32>,
    pub location: Option<String>,
    pub name: Option<String>,
    pub description: Option<String>,
}

#[derive(RustcEncodable)]
pub struct Cache {
    pub schedule: String,
}

#[derive(RustcEncodable)]
pub struct Status {
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub spacefed: SpaceFED,
    pub cache: Cache,

    pub state: State,
    pub contact: Contact,
    pub issue_report_channels: [&'static str; 2],

    pub feeds: Feeds,
    pub projects: [&'static str; 3],
    pub sensors: Sensors,
}
<commit_msg>Added spaceapi::TemperatureSensor struct<commit_after>extern crate rustc_serialize;
use rustc_serialize::{Encodable, Encoder};

#[derive(RustcEncodable)]
pub struct Location {
    pub address: String,
    pub lat: f64,
    pub lon: f64,
}

#[derive(RustcEncodable)]
pub struct State {
    pub open: bool,
    pub message: String,
}

#[derive(RustcEncodable)]
pub struct Contact {
    pub irc: String,
    pub twitter: String,
    pub foursquare: String,
    pub email: String,
}

#[derive(RustcEncodable)]
pub struct SpaceFED {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

pub struct Feed {
    pub _type: String,
    pub url: String,
}

/// Adapted from the generated code.
/// This is required to translate the `_type` field in the struct to a JSON field called `type`.
impl Encodable for Feed {
    fn encode<S: Encoder>(&self, encoder: &mut S) -> Result<(), S::Error> {
        encoder.emit_struct("Feed", 2usize, |enc| {
            try!(
                enc.emit_struct_field("type", 0usize, |enc| self._type.encode(enc))
            );
            enc.emit_struct_field("url", 1usize, |enc| self.url.encode(enc))
        })
    }
}

#[derive(RustcEncodable)]
pub struct Feeds {
    pub blog: Feed,
}

#[derive(RustcEncodable)]
pub struct Sensors {
    pub people_now_present: [PeopleNowPresentSensor; 1],
}

#[derive(RustcEncodable)]
pub struct PeopleNowPresentSensor {
    pub value: Option<u32>,  // TODO: This should not be an option
    pub location: Option<String>,
    pub name: Option<String>,
    pub description: Option<String>,
}

#[derive(RustcEncodable)]
pub struct TemperatureSensor {
    pub value: u32,
    pub unit: String,
    pub location: String,
    pub name: Option<String>,
    pub description: Option<String>,
}

#[derive(RustcEncodable)]
pub struct Cache {
    pub schedule: String,
}

#[derive(RustcEncodable)]
pub struct Status {
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub spacefed: SpaceFED,
    pub cache: Cache,

    pub state: State,
    pub contact: Contact,
    pub issue_report_channels: [&'static str; 2],

    pub feeds: Feeds,
    pub projects: [&'static str; 3],
    pub sensors: Sensors,
}
<|endoftext|>


<commit_before>use std::fmt::Debug;
use super::super::*;

pub fn returns<T>() -> impl FnMut(&T) -> MatchResult {
    move |_s: &T| MatchResult::Matched { name: "anything".to_owned() }
}

pub fn this_assertion_fails_always<T>() -> impl FnMut(&T) -> MatchResult {
    move |_s: &T| MatchResult::Failed {
        name: "fails_always".to_owned(),
        reason: format_fail_reason("This matcher fails always")
    }
}

pub fn not<T, M>(mut matcher: M) -> impl FnMut(&T) -> MatchResult
where M: Matcher<T> {
    move |actual: &T| {
        match matcher.check(actual) {
            MatchResult::Matched { name } => MatchResult::Failed {
                name: format!("not({})", name),
                reason: format_fail_reason(&format!("{} is satisfied", name))
            },
            MatchResult::Failed { name, .. } => MatchResult::Matched { name: name },
        }
    }
}

pub fn eq<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialEq + Debug {
    move |actual: &T|
        if *actual == expected {
            MatchResult::Matched { name: "equal".to_owned() }
        } else {
            MatchResult::Failed {
                name: "eq".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}

pub fn less_than<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual < expected {
            MatchResult::Matched { name: "less_than".to_owned() }
        } else {
            MatchResult::Failed {
                name: "less_than".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn lt<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { less_than(expected) }

pub fn greater_than<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual > expected {
            MatchResult::Matched { name: "greater_than".to_owned() }
        } else {
            MatchResult::Failed {
                name: "greater_than".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn gt<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { greater_than(expected) }

pub fn less_than_or_equal<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual <= expected {
            MatchResult::Matched { name: "less_than_or_equal".to_owned() }
        } else {
            MatchResult::Failed {
                name: "less_than_or_equal".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn leq<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { less_than_or_equal(expected) }

pub fn greater_than_or_equal<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual >= expected {
            MatchResult::Matched { name: "greater_than_or_equal".to_owned() }
        } else {
            MatchResult::Failed {
                name: "greater_than_or_equal".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn geq<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { greater_than_or_equal(expected) }

pub fn is_same_object<'a, T>(expected: &'a T) -> impl Fn(&T) -> MatchResult
where T: PartialEq + Debug {
    move |actual: &T|
        if actual == expected {
            MatchResult::Matched { name: "is_same_object".to_owned() }
        } else {
            MatchResult::Failed {
                name: "is_same_object".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
<commit_msg>rename returns() -> assertion_always_succeeds() rename this_assertion_fails_always() -> assertion_always_fails<commit_after>use std::fmt::Debug;
use super::super::*;

pub fn assertion_always_succeeds<T>() -> impl FnMut(&T) -> MatchResult {
    move |_s: &T| MatchResult::Matched { name: "succeeds_always".to_owned() }
}

pub fn assertion_always_fails<T>() -> impl FnMut(&T) -> MatchResult {
    move |_s: &T| MatchResult::Failed {
        name: "fails_always".to_owned(),
        reason: format_fail_reason("This matcher fails always")
    }
}

pub fn not<T, M>(mut matcher: M) -> impl FnMut(&T) -> MatchResult
where M: Matcher<T> {
    move |actual: &T| {
        match matcher.check(actual) {
            MatchResult::Matched { name } => MatchResult::Failed {
                name: format!("not({})", name),
                reason: format_fail_reason(&format!("{} is satisfied", name))
            },
            MatchResult::Failed { name, .. } => MatchResult::Matched { name: name },
        }
    }
}

pub fn eq<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialEq + Debug {
    move |actual: &T|
        if *actual == expected {
            MatchResult::Matched { name: "equal".to_owned() }
        } else {
            MatchResult::Failed {
                name: "eq".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}

pub fn less_than<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual < expected {
            MatchResult::Matched { name: "less_than".to_owned() }
        } else {
            MatchResult::Failed {
                name: "less_than".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn lt<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { less_than(expected) }

pub fn greater_than<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual > expected {
            MatchResult::Matched { name: "greater_than".to_owned() }
        } else {
            MatchResult::Failed {
                name: "greater_than".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn gt<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { greater_than(expected) }

pub fn less_than_or_equal<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual <= expected {
            MatchResult::Matched { name: "less_than_or_equal".to_owned() }
        } else {
            MatchResult::Failed {
                name: "less_than_or_equal".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn leq<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { less_than_or_equal(expected) }

pub fn greater_than_or_equal<T>(expected: T) -> impl Fn(&T) -> MatchResult
where T: PartialOrd + Debug {
    move |actual: &T|
        if *actual >= expected {
            MatchResult::Matched { name: "greater_than_or_equal".to_owned() }
        } else {
            MatchResult::Failed {
                name: "greater_than_or_equal".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
pub fn geq<T: PartialOrd + Debug>(expected: T) -> impl Fn(&T) -> MatchResult { greater_than_or_equal(expected) }

pub fn is_same_object<'a, T>(expected: &'a T) -> impl Fn(&T) -> MatchResult
where T: PartialEq + Debug {
    move |actual: &T|
        if actual == expected {
            MatchResult::Matched { name: "is_same_object".to_owned() }
        } else {
            MatchResult::Failed {
                name: "is_same_object".to_owned(),
                reason: format_fail_comparison(actual, &expected)
            }
        }
}
<|endoftext|>


<commit_before>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{
    mir::{self, interpret::ConstAlloc},
    ty::ScalarInt,
};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
        bug!("intern_const_alloc_recursive should not error in this case")
    }
    ConstValue::Scalar(loc_place.ptr)
}

/// Convert an evaluated constant to a type level constant
pub(crate) fn const_to_valtree<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    raw: ConstAlloc<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let place = ecx.raw_const_to_mplace(raw).unwrap();
    const_to_valtree_inner(&ecx, &place)
}

fn const_to_valtree_inner<'tcx>(
    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
    place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let branches = |n, variant| {
        let place = match variant {
            Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
            None => *place,
        };
        let variant =
            variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
        let fields = (0..n).map(|i| {
            let field = ecx.mplace_field(&place, i).unwrap();
            const_to_valtree_inner(ecx, &field)
        });
        Some(ty::ValTree::Branch(
            ecx.tcx
                .arena
                .alloc_from_iter(variant.into_iter().chain(fields).collect::<Option<Vec<_>>>()?),
        ))
    };
    match place.layout.ty.kind() {
        ty::FnDef(..) => Some(ty::ValTree::zst()),
        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
            let val = ecx.read_immediate(&place.into()).unwrap();
            let val = val.to_scalar().unwrap();
            Some(ty::ValTree::Leaf(val.assert_int()))
        }

        // Raw pointers are not allowed in type level constants, as raw pointers cannot be treated
        // like references. If we looked behind the raw pointer, we may be breaking the meaning of
        // the raw pointer. Equality on raw pointers is performed on the pointer and not on the pointee,
        // and we cannot guarantee any kind of pointer stability in the type system.
        // Technically we could allow function pointers, but they are not guaranteed to be the
        // same as the function pointers at runtime.
        ty::FnPtr(_) | ty::RawPtr(_) => None,
        ty::Ref(..) => unimplemented!("need to use deref_const"),

        // Trait objects are not allowed in type level constants, as we have no concept for
        // resolving their backing type, even if we can do that at const eval time. We may want to consider
        // adding a `ValTree::DownCast(Ty<'tcx>, Box<ValTree>)` in the future, but I don't even know the
        // questions such a concept would open up, so an RFC would probably be good for this.
        ty::Dynamic(..) => None,

        ty::Slice(_) | ty::Str => {
            unimplemented!("need to find the backing data of the slice/str and recurse on that")
        }
        ty::Tuple(substs) => branches(substs.len(), None),
        ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),

        ty::Adt(def, _) => {
            if def.variants.is_empty() {
                // Uninhabited
                return None;
            }

            let variant = ecx.read_discriminant(&place.into()).unwrap().1;

            branches(def.variants[variant].fields.len(), Some(variant))
        }

        ty::Never
        | ty::Error(_)
        | ty::Foreign(..)
        | ty::Infer(ty::FreshIntTy(_))
        | ty::Infer(ty::FreshFloatTy(_))
        | ty::Projection(..)
        | ty::Param(_)
        | ty::Bound(..)
        | ty::Placeholder(..)
        // FIXME(oli-obk): we could look behind opaque types
        | ty::Opaque(..)
        | ty::Infer(_)
        // FIXME(oli-obk): we can probably encode closures just like structs
        | ty::Closure(..)
        | ty::Generator(..)
        | ty::GeneratorWitness(..) => None,
    }
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: &[] };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(&op).unwrap().1;
            let down = ecx.operand_downcast(&op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(&down, i).unwrap();
        let val = op_to_const(&ecx, &field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(&op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
}
<commit_msg>Explain why we do not allow const_to_valtree to read from statics<commit_after>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{
    mir::{self, interpret::ConstAlloc},
    ty::ScalarInt,
};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
        bug!("intern_const_alloc_recursive should not error in this case")
    }
    ConstValue::Scalar(loc_place.ptr)
}

/// Convert an evaluated constant to a type level constant
pub(crate) fn const_to_valtree<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    raw: ConstAlloc<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let ecx = mk_eval_cx(
        tcx, DUMMY_SP, param_env,
        // It is absolutely crucial for soundness that
        // we do not read from static items or other mutable memory.
        false,
    );
    let place = ecx.raw_const_to_mplace(raw).unwrap();
    const_to_valtree_inner(&ecx, &place)
}

fn const_to_valtree_inner<'tcx>(
    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
    place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let branches = |n, variant| {
        let place = match variant {
            Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
            None => *place,
        };
        let variant =
            variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
        let fields = (0..n).map(|i| {
            let field = ecx.mplace_field(&place, i).unwrap();
            const_to_valtree_inner(ecx, &field)
        });
        Some(ty::ValTree::Branch(
            ecx.tcx
                .arena
                .alloc_from_iter(variant.into_iter().chain(fields).collect::<Option<Vec<_>>>()?),
        ))
    };
    match place.layout.ty.kind() {
        ty::FnDef(..) => Some(ty::ValTree::zst()),
        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
            let val = ecx.read_immediate(&place.into()).unwrap();
            let val = val.to_scalar().unwrap();
            Some(ty::ValTree::Leaf(val.assert_int()))
        }

        // Raw pointers are not allowed in type level constants, as raw pointers cannot be treated
        // like references. If we looked behind the raw pointer, we may be breaking the meaning of
        // the raw pointer. Equality on raw pointers is performed on the pointer and not on the pointee,
        // and we cannot guarantee any kind of pointer stability in the type system.
        // Technically we could allow function pointers, but they are not guaranteed to be the
        // same as the function pointers at runtime.
        ty::FnPtr(_) | ty::RawPtr(_) => None,
        ty::Ref(..) => unimplemented!("need to use deref_const"),

        // Trait objects are not allowed in type level constants, as we have no concept for
        // resolving their backing type, even if we can do that at const eval time. We may want to consider
        // adding a `ValTree::DownCast(Ty<'tcx>, Box<ValTree>)` in the future, but I don't even know the
        // questions such a concept would open up, so an RFC would probably be good for this.
        ty::Dynamic(..) => None,

        ty::Slice(_) | ty::Str => {
            unimplemented!("need to find the backing data of the slice/str and recurse on that")
        }
        ty::Tuple(substs) => branches(substs.len(), None),
        ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),

        ty::Adt(def, _) => {
            if def.variants.is_empty() {
                // Uninhabited
                return None;
            }

            let variant = ecx.read_discriminant(&place.into()).unwrap().1;

            branches(def.variants[variant].fields.len(), Some(variant))
        }

        ty::Never
        | ty::Error(_)
        | ty::Foreign(..)
        | ty::Infer(ty::FreshIntTy(_))
        | ty::Infer(ty::FreshFloatTy(_))
        | ty::Projection(..)
        | ty::Param(_)
        | ty::Bound(..)
        | ty::Placeholder(..)
        // FIXME(oli-obk): we could look behind opaque types
        | ty::Opaque(..)
        | ty::Infer(_)
        // FIXME(oli-obk): we can probably encode closures just like structs
        | ty::Closure(..)
        | ty::Generator(..)
        | ty::GeneratorWitness(..) => None,
    }
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: &[] };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(&op).unwrap().1;
            let down = ecx.operand_downcast(&op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(&down, i).unwrap();
        let val = op_to_const(&ecx, &field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(&op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::{HashMap, BTreeMap};
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::rc::Rc;

use serde_json::Value;

use compiler::{ArgValue, Codegen, Component, Lexer, Parser};

/// A type abstracting the functions used for Polly.
pub type PolyFn = Box<Fn(BTreeMap<String, ArgValue>, &Rc<RefCell<Template>>)
                         -> Result<String, String>>;

fn std_functions() -> HashMap<String, PolyFn> {
    use serde_json::Value;
    use compiler::tokens::ArgValue::*;

    let mut map: HashMap<String, PolyFn> = HashMap::new();

    map.insert(String::from("std.each"), Box::new(|args: BTreeMap<String, ArgValue>, parent| {
        let mut output = String::new();
        if let Some(&Json(Some(Value::Array(ref array)))) = args.get("array") {
            if let Some(&Comp(Some(ref component))) = args.get("component") {
                match component.number_of_args() {
                    0 => {
                        for _ in array {
                            output.push_str(&*Template::call_component(&component, parent));
                        }
                        Ok(output)
                    }
                    1 => {
                        let name = component.args().first().unwrap().value();
                        for item in array {
                            let mut map = BTreeMap::new();
                            map.insert(name.clone(), item.clone());
                            output.push_str(&*Template::call_component_with_args(component,
                                                                                parent,
                                                                                map));
                        }
                        Ok(output)
                    }
                    _ => {
                        if let Some(&Value::Object(_)) = array.first() {
                            let iter = array.iter();
                            for json in iter {
                                if let Value::Object(ref object) = *json {
                                    let mut map = BTreeMap::new();
                                    for key in component.args() {
                                        let key = key.value();
                                        if let Some(value) = object.get(&*key) {
                                            map.insert(key, value.clone());
                                        }
                                    }
                                    output.push_str(&*Template::call_component_with_args(&component,
                                                parent,
                                                                                map));
                                }
                            }
                            Ok(output)
                        } else {
                            Err(String::from("JSON wasn't an object, and the component has \
                                              multiple arguments, so it can't be properly \
                                              destructured."))
                        }
                    }
                }
            } else {
                Err(String::from("type passed in wasn't a component"))
            }

        } else {
            Err(format!("type passed in wasn't an array it was: {:#?}",
                        args.get("array")))
        }
    }));

    map.insert(String::from("std.if"),
               Box::new(|args, parent| {

                   if let Some(&Json(Some(ref json))) = args.get("condition") {
                       let condition = match *json {
                           Value::Array(ref array) => !array.is_empty(),
                           Value::Null => false,
                           Value::Bool(ref boolean) => *boolean,
                           Value::I64(ref num) => *num != 0,
                           Value::U64(ref num) => *num != 0,
                           Value::F64(ref num) => *num != 0.0,
                           Value::String(ref string) => !string.is_empty(),
                           Value::Object(ref object) => !object.is_empty(),
                       };

                       if condition {
                           if let Some(&Comp(Some(ref component))) = args.get("component") {
                               if let Some(&Json(Some(ref json))) = args.get("json") {
                                   match component.number_of_args() {
                                       0 => Ok(Template::call_component(component, parent)),
                                       1 => {
                                           let name = component.args().first().unwrap().value();
                                           let mut map = BTreeMap::new();
                                           match json {
                                               &Value::Object(ref object) => {
                                                   if let Some(value) = object.get(&name) {
                                                       map.insert(name, value.clone());
                                                   }
                                               }
                                               rest => {
                                                   map.insert(name, rest.clone());
                                               }
                                           }
                                           Ok(Template::call_component_with_args(component,
                                                                                parent,
                                                                                map))
                                       }
                                       _ => {
                                           if let Value::Object(ref map) = *json {
                                               Ok(Template::call_component_with_args(component,
                                                                                parent,
                                                                                map.clone()))
                                           } else {
                                               Err(String::from("Component has more than one \
                                                                 argument, and the JSON passed \
                                                                 in wasn't an object, so I don't \
                                                                 know how to destructure it."))
                                           }
                                       }
                                   }
                               } else {
                                   Ok(Template::call_component(component, parent))
                               }
                           } else {
                               Err(format!("The component arg, wasn't a component it was a {:#?}",
                                           args.get("component")))
                           }
                       } else {
                           Ok(String::new())
                       }
                   } else {
                       Err(format!("The json arg, wasn't JSON it is {:#?}",
                                   args.get("condition")))
                   }
               }));

    map
}

/// The Polly template.
pub struct Template {
    components: HashMap<String, Component>,
    file: PathBuf,
    functions: HashMap<String, PolyFn>,
    source: String,
    locales_dir: Option<String>,
    variables: BTreeMap<String, Value>,
}

impl Template {
    fn add_components(&mut self,
                      mut components: HashMap<String, Component>)
                      -> Result<(), TemplateError> {
        for (key, value) in components.drain() {
            if let Some(_) = self.components.insert(key, value) {
                return Err(TemplateError::PreDefinedComponent);
            }
        }
        Ok(())
    }

    fn add_component(&mut self, key: String, value: Component) -> Result<(), TemplateError> {
        if let Some(_) = self.components.insert(key, value) {
            return Err(TemplateError::PreDefinedComponent);
        } else {
            return Ok(());
        }
    }

    fn call_component(component: &Component, parent: &Rc<RefCell<Template>>) -> String {
        Codegen::call_component(component, None, parent)
    }

    fn call_component_with_args(component: &Component,
                                parent: &Rc<RefCell<Template>>,
                                map: BTreeMap<String, Value>)
                                -> String {
        Codegen::call_component(component, Some(map), parent)
    }

    /// Get a component from within the template.
    pub fn get_component(&self, name: &str) -> Option<&Component> {
        self.components.get(name)
    }

    /// Get a function from within the template.
    pub fn get_function(&self, name: &str) -> Option<&PolyFn> {
        self.functions.get(name)
    }

    /// Loads the template from the file path.
    pub fn load<P: AsRef<Path>>(file_path: P) -> Result<Self, TemplateError> {
        let source = match Template::read_to_source(file_path.as_ref()) {
            Ok(source) => source,
            Err(error) => return Err(error),
        };

        Ok(Template::new(file_path, source))
    }

    fn new<P: AsRef<Path>, S: Into<String>>(path: P, source: S) -> Self {
        Template {
            components: HashMap::new(),
            file: path.as_ref().to_path_buf(),
            functions: std_functions(),
            source: source.into(),
            locales_dir: Some(String::from("./templates/locales")),
            variables: BTreeMap::new(),
        }
    }

    /// Specify that a template has no locales available.
    pub fn no_locales(mut self) -> Self {
        self.locales_dir = None;
        self
    }

    /// Loads the template from the source provided. The file path is also required, for error
    /// handling
    pub fn load_from_source<P: AsRef<Path>, S: Into<String>>(path: P, source: S) -> Self {
        Template::new(path, source)
    }

    /// Pass in a `serde_json` Object, for the JSON of the template.
    pub fn json(mut self, json: BTreeMap<String, Value>) -> Self {
        self.variables = json;
        self
    }

    /// Override the default locales directory.
    pub fn locales_dir<S: Into<String>>(mut self, locales_dir: S) -> Self {
        self.locales_dir = Some(locales_dir.into());
        self
    }

    /// Registers a function to the template.
    pub fn register(&mut self, name: String, function: PolyFn) -> Result<(), TemplateError> {
        if let Some(_) = self.functions.insert(name, function) {
            Err(TemplateError::PreDefinedFunction)
        } else {
            Ok(())
        }
    }

    pub fn import<P: AsRef<Path>>(&mut self, path: P) -> Result<(), TemplateError> {
        match Template::read_to_source(path) {
            Ok(source) => {
                for (key, value) in Parser::component_pass(Lexer::new(&*source).output()) {
                    if let Err(error) = self.add_component(key, value) {
                        return Err(error);
                    };
                }
                Ok(())
            }
            Err(error) => return Err(error),
        }
    }

    fn read_to_source<P: AsRef<Path>>(path: P) -> Result<String, TemplateError> {
        println!("{:?}", path.as_ref());
        let mut file = File::open(path.as_ref()).unwrap();
        let mut contents = String::new();
        match file.read_to_string(&mut contents) {
            Ok(_) => Ok(contents),
            Err(error) => Err(TemplateError::IoError(error)),
        }
    }

    /// Renders the template into a HTML String.
    pub fn render(mut self, lang: &str) -> Result<String, TemplateError> {
        let output = {
            let parser = Parser::new(Lexer::new(&self.source).output());
            if let Err(error) = self.add_components(parser.get_components()) {
                return Err(error);
            };
            parser.output()
        };
        let file_name = self.file.file_name().unwrap().to_str().unwrap().to_owned();

        let locales_dir = match self.locales_dir {
            Some(ref locales_dir) => locales_dir.clone(),
            None => String::new(),
        };

        if !locales_dir.is_empty() {
            let path = format!("{dir}/{lang}/{file}",
                               dir = locales_dir,
                               lang = lang,
                               file = file_name);
            match Template::read_to_source(path) {
                Ok(source) => {
                    for (key, value) in Parser::component_pass(Lexer::new(&*source).output()) {
                        let new_key = format!("locales.{}", key);
                        if let Err(error) = self.add_component(new_key, value) {
                            return Err(error);
                        };
                    }
                }
                error => return error,
            }
        }

        let source = self.source.to_owned();
        let variables = self.variables.to_owned();

        let mut codegen = Codegen::new(output,
                                       file_name,
                                       source,
                                       variables,
                                       Rc::new(RefCell::new(self)));
        Ok(codegen.generate_html())
    }
}
/// Errors relating to the templating rendering.
#[derive(Debug)]
pub enum TemplateError {
    /// The component called already exists.
    PreDefinedComponent,
    /// The function called already exists.
    PreDefinedFunction,
    /// Any IO errors, from the methods.
    IoError(io::Error),
}

#[allow(dead_code, unused_imports)]
mod tests {
    use super::Template;
    use std::fs::File;
    use std::io::Read;
    use std::collections::BTreeMap;
    use serde_json::Value;

    const BASIC: &'static str = "<!DOCTYPE html>
Hello World!
";
    const BASIC_DE: &'static str = "<!DOCTYPE html>
Hallo Welt!
";
    #[test]
    fn element() {
        assert_eq!(Template::load("./tests/element.polly")
                       .unwrap()
                       .no_locales()
                       .render("en")
                       .unwrap(),
                   BASIC);
    }

    #[test]
    fn component() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        json.insert("world".to_owned(), Value::String("World".to_owned()));
        let result = Template::load("./tests/component.polly")
                         .unwrap()
                         .no_locales()
                         .json(json)
                         .render("en");
        assert_eq!(result.unwrap(), BASIC);
    }

    #[test]
    fn component_imported() {
        let mut template = Template::load("./tests/component_import.polly").unwrap().no_locales();
        template.import("./tests/imported.polly").unwrap();
        assert_eq!(template.render("en").unwrap(), BASIC);
    }

    #[test]
    fn variable() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        json.insert("world".to_owned(), Value::String("World".to_owned()));
        let result = Template::load("./tests/variable.polly")
                         .unwrap()
                         .no_locales()
                         .json(json)
                         .render("en");
        assert_eq!(result.unwrap(), BASIC);
    }

    #[test]
    fn function_each() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        let expected = "<!DOCTYPE \
                        html>

Rust

C++

JavaScript

</bo\
                        dy>";
        json.insert("array".to_owned(),
                    Value::Array(vec![Value::String("Rust".to_owned()),
                                      Value::String("C++".to_owned()),
                                      Value::String("JavaScript".to_owned())]));
        assert_eq!(Template::load("./tests/function_each.polly")
                       .unwrap()
                       .no_locales()
                       .json(json)
                       .render("en")
                       .unwrap(),
                   expected);

    }

    #[test]
    fn function_if() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        json.insert(String::from("condition"), Value::Bool(true));
        json.insert(String::from("text"),
                    Value::String(String::from("Hello World!")));
        assert_eq!(Template::load("./tests/function_if.polly")
                       .unwrap()
                       .no_locales()
                       .json(json)
                       .render("en")
                       .unwrap(),
                   BASIC);

    }

    #[test]
    fn locales() {
        let json: BTreeMap<String, Value> = BTreeMap::new();

        assert_eq!(Template::load("./tests/locales.polly")
                       .unwrap()
                       .json(json.to_owned())
                       .render("en")
                       .unwrap(),
                   BASIC);
        assert_eq!(Template::load("./tests/locales.polly")
                       .unwrap()
                       .json(json)
                       .render("de")
                       .unwrap(),
                   BASIC_DE);
    }

}
<commit_msg>mpre dcocumentation<commit_after>use std::cell::RefCell;
use std::collections::{HashMap, BTreeMap};
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::rc::Rc;

use serde_json::Value;

use compiler::{ArgValue, Codegen, Component, Lexer, Parser};

/// A type abstracting the functions used for Polly.
pub type PolyFn = Box<Fn(BTreeMap<String, ArgValue>, &Rc<RefCell<Template>>)
                         -> Result<String, String>>;

fn std_functions() -> HashMap<String, PolyFn> {
    use serde_json::Value;
    use compiler::tokens::ArgValue::*;

    let mut map: HashMap<String, PolyFn> = HashMap::new();

    map.insert(String::from("std.each"), Box::new(|args: BTreeMap<String, ArgValue>, parent| {
        let mut output = String::new();
        if let Some(&Json(Some(Value::Array(ref array)))) = args.get("array") {
            if let Some(&Comp(Some(ref component))) = args.get("component") {
                match component.number_of_args() {
                    0 => {
                        for _ in array {
                            output.push_str(&*Template::call_component(&component, parent));
                        }
                        Ok(output)
                    }
                    1 => {
                        let name = component.args().first().unwrap().value();
                        for item in array {
                            let mut map = BTreeMap::new();
                            map.insert(name.clone(), item.clone());
                            output.push_str(&*Template::call_component_with_args(component,
                                                                                parent,
                                                                                map));
                        }
                        Ok(output)
                    }
                    _ => {
                        if let Some(&Value::Object(_)) = array.first() {
                            let iter = array.iter();
                            for json in iter {
                                if let Value::Object(ref object) = *json {
                                    let mut map = BTreeMap::new();
                                    for key in component.args() {
                                        let key = key.value();
                                        if let Some(value) = object.get(&*key) {
                                            map.insert(key, value.clone());
                                        }
                                    }
                                    output.push_str(&*Template::call_component_with_args(&component,
                                                parent,
                                                                                map));
                                }
                            }
                            Ok(output)
                        } else {
                            Err(String::from("JSON wasn't an object, and the component has \
                                              multiple arguments, so it can't be properly \
                                              destructured."))
                        }
                    }
                }
            } else {
                Err(String::from("type passed in wasn't a component"))
            }

        } else {
            Err(format!("type passed in wasn't an array it was: {:#?}",
                        args.get("array")))
        }
    }));

    map.insert(String::from("std.if"),
               Box::new(|args, parent| {

                   if let Some(&Json(Some(ref json))) = args.get("condition") {
                       let condition = match *json {
                           Value::Array(ref array) => !array.is_empty(),
                           Value::Null => false,
                           Value::Bool(ref boolean) => *boolean,
                           Value::I64(ref num) => *num != 0,
                           Value::U64(ref num) => *num != 0,
                           Value::F64(ref num) => *num != 0.0,
                           Value::String(ref string) => !string.is_empty(),
                           Value::Object(ref object) => !object.is_empty(),
                       };

                       if condition {
                           if let Some(&Comp(Some(ref component))) = args.get("component") {
                               if let Some(&Json(Some(ref json))) = args.get("json") {
                                   match component.number_of_args() {
                                       0 => Ok(Template::call_component(component, parent)),
                                       1 => {
                                           let name = component.args().first().unwrap().value();
                                           let mut map = BTreeMap::new();
                                           match json {
                                               &Value::Object(ref object) => {
                                                   if let Some(value) = object.get(&name) {
                                                       map.insert(name, value.clone());
                                                   }
                                               }
                                               rest => {
                                                   map.insert(name, rest.clone());
                                               }
                                           }
                                           Ok(Template::call_component_with_args(component,
                                                                                parent,
                                                                                map))
                                       }
                                       _ => {
                                           if let Value::Object(ref map) = *json {
                                               Ok(Template::call_component_with_args(component,
                                                                                parent,
                                                                                map.clone()))
                                           } else {
                                               Err(String::from("Component has more than one \
                                                                 argument, and the JSON passed \
                                                                 in wasn't an object, so I don't \
                                                                 know how to destructure it."))
                                           }
                                       }
                                   }
                               } else {
                                   Ok(Template::call_component(component, parent))
                               }
                           } else {
                               Err(format!("The component arg, wasn't a component it was a {:#?}",
                                           args.get("component")))
                           }
                       } else {
                           Ok(String::new())
                       }
                   } else {
                       Err(format!("The json arg, wasn't JSON it is {:#?}",
                                   args.get("condition")))
                   }
               }));

    map
}

/// The Polly template.
pub struct Template {
    components: HashMap<String, Component>,
    file: PathBuf,
    functions: HashMap<String, PolyFn>,
    source: String,
    locales_dir: Option<String>,
    variables: BTreeMap<String, Value>,
}

impl Template {
    fn add_components(&mut self,
                      mut components: HashMap<String, Component>)
                      -> Result<(), TemplateError> {
        for (key, value) in components.drain() {
            if let Some(_) = self.components.insert(key, value) {
                return Err(TemplateError::PreDefinedComponent);
            }
        }
        Ok(())
    }

    fn add_component(&mut self, key: String, value: Component) -> Result<(), TemplateError> {
        if let Some(_) = self.components.insert(key, value) {
            return Err(TemplateError::PreDefinedComponent);
        } else {
            return Ok(());
        }
    }

    fn call_component(component: &Component, parent: &Rc<RefCell<Template>>) -> String {
        Codegen::call_component(component, None, parent)
    }

    fn call_component_with_args(component: &Component,
                                parent: &Rc<RefCell<Template>>,
                                map: BTreeMap<String, Value>)
                                -> String {
        Codegen::call_component(component, Some(map), parent)
    }

    /// Get a component from within the template.
    pub fn get_component(&self, name: &str) -> Option<&Component> {
        self.components.get(name)
    }

    /// Get a function from within the template.
    pub fn get_function(&self, name: &str) -> Option<&PolyFn> {
        self.functions.get(name)
    }

    /// Loads the template from the file path.
    pub fn load<P: AsRef<Path>>(file_path: P) -> Result<Self, TemplateError> {
        let source = match Template::read_to_source(file_path.as_ref()) {
            Ok(source) => source,
            Err(error) => return Err(error),
        };

        Ok(Template::new(file_path, source))
    }

    fn new<P: AsRef<Path>, S: Into<String>>(path: P, source: S) -> Self {
        Template {
            components: HashMap::new(),
            file: path.as_ref().to_path_buf(),
            functions: std_functions(),
            source: source.into(),
            locales_dir: Some(String::from("./templates/locales")),
            variables: BTreeMap::new(),
        }
    }

    /// Specify that a template has no locales available.
    pub fn no_locales(mut self) -> Self {
        self.locales_dir = None;
        self
    }

    /// Loads the template from the source provided. The file path is also required, for error
    /// handling
    pub fn load_from_source<P: AsRef<Path>, S: Into<String>>(path: P, source: S) -> Self {
        Template::new(path, source)
    }

    /// Pass in a `serde_json` Object, for the JSON of the template.
    pub fn json(mut self, json: BTreeMap<String, Value>) -> Self {
        self.variables = json;
        self
    }

    /// Override the default locales directory.
    pub fn locales_dir<S: Into<String>>(mut self, locales_dir: S) -> Self {
        self.locales_dir = Some(locales_dir.into());
        self
    }

    /// Registers a function to the template.
    pub fn register(&mut self, name: String, function: PolyFn) -> Result<(), TemplateError> {
        if let Some(_) = self.functions.insert(name, function) {
            Err(TemplateError::PreDefinedFunction)
        } else {
            Ok(())
        }
    }

    /// Imports components from another template.
    pub fn import<P: AsRef<Path>>(&mut self, path: P) -> Result<(), TemplateError> {
        match Template::read_to_source(path) {
            Ok(source) => {
                for (key, value) in Parser::component_pass(Lexer::new(&*source).output()) {
                    if let Err(error) = self.add_component(key, value) {
                        return Err(error);
                    };
                }
                Ok(())
            }
            Err(error) => return Err(error),
        }
    }

    fn read_to_source<P: AsRef<Path>>(path: P) -> Result<String, TemplateError> {
        println!("{:?}", path.as_ref());
        let mut file = File::open(path.as_ref()).unwrap();
        let mut contents = String::new();
        match file.read_to_string(&mut contents) {
            Ok(_) => Ok(contents),
            Err(error) => Err(TemplateError::IoError(error)),
        }
    }

    /// Renders the template into a HTML String.
    pub fn render(mut self, lang: &str) -> Result<String, TemplateError> {
        let output = {
            let parser = Parser::new(Lexer::new(&self.source).output());
            if let Err(error) = self.add_components(parser.get_components()) {
                return Err(error);
            };
            parser.output()
        };
        let file_name = self.file.file_name().unwrap().to_str().unwrap().to_owned();

        let locales_dir = match self.locales_dir {
            Some(ref locales_dir) => locales_dir.clone(),
            None => String::new(),
        };

        if !locales_dir.is_empty() {
            let path = format!("{dir}/{lang}/{file}",
                               dir = locales_dir,
                               lang = lang,
                               file = file_name);
            match Template::read_to_source(path) {
                Ok(source) => {
                    for (key, value) in Parser::component_pass(Lexer::new(&*source).output()) {
                        let new_key = format!("locales.{}", key);
                        if let Err(error) = self.add_component(new_key, value) {
                            return Err(error);
                        };
                    }
                }
                error => return error,
            }
        }

        let source = self.source.to_owned();
        let variables = self.variables.to_owned();

        let mut codegen = Codegen::new(output,
                                       file_name,
                                       source,
                                       variables,
                                       Rc::new(RefCell::new(self)));
        Ok(codegen.generate_html())
    }
}
/// Errors relating to the templating rendering.
#[derive(Debug)]
pub enum TemplateError {
    /// The component called already exists.
    PreDefinedComponent,
    /// The function called already exists.
    PreDefinedFunction,
    /// Any IO errors, from the methods.
    IoError(io::Error),
}

#[allow(dead_code, unused_imports)]
mod tests {
    use super::Template;
    use std::fs::File;
    use std::io::Read;
    use std::collections::BTreeMap;
    use serde_json::Value;

    const BASIC: &'static str = "<!DOCTYPE html>
Hello World!
";
    const BASIC_DE: &'static str = "<!DOCTYPE html>
Hallo Welt!
";
    #[test]
    fn element() {
        assert_eq!(Template::load("./tests/element.polly")
                       .unwrap()
                       .no_locales()
                       .render("en")
                       .unwrap(),
                   BASIC);
    }

    #[test]
    fn component() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        json.insert("world".to_owned(), Value::String("World".to_owned()));
        let result = Template::load("./tests/component.polly")
                         .unwrap()
                         .no_locales()
                         .json(json)
                         .render("en");
        assert_eq!(result.unwrap(), BASIC);
    }

    #[test]
    fn component_imported() {
        let mut template = Template::load("./tests/component_import.polly").unwrap().no_locales();
        template.import("./tests/imported.polly").unwrap();
        assert_eq!(template.render("en").unwrap(), BASIC);
    }

    #[test]
    fn variable() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        json.insert("world".to_owned(), Value::String("World".to_owned()));
        let result = Template::load("./tests/variable.polly")
                         .unwrap()
                         .no_locales()
                         .json(json)
                         .render("en");
        assert_eq!(result.unwrap(), BASIC);
    }

    #[test]
    fn function_each() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        let expected = "<!DOCTYPE \
                        html>

Rust

C++

JavaScript

</bo\
                        dy>";
        json.insert("array".to_owned(),
                    Value::Array(vec![Value::String("Rust".to_owned()),
                                      Value::String("C++".to_owned()),
                                      Value::String("JavaScript".to_owned())]));
        assert_eq!(Template::load("./tests/function_each.polly")
                       .unwrap()
                       .no_locales()
                       .json(json)
                       .render("en")
                       .unwrap(),
                   expected);

    }

    #[test]
    fn function_if() {
        let mut json: BTreeMap<String, Value> = BTreeMap::new();
        json.insert(String::from("condition"), Value::Bool(true));
        json.insert(String::from("text"),
                    Value::String(String::from("Hello World!")));
        assert_eq!(Template::load("./tests/function_if.polly")
                       .unwrap()
                       .no_locales()
                       .json(json)
                       .render("en")
                       .unwrap(),
                   BASIC);

    }

    #[test]
    fn locales() {
        let json: BTreeMap<String, Value> = BTreeMap::new();

        assert_eq!(Template::load("./tests/locales.polly")
                       .unwrap()
                       .json(json.to_owned())
                       .render("en")
                       .unwrap(),
                   BASIC);
        assert_eq!(Template::load("./tests/locales.polly")
                       .unwrap()
                       .json(json)
                       .render("de")
                       .unwrap(),
                   BASIC_DE);
    }

}
<|endoftext|>


<commit_before>use libc::system;
use std::ffi::CString;

static TMUX_NAME: &'static str = "tmux";

fn call(command: String) {
    let system_call = CString::new(format!("{} {}", TMUX_NAME, command)).unwrap();
    unsafe { system(system_call.as_ptr()); };
}

pub fn open(session_name: String) -> () {
    call(format!("new -s {} -n {}", session_name, "hello"));
}

static SELECT_LAYOUT: &'static str = "select-layout";
pub fn select_layout(window: String, layout: String) -> () {
    call(format!("{} -t {} {}", SELECT_LAYOUT, window, layout));
}

pub fn new_window(session_name: String, window_name: String) -> () {
    call(format!("new-window -s {} -n {}", session_name, window_name));
}
<commit_msg>Separate the creating and opening of a session.<commit_after>use libc::system;
use std::ffi::CString;

static TMUX_NAME: &'static str = "tmux";

fn call(command: String) {
    let system_call = CString::new(format!("{} {}", TMUX_NAME, command)).unwrap();
    unsafe { system(system_call.as_ptr()); };
}

pub fn open(session_name: String) -> () {
    call(format!("attach -t {}", session_name));
}

pub fn new_session(session_name: String, first_window: String) -> () {
    call(format!("new -d -s {} -n {}", session_name, first_window));
}

static SELECT_LAYOUT: &'static str = "select-layout";
pub fn select_layout(window: String, layout: String) -> () {
    call(format!("{} -t {} {}", SELECT_LAYOUT, window, layout));
}

pub fn new_window(session_name: String, window_name: String) -> () {
    call(format!("new-window -t {} -n {}", session_name, window_name));
}
<|endoftext|>