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

use crate::{
    smoke_test_environment::new_local_swarm,
    test_utils::{create_and_fund_account, transfer_and_reconfig, transfer_coins},
};
use diem_config::config::{BootstrappingMode, ContinuousSyncingMode, NodeConfig};
use diem_rest_client::Client as RestClient;
use diem_sdk::{transaction_builder::TransactionFactory, types::LocalAccount};
use forge::{LocalSwarm, NodeExt, Swarm, SwarmExt};
use std::{
    fs,
    time::{Duration, Instant},
};

const MAX_CATCH_UP_SECS: u64 = 60; // The max time we'll wait for nodes to catch up

#[tokio::test]
async fn test_full_node_bootstrap_outputs() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transaction outputs to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config.state_sync.state_sync_driver.bootstrapping_mode =
        BootstrappingMode::ApplyTransactionOutputsFromGenesis;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, true).await;
}

#[tokio::test]
async fn test_full_node_bootstrap_transactions() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transactions to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config.state_sync.state_sync_driver.bootstrapping_mode =
        BootstrappingMode::ExecuteTransactionsFromGenesis;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactions;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, true).await;
}

#[tokio::test]
async fn test_full_node_continuous_sync_outputs() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transaction outputs to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, false).await;
}

#[tokio::test]
async fn test_full_node_continuous_sync_transactions() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transactions to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactions;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, false).await;
}

/// A helper method that tests that a full node can sync from a validator after
/// a failure and continue to stay up-to-date.
async fn test_full_node_sync(
    full_node_config: NodeConfig,
    mut swarm: LocalSwarm,
    epoch_changes: bool,
) {
    // Start the validator and fullnode (make sure they boot up)
    // Create the fullnode
    let validator_peer_id = swarm.validators().next().unwrap().peer_id();
    let vfn_peer_id = swarm
        .add_validator_fullnode(
            &swarm.versions().max().unwrap(),
            full_node_config,
            validator_peer_id,
        )
        .await
        .unwrap();

    swarm
        .validator_mut(validator_peer_id)
        .unwrap()
        .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();
    for fullnode in swarm.full_nodes_mut() {
        fullnode
            .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
            .await
            .unwrap();
    }

    // Stop the fullnode
    swarm.fullnode_mut(vfn_peer_id).unwrap().stop();

    // Execute a number of transactions on the validator
    let validator_client = swarm.validator(validator_peer_id).unwrap().rest_client();
    let transaction_factory = swarm.chain_info().transaction_factory();

    let mut account_0 = create_and_fund_account(&mut swarm, 1000).await;
    let mut account_1 = create_and_fund_account(&mut swarm, 1000).await;
    execute_transactions(
        &mut swarm,
        &validator_client,
        &transaction_factory,
        &mut account_0,
        &account_1,
        epoch_changes,
    )
    .await;

    // Restart the fullnode and verify it can sync
    swarm
        .fullnode_mut(vfn_peer_id)
        .unwrap()
        .restart()
        .await
        .unwrap();
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();

    // Execute more transactions on the validator and verify the fullnode catches up
    execute_transactions(
        &mut swarm,
        &validator_client,
        &transaction_factory,
        &mut account_1,
        &account_0,
        epoch_changes,
    )
    .await;
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();
}

#[tokio::test]
async fn test_validator_bootstrap_outputs() {
    // Create a swarm of 4 validators with state sync v2 enabled (output syncing)
    let mut swarm = new_local_swarm(4).await;
    for validator in swarm.validators_mut() {
        let mut config = validator.config().clone();
        config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
        config.state_sync.state_sync_driver.bootstrapping_mode =
            BootstrappingMode::ApplyTransactionOutputsFromGenesis;
        config.state_sync.state_sync_driver.continuous_syncing_mode =
            ContinuousSyncingMode::ApplyTransactionOutputs;
        config.save(validator.config_path()).unwrap();
        validator.restart().await.unwrap();
    }

    // Test the ability of the validators to sync
    test_validator_sync(swarm).await;
}

#[tokio::test]
async fn test_validator_bootstrap_transactions() {
    // Create a swarm of 4 validators with state sync v2 enabled (transaction syncing)
    let mut swarm = new_local_swarm(4).await;
    for validator in swarm.validators_mut() {
        let mut config = validator.config().clone();
        config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
        config.state_sync.state_sync_driver.bootstrapping_mode =
            BootstrappingMode::ExecuteTransactionsFromGenesis;
        config.state_sync.state_sync_driver.continuous_syncing_mode =
            ContinuousSyncingMode::ExecuteTransactions;
        config.save(validator.config_path()).unwrap();
        validator.restart().await.unwrap();
    }

    // Test the ability of the validators to sync
    test_validator_sync(swarm).await;
}

/// A helper method that tests that a validator can sync after a failure and
/// continue to stay up-to-date.
async fn test_validator_sync(mut swarm: LocalSwarm) {
    // Launch the swarm and wait for it to be ready
    swarm.launch().await.unwrap();

    // Execute multiple transactions through validator 0
    let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::<Vec<_>>();
    let validator_client_0 = swarm
        .validator(validator_peer_ids[0])
        .unwrap()
        .rest_client();
    let transaction_factory = swarm.chain_info().transaction_factory();
    let mut account_0 = create_and_fund_account(&mut swarm, 1000).await;
    let mut account_1 = create_and_fund_account(&mut swarm, 1000).await;
    execute_transactions(
        &mut swarm,
        &validator_client_0,
        &transaction_factory,
        &mut account_0,
        &account_1,
        true,
    )
    .await;
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();

    // Stop validator 1 and delete the storage
    let validator_1 = validator_peer_ids[1];
    swarm.validator_mut(validator_1).unwrap().stop();
    let node_config = swarm.validator_mut(validator_1).unwrap().config().clone();
    let state_db_path = node_config.storage.dir().join("diemdb");
    assert!(state_db_path.as_path().exists());
    fs::remove_dir_all(state_db_path).unwrap();

    // Execute more transactions
    execute_transactions(
        &mut swarm,
        &validator_client_0,
        &transaction_factory,
        &mut account_1,
        &account_0,
        true,
    )
    .await;

    // Restart validator 1 and wait for all nodes to catchup
    swarm.validator_mut(validator_1).unwrap().start().unwrap();
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();

    // Execute multiple transactions and verify validator 1 can sync
    execute_transactions(
        &mut swarm,
        &validator_client_0,
        &transaction_factory,
        &mut account_0,
        &account_1,
        true,
    )
    .await;
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();
}

/// Executes transactions using the given transaction factory, client and
/// accounts. If `force_epoch_changes` is true, also execute transactions to
/// force reconfigurations.
async fn execute_transactions(
    swarm: &mut LocalSwarm,
    client: &RestClient,
    transaction_factory: &TransactionFactory,
    sender: &mut LocalAccount,
    receiver: &LocalAccount,
    execute_epoch_changes: bool,
) {
    let num_transfers = 10;
    if execute_epoch_changes {
        transfer_and_reconfig(
            client,
            transaction_factory,
            swarm.chain_info().root_account,
            sender,
            receiver,
            num_transfers,
        )
        .await;
    } else {
        for _ in 0..num_transfers {
            // Execute simple transfer transactions
            transfer_coins(client, transaction_factory, sender, receiver, 1).await;
        }
    }
}
<commit_msg>[State Sync] Add simple test for single validator node deployment.<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    smoke_test_environment::new_local_swarm,
    test_utils::{create_and_fund_account, transfer_and_reconfig, transfer_coins},
};
use diem_config::config::{BootstrappingMode, ContinuousSyncingMode, NodeConfig};
use diem_rest_client::Client as RestClient;
use diem_sdk::{transaction_builder::TransactionFactory, types::LocalAccount};
use forge::{LocalSwarm, NodeExt, Swarm, SwarmExt};
use std::{
    fs,
    time::{Duration, Instant},
};

const MAX_CATCH_UP_SECS: u64 = 60; // The max time we'll wait for nodes to catch up

#[tokio::test]
async fn test_full_node_bootstrap_outputs() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transaction outputs to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config.state_sync.state_sync_driver.bootstrapping_mode =
        BootstrappingMode::ApplyTransactionOutputsFromGenesis;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, true).await;
}

#[tokio::test]
async fn test_full_node_bootstrap_transactions() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transactions to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config.state_sync.state_sync_driver.bootstrapping_mode =
        BootstrappingMode::ExecuteTransactionsFromGenesis;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactions;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, true).await;
}

#[tokio::test]
async fn test_full_node_continuous_sync_outputs() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transaction outputs to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, false).await;
}

#[tokio::test]
async fn test_full_node_continuous_sync_transactions() {
    // Create a validator swarm of 1 validator node
    let swarm = new_local_swarm(1).await;

    // Create a fullnode config that uses transactions to sync
    let mut vfn_config = NodeConfig::default_for_validator_full_node();
    vfn_config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    vfn_config
        .state_sync
        .state_sync_driver
        .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactions;

    // Test the ability of the fullnode to sync
    test_full_node_sync(vfn_config, swarm, false).await;
}

/// A helper method that tests that a full node can sync from a validator after
/// a failure and continue to stay up-to-date.
async fn test_full_node_sync(
    full_node_config: NodeConfig,
    mut swarm: LocalSwarm,
    epoch_changes: bool,
) {
    // Start the validator and fullnode (make sure they boot up)
    // Create the fullnode
    let validator_peer_id = swarm.validators().next().unwrap().peer_id();
    let vfn_peer_id = swarm
        .add_validator_fullnode(
            &swarm.versions().max().unwrap(),
            full_node_config,
            validator_peer_id,
        )
        .await
        .unwrap();

    swarm
        .validator_mut(validator_peer_id)
        .unwrap()
        .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();
    for fullnode in swarm.full_nodes_mut() {
        fullnode
            .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
            .await
            .unwrap();
    }

    // Stop the fullnode
    swarm.fullnode_mut(vfn_peer_id).unwrap().stop();

    // Execute a number of transactions on the validator
    let validator_client = swarm.validator(validator_peer_id).unwrap().rest_client();
    let transaction_factory = swarm.chain_info().transaction_factory();

    let mut account_0 = create_and_fund_account(&mut swarm, 1000).await;
    let mut account_1 = create_and_fund_account(&mut swarm, 1000).await;
    execute_transactions(
        &mut swarm,
        &validator_client,
        &transaction_factory,
        &mut account_0,
        &account_1,
        epoch_changes,
    )
    .await;

    // Restart the fullnode and verify it can sync
    swarm
        .fullnode_mut(vfn_peer_id)
        .unwrap()
        .restart()
        .await
        .unwrap();
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();

    // Execute more transactions on the validator and verify the fullnode catches up
    execute_transactions(
        &mut swarm,
        &validator_client,
        &transaction_factory,
        &mut account_1,
        &account_0,
        epoch_changes,
    )
    .await;
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();
}

#[tokio::test]
async fn test_validator_bootstrap_outputs() {
    // Create a swarm of 4 validators with state sync v2 enabled (output syncing)
    let mut swarm = new_local_swarm(4).await;
    for validator in swarm.validators_mut() {
        let mut config = validator.config().clone();
        config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
        config.state_sync.state_sync_driver.bootstrapping_mode =
            BootstrappingMode::ApplyTransactionOutputsFromGenesis;
        config.state_sync.state_sync_driver.continuous_syncing_mode =
            ContinuousSyncingMode::ApplyTransactionOutputs;
        config.save(validator.config_path()).unwrap();
        validator.restart().await.unwrap();
    }

    // Test the ability of the validators to sync
    test_validator_sync(swarm).await;
}

#[tokio::test]
async fn test_validator_bootstrap_transactions() {
    // Create a swarm of 4 validators with state sync v2 enabled (transaction syncing)
    let mut swarm = new_local_swarm(4).await;
    for validator in swarm.validators_mut() {
        let mut config = validator.config().clone();
        config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
        config.state_sync.state_sync_driver.bootstrapping_mode =
            BootstrappingMode::ExecuteTransactionsFromGenesis;
        config.state_sync.state_sync_driver.continuous_syncing_mode =
            ContinuousSyncingMode::ExecuteTransactions;
        config.save(validator.config_path()).unwrap();
        validator.restart().await.unwrap();
    }

    // Test the ability of the validators to sync
    test_validator_sync(swarm).await;
}

/// A helper method that tests that a validator can sync after a failure and
/// continue to stay up-to-date.
async fn test_validator_sync(mut swarm: LocalSwarm) {
    // Launch the swarm and wait for it to be ready
    swarm.launch().await.unwrap();

    // Execute multiple transactions through validator 0
    let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::<Vec<_>>();
    let validator_client_0 = swarm
        .validator(validator_peer_ids[0])
        .unwrap()
        .rest_client();
    let transaction_factory = swarm.chain_info().transaction_factory();
    let mut account_0 = create_and_fund_account(&mut swarm, 1000).await;
    let mut account_1 = create_and_fund_account(&mut swarm, 1000).await;
    execute_transactions(
        &mut swarm,
        &validator_client_0,
        &transaction_factory,
        &mut account_0,
        &account_1,
        true,
    )
    .await;
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();

    // Stop validator 1 and delete the storage
    let validator_1 = validator_peer_ids[1];
    swarm.validator_mut(validator_1).unwrap().stop();
    let node_config = swarm.validator_mut(validator_1).unwrap().config().clone();
    let state_db_path = node_config.storage.dir().join("diemdb");
    assert!(state_db_path.as_path().exists());
    fs::remove_dir_all(state_db_path).unwrap();

    // Execute more transactions
    execute_transactions(
        &mut swarm,
        &validator_client_0,
        &transaction_factory,
        &mut account_1,
        &account_0,
        true,
    )
    .await;

    // Restart validator 1 and wait for all nodes to catchup
    swarm.validator_mut(validator_1).unwrap().start().unwrap();
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();

    // Execute multiple transactions and verify validator 1 can sync
    execute_transactions(
        &mut swarm,
        &validator_client_0,
        &transaction_factory,
        &mut account_0,
        &account_1,
        true,
    )
    .await;
    swarm
        .wait_for_all_nodes_to_catchup(Instant::now() + Duration::from_secs(MAX_CATCH_UP_SECS))
        .await
        .unwrap();
}

#[tokio::test]
async fn test_single_validator_failure() {
    // Create a swarm of 1 validator
    let mut swarm = new_local_swarm(1).await;
    swarm.launch().await.unwrap();

    // Enable state sync v2 and reboot the node
    let validator = swarm.validators_mut().next().unwrap();
    let mut config = validator.config().clone();
    config.state_sync.state_sync_driver.enable_state_sync_v2 = true;
    config.save(validator.config_path()).unwrap();
    validator.stop();
    swarm.launch().await.unwrap();

    // Execute multiple transactions
    let validator = swarm.validators_mut().next().unwrap();
    let validator_client = validator.rest_client();
    let transaction_factory = swarm.chain_info().transaction_factory();
    let mut account_0 = create_and_fund_account(&mut swarm, 1000).await;
    let mut account_1 = create_and_fund_account(&mut swarm, 1000).await;
    execute_transactions(
        &mut swarm,
        &validator_client,
        &transaction_factory,
        &mut account_0,
        &account_1,
        true,
    )
    .await;

    // Restart the validator
    let validator = swarm.validators_mut().next().unwrap();
    validator.stop();
    swarm.launch().await.unwrap();

    // Execute more transactions
    execute_transactions(
        &mut swarm,
        &validator_client,
        &transaction_factory,
        &mut account_1,
        &account_0,
        true,
    )
    .await;
}

/// Executes transactions using the given transaction factory, client and
/// accounts. If `force_epoch_changes` is true, also execute transactions to
/// force reconfigurations.
async fn execute_transactions(
    swarm: &mut LocalSwarm,
    client: &RestClient,
    transaction_factory: &TransactionFactory,
    sender: &mut LocalAccount,
    receiver: &LocalAccount,
    execute_epoch_changes: bool,
) {
    let num_transfers = 10;
    if execute_epoch_changes {
        transfer_and_reconfig(
            client,
            transaction_factory,
            swarm.chain_info().root_account,
            sender,
            receiver,
            num_transfers,
        )
        .await;
    } else {
        for _ in 0..num_transfers {
            // Execute simple transfer transactions
            transfer_coins(client, transaction_factory, sender, receiver, 1).await;
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

extern crate glib;
use glib::prelude::*;
extern crate gio;
use gio::prelude::*;

extern crate gstreamer as gst;
use gst::prelude::*;

extern crate gstfallbackswitch;

extern crate gtk;
use gtk::prelude::*;
use std::cell::RefCell;
use std::env;

fn create_pipeline() -> (gst::Pipeline, gst::Pad, gst::Element, gtk::Widget) {
    let pipeline = gst::Pipeline::new(None);

    let video_src = gst::ElementFactory::make("videotestsrc", None).unwrap();
    video_src.set_property("is-live", &true).unwrap();
    video_src.set_property_from_str("pattern", "ball");

    let fallback_video_src = gst::ElementFactory::make("videotestsrc", None).unwrap();

    let fallbackswitch = gst::ElementFactory::make("fallbackswitch", None).unwrap();
    fallbackswitch
        .set_property("timeout", &gst::SECOND)
        .unwrap();

    let videoconvert = gst::ElementFactory::make("videoconvert", None).unwrap();

    let (video_sink, video_widget) =
        //if let Some(gtkglsink) = gst::ElementFactory::make("gtkglsink", None) {
        //    let glsinkbin = gst::ElementFactory::make("glsinkbin", None).unwrap();
        //    glsinkbin.set_property("sink", &gtkglsink).unwrap();

        //    let widget = gtkglsink.get_property("widget").unwrap();
        //    (glsinkbin, widget.get::<gtk::Widget>().unwrap().unwrap())
        //} else
        {
            let sink = gst::ElementFactory::make("gtksink", None).unwrap();
            let widget = sink.get_property("widget").unwrap();
            (sink, widget.get::<gtk::Widget>().unwrap().unwrap())
        };

    pipeline
        .add_many(&[
            &video_src,
            &fallback_video_src,
            &fallbackswitch,
            &videoconvert,
            &video_sink,
        ])
        .unwrap();

    video_src
        .link_pads(Some("src"), &fallbackswitch, Some("sink"))
        .unwrap();
    fallback_video_src
        .link_pads(Some("src"), &fallbackswitch, Some("fallback_sink"))
        .unwrap();
    fallbackswitch
        .link_pads(Some("src"), &videoconvert, Some("sink"))
        .unwrap();
    videoconvert
        .link_pads(Some("src"), &video_sink, Some("sink"))
        .unwrap();

    (
        pipeline,
        video_src.get_static_pad("src").unwrap(),
        video_sink,
        video_widget,
    )
}

fn create_ui(app: &gtk::Application) {
    let (pipeline, video_src_pad, video_sink, video_widget) = create_pipeline();

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    window.set_default_size(320, 240);
    let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
    vbox.pack_start(&video_widget, true, true, 0);

    let position_label = gtk::Label::new(Some("Position: 00:00:00"));
    vbox.pack_start(&position_label, true, true, 5);

    let drop_button = gtk::ToggleButton::new_with_label("Drop Signal");
    vbox.pack_start(&drop_button, true, true, 5);

    window.add(&vbox);
    window.show_all();

    app.add_window(&window);

    let video_sink_weak = video_sink.downgrade();
    let timeout_id = gtk::timeout_add(100, move || {
        let video_sink = match video_sink_weak.upgrade() {
            Some(video_sink) => video_sink,
            None => return glib::Continue(true),
        };

        let position = video_sink
            .query_position::<gst::ClockTime>()
            .unwrap_or_else(|| 0.into());
        position_label.set_text(&format!("Position: {:.1}", position));

        glib::Continue(true)
    });

    let video_src_pad_weak = video_src_pad.downgrade();
    let drop_id = RefCell::new(None);
    drop_button.connect_toggled(move |drop_button| {
        let video_src_pad = match video_src_pad_weak.upgrade() {
            Some(video_src_pad) => video_src_pad,
            None => return,
        };

        let drop = drop_button.get_active();
        if drop {
            let mut drop_id = drop_id.borrow_mut();
            if drop_id.is_none() {
                *drop_id = video_src_pad
                    .add_probe(gst::PadProbeType::BUFFER, |_, _| gst::PadProbeReturn::Drop);
            }
        } else {
            if let Some(drop_id) = drop_id.borrow_mut().take() {
                video_src_pad.remove_probe(drop_id);
            }
        }
    });

    let app_weak = app.downgrade();
    window.connect_delete_event(move |_, _| {
        let app = match app_weak.upgrade() {
            Some(app) => app,
            None => return Inhibit(false),
        };

        app.quit();
        Inhibit(false)
    });

    let bus = pipeline.get_bus().unwrap();
    let app_weak = app.downgrade();
    bus.add_watch_local(move |_, msg| {
        use gst::MessageView;

        let app = match app_weak.upgrade() {
            Some(app) => app,
            None => return glib::Continue(false),
        };

        match msg.view() {
            MessageView::Eos(..) => app.quit(),
            MessageView::Error(err) => {
                println!(
                    "Error from {:?}: {} ({:?})",
                    msg.get_src().map(|s| s.get_path_string()),
                    err.get_error(),
                    err.get_debug()
                );
                app.quit();
            }
            _ => (),
        };

        glib::Continue(true)
    });

    pipeline.set_state(gst::State::Playing).unwrap();

    // Pipeline reference is owned by the closure below, so will be
    // destroyed once the app is destroyed
    let timeout_id = RefCell::new(Some(timeout_id));
    app.connect_shutdown(move |_| {
        pipeline.set_state(gst::State::Null).unwrap();

        bus.remove_watch().unwrap();

        if let Some(timeout_id) = timeout_id.borrow_mut().take() {
            glib::source_remove(timeout_id);
        }
    });
}

fn main() {
    gst::init().unwrap();
    gtk::init().unwrap();

    gstfallbackswitch::plugin_register_static().expect("Failed to register fallbackswitch plugin");

    let app = gtk::Application::new(None, gio::ApplicationFlags::FLAGS_NONE).unwrap();

    app.connect_activate(create_ui);
    let args = env::args().collect::<Vec<_>>();
    app.run(&args);
}
<commit_msg>fallbackswitch: Allow using compressed video in the example application<commit_after>// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

extern crate glib;
use glib::prelude::*;
extern crate gio;
use gio::prelude::*;

extern crate gstreamer as gst;
use gst::prelude::*;

extern crate gstfallbackswitch;

extern crate gtk;
use gtk::prelude::*;
use std::cell::RefCell;
use std::env;

const MAIN_PIPELINE: &str = "videotestsrc is-live=true pattern=ball";
const FALLBACK_PIPELINE: &str = "videotestsrc is-live=true pattern=snow";

//const MAIN_PIPELINE: &str = "videotestsrc is-live=true pattern=ball ! x264enc tune=zerolatency";
//const FALLBACK_PIPELINE: &str = "videotestsrc is-live=true pattern=snow ! x264enc tune=zerolatency";

fn create_pipeline() -> (gst::Pipeline, gst::Pad, gst::Element, gtk::Widget) {
    let pipeline = gst::Pipeline::new(None);

    let video_src = gst::parse_bin_from_description(MAIN_PIPELINE, true)
        .unwrap()
        .upcast();
    let fallback_video_src = gst::parse_bin_from_description(&FALLBACK_PIPELINE, true)
        .unwrap()
        .upcast();

    let fallbackswitch = gst::ElementFactory::make("fallbackswitch", None).unwrap();
    fallbackswitch
        .set_property("timeout", &gst::SECOND)
        .unwrap();

    let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();
    let videoconvert = gst::ElementFactory::make("videoconvert", None).unwrap();

    let videoconvert_clone = videoconvert.clone();
    decodebin.connect_pad_added(move |_, pad| {
        let caps = pad.get_current_caps().unwrap();
        let s = caps.get_structure(0).unwrap();

        let sinkpad = videoconvert_clone.get_static_pad("sink").unwrap();

        if s.get_name() == "video/x-raw" && !sinkpad.is_linked() {
            pad.link(&sinkpad).unwrap();
        }
    });

    let (video_sink, video_widget) =
        //if let Some(gtkglsink) = gst::ElementFactory::make("gtkglsink", None) {
        //    let glsinkbin = gst::ElementFactory::make("glsinkbin", None).unwrap();
        //    glsinkbin.set_property("sink", &gtkglsink).unwrap();

        //    let widget = gtkglsink.get_property("widget").unwrap();
        //    (glsinkbin, widget.get::<gtk::Widget>().unwrap().unwrap())
        //} else
        {
            let sink = gst::ElementFactory::make("gtksink", None).unwrap();
            let widget = sink.get_property("widget").unwrap();
            (sink, widget.get::<gtk::Widget>().unwrap().unwrap())
        };

    pipeline
        .add_many(&[
            &video_src,
            &fallback_video_src,
            &fallbackswitch,
            &decodebin,
            &videoconvert,
            &video_sink,
        ])
        .unwrap();

    video_src
        .link_pads(Some("src"), &fallbackswitch, Some("sink"))
        .unwrap();
    fallback_video_src
        .link_pads(Some("src"), &fallbackswitch, Some("fallback_sink"))
        .unwrap();
    fallbackswitch
        .link_pads(Some("src"), &decodebin, Some("sink"))
        .unwrap();
    videoconvert
        .link_pads(Some("src"), &video_sink, Some("sink"))
        .unwrap();

    (
        pipeline,
        video_src.get_static_pad("src").unwrap(),
        video_sink,
        video_widget,
    )
}

fn create_ui(app: &gtk::Application) {
    let (pipeline, video_src_pad, video_sink, video_widget) = create_pipeline();

    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    window.set_default_size(320, 240);
    let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
    vbox.pack_start(&video_widget, true, true, 0);

    let position_label = gtk::Label::new(Some("Position: 00:00:00"));
    vbox.pack_start(&position_label, true, true, 5);

    let drop_button = gtk::ToggleButton::new_with_label("Drop Signal");
    vbox.pack_start(&drop_button, true, true, 5);

    window.add(&vbox);
    window.show_all();

    app.add_window(&window);

    let video_sink_weak = video_sink.downgrade();
    let timeout_id = gtk::timeout_add(100, move || {
        let video_sink = match video_sink_weak.upgrade() {
            Some(video_sink) => video_sink,
            None => return glib::Continue(true),
        };

        let position = video_sink
            .query_position::<gst::ClockTime>()
            .unwrap_or_else(|| 0.into());
        position_label.set_text(&format!("Position: {:.1}", position));

        glib::Continue(true)
    });

    let video_src_pad_weak = video_src_pad.downgrade();
    let drop_id = RefCell::new(None);
    drop_button.connect_toggled(move |drop_button| {
        let video_src_pad = match video_src_pad_weak.upgrade() {
            Some(video_src_pad) => video_src_pad,
            None => return,
        };

        let drop = drop_button.get_active();
        if drop {
            let mut drop_id = drop_id.borrow_mut();
            if drop_id.is_none() {
                *drop_id = video_src_pad
                    .add_probe(gst::PadProbeType::BUFFER, |_, _| gst::PadProbeReturn::Drop);
            }
        } else {
            if let Some(drop_id) = drop_id.borrow_mut().take() {
                video_src_pad.remove_probe(drop_id);
            }
        }
    });

    let app_weak = app.downgrade();
    window.connect_delete_event(move |_, _| {
        let app = match app_weak.upgrade() {
            Some(app) => app,
            None => return Inhibit(false),
        };

        app.quit();
        Inhibit(false)
    });

    let bus = pipeline.get_bus().unwrap();
    let app_weak = app.downgrade();
    bus.add_watch_local(move |_, msg| {
        use gst::MessageView;

        let app = match app_weak.upgrade() {
            Some(app) => app,
            None => return glib::Continue(false),
        };

        match msg.view() {
            MessageView::Eos(..) => app.quit(),
            MessageView::Error(err) => {
                println!(
                    "Error from {:?}: {} ({:?})",
                    msg.get_src().map(|s| s.get_path_string()),
                    err.get_error(),
                    err.get_debug()
                );
                app.quit();
            }
            _ => (),
        };

        glib::Continue(true)
    });

    pipeline.set_state(gst::State::Playing).unwrap();

    // Pipeline reference is owned by the closure below, so will be
    // destroyed once the app is destroyed
    let timeout_id = RefCell::new(Some(timeout_id));
    app.connect_shutdown(move |_| {
        pipeline.set_state(gst::State::Null).unwrap();

        bus.remove_watch().unwrap();

        if let Some(timeout_id) = timeout_id.borrow_mut().take() {
            glib::source_remove(timeout_id);
        }
    });
}

fn main() {
    gst::init().unwrap();
    gtk::init().unwrap();

    gstfallbackswitch::plugin_register_static().expect("Failed to register fallbackswitch plugin");

    let app = gtk::Application::new(None, gio::ApplicationFlags::FLAGS_NONE).unwrap();

    app.connect_activate(create_ui);
    let args = env::args().collect::<Vec<_>>();
    app.run(&args);
}
<|endoftext|>


<commit_before><ATTRIBUTE>#[macro_use]</ATTRIBUTE>
extern crate <CRATE>log</CRATE>;

use std::collections::<STRUCT>HashMap</STRUCT>;
use std::rc::<STRUCT>Rc</STRUCT>;

mod <MODULE>stuff</MODULE>;

pub enum <ENUM>Flag</ENUM> {
    <ENUM_VARIANT>Good</ENUM_VARIANT>,
    <ENUM_VARIANT>Bad</ENUM_VARIANT>,
    <ENUM_VARIANT>Ugly</ENUM_VARIANT>
}

const <CONSTANT>QUALITY</CONSTANT>: <ENUM>Flag</ENUM> = <ENUM>Flag</ENUM>::<ENUM_VARIANT>Good</ENUM_VARIANT>;

pub trait <TRAIT>Write</TRAIT> {
    fn <METHOD>write</METHOD>(&mut <SELF_PARAMETER>self</SELF_PARAMETER>, <PARAMETER>buf</PARAMETER>: &[<PRIMITIVE_TYPE>u8</PRIMITIVE_TYPE>]) -> <ENUM>Result</ENUM><usize>;
}

struct <STRUCT>Object</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>> {
    <FIELD>flag</FIELD>: <ENUM>Flag</ENUM>,
    <FIELD>fields</FIELD>: <STRUCT>HashMap</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>, <PRIMITIVE_TYPE>u64</PRIMITIVE_TYPE>>
}

<KEYWORD>union</KEYWORD> <UNION>MyUnion</UNION> {
    <FIELD>f1</FIELD>: <PRIMITIVE_TYPE>u32</PRIMITIVE_TYPE>,
    <FIELD>f2</FIELD>: <PRIMITIVE_TYPE>f32</PRIMITIVE_TYPE>,
}

type <TYPE_ALIAS>RcObject</TYPE_ALIAS><<TYPE_PARAMETER>T</TYPE_PARAMETER>> = <STRUCT>Rc</STRUCT><<STRUCT>Object</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>>>;

impl<<TYPE_PARAMETER>T</TYPE_PARAMETER>> Write for <STRUCT>Object</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>> {
    fn <METHOD>write</METHOD>(&mut <SELF_PARAMETER>self</SELF_PARAMETER>, <PARAMETER>buf</PARAMETER>: &[<PRIMITIVE_TYPE>u8</PRIMITIVE_TYPE>]) -> <ENUM>Result</ENUM><usize> {
        let s = stuff::<FUNCTION>write_map</FUNCTION>(&self.<FIELD>fields</FIELD>, <PARAMETER>buf</PARAMETER>)<Q_OPERATOR>?</Q_OPERATOR>;
        <MACRO>info!</MACRO>("{} byte(s) written", s);
        <ENUM_VARIANT>Ok</ENUM_VARIANT>(s)
    }
}

/* Block comment */
fn <FUNCTION>main</FUNCTION>() {
    // A simple integer calculator:
    // `+` or `-` means add or subtract by 1
    // `*` or `/` means multiply or divide by 2
    <MODULE>stuff</MODULE>::<STRUCT>AppVersion</STRUCT>::<ASSOC_FUNCTION>print</ASSOC_FUNCTION>();

    let input = <ENUM>Option</ENUM>::<ENUM_VARIANT>None</ENUM_VARIANT>;
    let program = input.<METHOD>unwrap_or_else</METHOD>(|| "+ + * - /");
    let mut <MUT_BINDING>accumulator</MUT_BINDING> = 0;

    for token in program.<METHOD>chars</METHOD>() {
        match token {
            '+' => <MUT_BINDING>accumulator</MUT_BINDING> += 1,
            '-' => <MUT_BINDING>accumulator</MUT_BINDING> -= 1,
            '*' => <MUT_BINDING>accumulator</MUT_BINDING> *= 2,
            '/' => <MUT_BINDING>accumulator</MUT_BINDING> /= 2,
            _ => { /* ignore everything else */ }
        }
    }

    <MACRO>info!</MACRO>("The program \"{}\" calculates the value {}",
             program, <MUT_BINDING>accumulator</MUT_BINDING>);
}

/// Some documentation `with code`
/// # Heading
/// [Rust](https://www.rust-lang.org/)
<ATTRIBUTE>#[cfg(target_os=</ATTRIBUTE>"linux"<ATTRIBUTE>)]</ATTRIBUTE>
<KEYWORD_UNSAFE>unsafe</KEYWORD_UNSAFE> fn <FUNCTION>a_function</FUNCTION><<TYPE_PARAMETER>T</TYPE_PARAMETER>: <LIFETIME>'lifetime</LIFETIME>>(<MUT_PARAMETER>count</MUT_PARAMETER>: &mut i64) -> ! {
    <MUT_PARAMETER>count</MUT_PARAMETER> += 1;
    'label: loop {
        <MACRO>println!</MACRO>("Hello\x20W\u{f3}rld!\u{abcdef}");
    }
}

fn <FUNCTION>test</FUNCTION>() {
    <KEYWORD_UNSAFE>unsafe</KEYWORD_UNSAFE> {
        <UNSAFE_CODE>a_function</UNSAFE_CODE>(1);
    }
}
<commit_msg>ANN: update highlighter demo according to the latest changes<commit_after><ATTRIBUTE>#[macro_use]</ATTRIBUTE>
extern crate <CRATE>log</CRATE>;

use std::collections::<STRUCT>HashMap</STRUCT>;
use std::rc::<STRUCT>Rc</STRUCT>;

mod <MODULE>stuff</MODULE>;

pub enum <ENUM>Flag</ENUM> {
    <ENUM_VARIANT>Good</ENUM_VARIANT>,
    <ENUM_VARIANT>Bad</ENUM_VARIANT>,
    <ENUM_VARIANT>Ugly</ENUM_VARIANT>
}

const <CONSTANT>QUALITY</CONSTANT>: <ENUM>Flag</ENUM> = <ENUM>Flag</ENUM>::<ENUM_VARIANT>Good</ENUM_VARIANT>;

struct Table<const <CONST_PARAMETER>N</CONST_PARAMETER>: usize>([[i32; <CONST_PARAMETER>N</CONST_PARAMETER>]; <CONST_PARAMETER>N</CONST_PARAMETER>])

pub trait <TRAIT>Write</TRAIT> {
    fn <METHOD>write</METHOD>(&mut <SELF_PARAMETER>self</SELF_PARAMETER>, <PARAMETER>buf</PARAMETER>: &[<PRIMITIVE_TYPE>u8</PRIMITIVE_TYPE>]) -> <ENUM>Result</ENUM><usize>;
}

struct <STRUCT>Object</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>> {
    <FIELD>flag</FIELD>: <ENUM>Flag</ENUM>,
    <FIELD>fields</FIELD>: <STRUCT>HashMap</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>, <PRIMITIVE_TYPE>u64</PRIMITIVE_TYPE>>
}

<KEYWORD>union</KEYWORD> <UNION>MyUnion</UNION> {
    <FIELD>f1</FIELD>: <PRIMITIVE_TYPE>u32</PRIMITIVE_TYPE>,
    <FIELD>f2</FIELD>: <PRIMITIVE_TYPE>f32</PRIMITIVE_TYPE>,
}

type <TYPE_ALIAS>RcObject</TYPE_ALIAS><<TYPE_PARAMETER>T</TYPE_PARAMETER>> = <STRUCT>Rc</STRUCT><<STRUCT>Object</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>>>;

impl<<TYPE_PARAMETER>T</TYPE_PARAMETER>> Write for <STRUCT>Object</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>> {
    fn <METHOD>write</METHOD>(&mut <SELF_PARAMETER>self</SELF_PARAMETER>, <PARAMETER>buf</PARAMETER>: &[<PRIMITIVE_TYPE>u8</PRIMITIVE_TYPE>]) -> <ENUM>Result</ENUM><usize> {
        let s = stuff::<FUNCTION_CALL>write_map</FUNCTION_CALL>(&self.<FIELD>fields</FIELD>, <PARAMETER>buf</PARAMETER>)<Q_OPERATOR>?</Q_OPERATOR>;
        <MACRO>info!</MACRO>("{} byte(s) written", s);
        <ENUM_VARIANT>Ok</ENUM_VARIANT>(s)
    }
}

impl<<TYPE_PARAMETER>T</TYPE_PARAMETER>> <TRAIT>Default</TRAIT> for <STRUCT>Object</STRUCT><<TYPE_PARAMETER>T</TYPE_PARAMETER>> {
    fn <ASSOC_FUNCTION>default</ASSOC_FUNCTION>() -> Self {
        <STRUCT>Object</STRUCT> { <FIELD>flag</FIELD>: <ENUM>Flag</ENUM>::<ENUM_VARIANT>Good</ENUM_VARIANT>, <FIELD>fields</FIELD>: <STRUCT>HashMap</STRUCT>::<ASSOC_FUNCTION_CALL>new</ASSOC_FUNCTION_CALL>() }
    }
}

/* Block comment */
fn <FUNCTION>main</FUNCTION>() {
    // A simple integer calculator:
    // `+` or `-` means add or subtract by 1
    // `*` or `/` means multiply or divide by 2
    <MODULE>stuff</MODULE>::<STRUCT>AppVersion</STRUCT>::<ASSOC_FUNCTION_CALL>print</ASSOC_FUNCTION_CALL>();

    let input = <ENUM>Option</ENUM>::<ENUM_VARIANT>None</ENUM_VARIANT>;
    let program = input.<METHOD_CALL>unwrap_or_else</METHOD_CALL>(|| "+ + * - /");
    let mut <MUT_BINDING>accumulator</MUT_BINDING> = 0;

    for token in program.<METHOD_CALL>chars</METHOD_CALL>() {
        match token {
            '+' => <MUT_BINDING>accumulator</MUT_BINDING> += 1,
            '-' => <MUT_BINDING>accumulator</MUT_BINDING> -= 1,
            '*' => <MUT_BINDING>accumulator</MUT_BINDING> *= 2,
            '/' => <MUT_BINDING>accumulator</MUT_BINDING> /= 2,
            _ => { /* ignore everything else */ }
        }
    }

    <MACRO>info!</MACRO>("The program \"{}\" calculates the value {}",
             program, <MUT_BINDING>accumulator</MUT_BINDING>);
}

/// Some documentation `with code`
/// # Heading
/// [Rust](https://www.rust-lang.org/)
<ATTRIBUTE>#[cfg(target_os=</ATTRIBUTE>"linux"<ATTRIBUTE>)]</ATTRIBUTE>
<KEYWORD_UNSAFE>unsafe</KEYWORD_UNSAFE> fn <FUNCTION>a_function</FUNCTION><<TYPE_PARAMETER>T</TYPE_PARAMETER>: <LIFETIME>'lifetime</LIFETIME>>(<MUT_PARAMETER>count</MUT_PARAMETER>: &mut i64) -> ! {
    <MUT_PARAMETER>count</MUT_PARAMETER> += 1;
    'label: loop {
        <MACRO>println!</MACRO>("Hello\x20W\u{f3}rld!\u{abcdef}");
    }
}

fn <FUNCTION>test</FUNCTION>() {
    <KEYWORD_UNSAFE>unsafe</KEYWORD_UNSAFE> {
        <UNSAFE_CODE>a_function</UNSAFE_CODE>(1);
    }
}
<|endoftext|>


<commit_before>#[link(name = "rusp",
       vers = "0.1",
       author = "Brendan Zabarauskas",
       url = "https://github.com/bjz/rusp")];

#[comment = "A minimal Lisp dialect for Rust."];
#[license = "ASL2"];
#[crate_type = "lib"];

// Inspired by Peter Norvig's [Lis.py](http://norvig.com/lispy.html)

use std::hashmap::*;

pub mod parser;
pub mod pprint;

///
/// Performs a recursive decent parse of the source string.
///
pub fn parse(src: &str) -> Result<~Value, parser::ParseFailure> {
    parser::Parser::new(src).parse()
}

/// Symbol identifier
pub type Ident = ~str;

/// Holds a typed value
#[deriving(Eq, Clone)]
pub enum Value {
    Bool(bool),
    Int(int),
    Float(float),
    Str(~str),
    List(~[~Value]),
    Symbol(Ident),
    Rust(RustFn),
    Lambda(~[Ident], ~Value),
}

/// Workaround for `deriving` not working for rust closures
pub struct RustFn(@fn(params: ~[Ident], env: &Rusp) -> Value);

impl Eq for RustFn {
    fn eq(&self, _: &RustFn) -> bool {
        fail!("Cannot test equality of Rust functions (yet?).");
    }
    fn ne(&self, other: &RustFn) -> bool { !self.eq(other) }
}

impl Clone for RustFn {
    fn clone(&self) -> RustFn {
        fail!("Cannot clone Rust functions (yet?)");
    }
}

#[deriving(Eq)]
pub struct Rusp {
    values: HashMap<Ident, Value>,
    outer: Option<@mut Rusp>,
}

pub type EvalResult = Result<Value, ~str>;

impl Rusp {
    /// Initializes an empty environment
    pub fn empty() -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: None,
        }
    }

    /// Initializes a new environment with the specified values
    pub fn new(outer: @mut Rusp) -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: Some(outer),
        }
    }

    /// Defines a new identifier/value pair in the environment
    pub fn define(&mut self, ident: Ident, val: Value) -> bool {
        self.values.insert(ident, val)
    }

    /// Attempts to find a value corresponding to the supplied identifier
    pub fn find(&self, ident: &Ident) -> Option<Value> {
        match self.values.find(ident) {
            Some(val) => Some(val.clone()),
            None => self.outer.chain(|env| env.find(ident)),
        }
    }

    /// Evaluates a Rusp expression in the environment
    pub fn eval(&mut self, value: &Value) -> EvalResult {
        match *value {
            Bool(b) => Ok(Bool(b)),
            Int(i) => Ok(Int(i)),
            Float(f) => Ok(Float(f)),
            Str(ref s) => Ok(Str(s.to_owned())),
            Symbol(ref id) => {
                match self.find(id) {
                    Some(v) => Ok(v),
                    None => Err(fmt!("The value of `%s` was not defined in this environment",
                                     id.to_str())),
                }
            }
            List(ref vals) => {
                match vals {
                    &[~Symbol(~"list"),..tl] => self.eval_list(tl),
                    &[~Symbol(~"if"),..tl] => self.eval_if(tl),
                    &[~Symbol(~"def"),..tl] => self.eval_def(tl),
                    &[~Symbol(~"do"),..tl] => self.eval_do(tl),
                    &[~Symbol(~"quote"),..tl] => self.eval_quote(tl),
                    &[~Symbol(~"fn"),..tl] => self.eval_fn(tl),
                    &[proc,..params] => fail!("Not yet implemented"),
                    _ => fail!("Not yet implemented"),
                }
            }
            Rust(_) => fail!("Not yet implemented"),
            Lambda(_,_) => fail!("Not yet implemented"),
        }
    }

    /// Return the arguments as a list
    fn eval_list(&mut self, vals: &[~Value]) -> EvalResult {
        let mut evaled = ~[];
        for vals.each |&val| {
            match self.eval(val) {
                Ok(v) => evaled.push(~v),
                Err(e) => return Err(e),
            }
        }
        Ok(List(evaled))
    }

    /// Conditional expression
    fn eval_if(&mut self, vals: &[~Value]) -> EvalResult {
        match vals {
            &[~pred, ~conseq, ~alt] => {
                do self.eval(*pred).chain |val| {
                    match val {
                        Bool(true) => self.eval(*conseq),
                        Bool(false) => self.eval(*alt),
                        _ => Err(fmt!("Expected boolean expression, found: %s", val.to_str())),
                    }
                }
            }
            _ => Err(~""), // TODO
        }
    }

    /// Define a new symbol in the environment
    fn eval_def(&mut self, vals: &[~Value]) -> EvalResult {
        match vals {
            &[~Symbol(ident), value] => {
                do self.eval(*value).chain |val| {
                    if self.define(ident.clone(), val) {
                        Ok(List(~[]))
                    } else {
                        Err(fmt!("`%s` was already defined in this environment.", ident.to_str()))
                    }
                }
            }
            _ => Err(~""), // TODO
        }
    }

    /// Evalue each expression in turn, returning the value of the last expression
    fn eval_do(&self, vals: &[~Value]) -> EvalResult {
        match vals {
            &[..fst, lst] => {
                for fst.each |val| {
                    match self.eval(val) {
                        Ok(List(ref l)) if l.is_empty() => (),
                        Ok(v) => return Err(fmt!("Expected unit expression, found: %s", v.to_str())),
                        Err(e) => return Err(e),
                    }
                }
                self.eval(lst)
            }
            _ => Err(~""), // TODO
        }
    }

    /// Return an expression without evaluating it
    fn eval_quote(&self, vals: &[~Value]) -> EvalResult {
        match vals {
            &[val,..tl] if tl.is_empty() => Ok((**val).clone()),
            _ => Err(~""), // TODO
        }
    }

    /// Evaluate to an anonymous function
    fn eval_fn(&self, vals: &[~Value]) -> EvalResult {
        match vals {
            &[~List(params), val] => {
                let mut idents = ~[];
                match *params {
                    List(ref symbols) => {
                        for symbols.each |symbol| {
                            match *symbol {
                                Symbol(ref ident) => idents.push(~ident.clone()),
                                _ => return Err(fmt!("Expected symbol identifier, found: \
                                                     %s", symbol.to_str())),
                            }
                        }
                    }
                };
                Ok(Lambda(idents, val.clone()))
            }
            _ => Err(~""), // TODO
        }
    }
}

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

    #[test]
    fn test_env() {
        let outer = Rusp::empty();
        outer.define(~"a", Int(0));
        outer.define(~"b", Float(1.0));
        outer.define(~"c", Str(~"hi"));

        let inner = Rusp::new(outer);
        inner.define(~"a", Int(3));
        inner.define(~"b", Int(4));

        assert_eq!(outer.find(&~"a"), Some(Int(0)));
        assert_eq!(outer.find(&~"b"), Some(Float(1.0)));
        assert_eq!(outer.find(&~"c"), Some(Str(~"hi")));
        assert_eq!(inner.find(&~"a"), Some(Int(3)));
        assert_eq!(inner.find(&~"b"), Some(Int(4)));
        assert_eq!(inner.find(&~"c"), Some(Str(~"hi")));
    }

    #[test]
    fn test_atoms() {
        assert_eq!(Rusp::empty().eval(&Int(1)).get(), Int(1));
        assert_eq!(Rusp::empty().eval(&Float(1.0)).get(), Float(1.0));
        assert_eq!(Rusp::empty().eval(&Str(~"hi")).get(), Str(~"hi"));
    }

    #[test]
    fn test_list() {
        assert_eq!(Rusp::empty().eval(&List(~[])).get(), List(~[]));
        assert_eq!(
            Rusp::empty().eval(&List(~[
                ~Symbol("list"),
                ~Str(~"hi"),
                ~List(~[~Symbol(~"quote"), ~Symbol(~"x")]),
                ~Int(1)
            ])).get(),
            List(~[
                ~Str(~"hi"),
                ~Symbol(~"x"),
                ~Int(1)
            ])
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(Rusp::empty().eval(
            &List(~[~Symbol(~"quote"), ~Symbol(~"x")])).get(),
            Symbol(~"x")
        );
    }

    #[test]
    fn test_symbol() {
        let env = Rusp::empty();
        env.define(~"a", Int(0));
        env.define(~"b", Float(1.0));
        env.define(~"c", Str(~"hi"));

        assert_eq!(env.eval(&Symbol(~"a")).get(), Int(0));
        assert_eq!(env.eval(&Symbol(~"b")).get(), Float(1.0));
        assert_eq!(env.eval(&Symbol(~"c")).get(), Str(~"hi"));
        assert!(env.eval(&Symbol(~"d")).is_err());
    }

    #[test]
    fn test_if() {
        fn not(test: Value) -> EvalResult {
            Rusp::empty().eval(&List(~[~Symbol(~"if"), ~test, ~Bool(false), ~Bool(true)]))
        }

        assert_eq!(not(Bool(true)).get(), Bool(false));
        assert_eq!(not(Bool(false)).get(), Bool(true));
        assert!(not(Str(~"hi")).is_err());
    }

    #[test]
    fn test_def() {
        let env = Rusp::empty();
        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"a"), ~Int(0)])).is_ok());
        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"b"), ~Float(1.0)])).is_ok());
        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"c"), ~Str(~"hi")])).is_ok());

        assert_eq!(env.find(&~"a"), Some(Int(0)));
        assert_eq!(env.find(&~"b"), Some(Float(1.0)));
        assert_eq!(env.find(&~"c"), Some(Str(~"hi")));

        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"c"), ~List(~[])])).is_err());
    }

    #[test]
    fn test_do() {
        assert_eq!(Rusp::empty().eval(&List(~[~Symbol(~"do"), ~Str(~"hi")])).get(), Str(~"hi"));
        assert_eq!(
            Rusp::empty().eval(&List(~[
                ~Symbol(~"do"),
                ~List(~[]),
                ~Do(~[~List(~[])]),
                ~Str(~"hi")
            ])).get(),
            Str(~"hi")
        );

        assert!(Rusp::empty().eval(&Do(~[~Str(~"hi"), ~List(~[])])).is_err());
    }

    #[test]
    fn test_apply() {

    }
}
<commit_msg>Fix up various pattern matching constructs<commit_after>#[link(name = "rusp",
       vers = "0.1",
       author = "Brendan Zabarauskas",
       url = "https://github.com/bjz/rusp")];

#[comment = "A minimal Lisp dialect for Rust."];
#[license = "ASL2"];
#[crate_type = "lib"];

// Inspired by Peter Norvig's [Lis.py](http://norvig.com/lispy.html)

use std::hashmap::*;

pub mod parser;
pub mod pprint;

///
/// Performs a recursive decent parse of the source string.
///
pub fn parse(src: &str) -> Result<~Value, parser::ParseFailure> {
    parser::Parser::new(src).parse()
}

/// Symbol identifier
pub type Ident = ~str;

/// Holds a typed value
#[deriving(Eq, Clone)]
pub enum Value {
    Bool(bool),
    Int(int),
    Float(float),
    Str(~str),
    List(~[~Value]),
    Symbol(Ident),
    Rust(RustFn),
    Lambda(~[Ident], ~Value),
}

/// Workaround for `deriving` not working for rust closures
pub struct RustFn(@fn(params: ~[Ident], env: &Rusp) -> Value);

impl Eq for RustFn {
    fn eq(&self, _: &RustFn) -> bool {
        fail!("Cannot test equality of Rust functions (yet?).");
    }
    fn ne(&self, other: &RustFn) -> bool { !self.eq(other) }
}

impl Clone for RustFn {
    fn clone(&self) -> RustFn {
        fail!("Cannot clone Rust functions (yet?)");
    }
}

#[deriving(Eq)]
pub struct Rusp {
    values: HashMap<Ident, Value>,
    outer: Option<@mut Rusp>,
}

pub type EvalResult = Result<Value, ~str>;

impl Rusp {
    /// Initializes an empty environment
    pub fn empty() -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: None,
        }
    }

    /// Initializes a new environment with the specified values
    pub fn new(outer: @mut Rusp) -> @mut Rusp {
        @mut Rusp {
            values: HashMap::new(),
            outer: Some(outer),
        }
    }

    /// Defines a new identifier/value pair in the environment
    pub fn define(&mut self, ident: Ident, val: Value) -> bool {
        self.values.insert(ident, val)
    }

    /// Attempts to find a value corresponding to the supplied identifier
    pub fn find(&self, ident: &Ident) -> Option<Value> {
        match self.values.find(ident) {
            Some(val) => Some(val.clone()),
            None => self.outer.chain(|env| env.find(ident)),
        }
    }

    /// Evaluates a Rusp expression in the environment
    pub fn eval(&mut self, value: &Value) -> EvalResult {
        match *value {
            Bool(b) => Ok(Bool(b)),
            Int(i) => Ok(Int(i)),
            Float(f) => Ok(Float(f)),
            Str(ref s) => Ok(Str(s.to_owned())),
            Symbol(ref id) => {
                match self.find(id) {
                    Some(v) => Ok(v),
                    None => Err(fmt!("The value of `%s` was not defined in this environment",
                                     id.to_str())),
                }
            }
            List(ref vals) => {
                match vals {
                    &[~Symbol(~"list"),..tl] => self.eval_list(tl),
                    &[~Symbol(~"if"),..tl] => self.eval_if(tl),
                    &[~Symbol(~"def"),..tl] => self.eval_def(tl),
                    &[~Symbol(~"do"),..tl] => self.eval_do(tl),
                    &[~Symbol(~"quote"),..tl] => self.eval_quote(tl),
                    &[~Symbol(~"fn"),..tl] => self.eval_fn(tl),
                    //&[proc,..params] => fail!("Not yet implemented"),
                    _ => fail!("Not yet implemented"),
                }
            }
            Rust(_) => fail!("Not yet implemented"),
            Lambda(_,_) => fail!("Not yet implemented"),
        }
    }

    /// Return the arguments as a list
    fn eval_list(&mut self, vals: &[~Value]) -> EvalResult {
        let mut evaled = ~[];
        for vals.each |&val| {
            match self.eval(val) {
                Ok(v) => evaled.push(~v),
                Err(e) => return Err(e),
            }
        }
        Ok(List(evaled))
    }

    /// Conditional expression
    fn eval_if(&mut self, vals: &[~Value]) -> EvalResult {
        match vals {
            [ref pred, ref conseq, ref alt] => {
                do self.eval(*pred).chain |val| {
                    match val {
                        Bool(true) => self.eval(*conseq),
                        Bool(false) => self.eval(*alt),
                        _ => Err(fmt!("Expected boolean expression, found: %s", val.to_str())),
                    }
                }
            }
            _ => Err(~""), // TODO
        }
    }

    /// Define a new symbol in the environment
    fn eval_def(&mut self, vals: &[~Value]) -> EvalResult {
        match vals {
            [~Symbol(ref ident), ref value] => {
                do self.eval(*value).chain |val| {
                    if self.define(ident.clone(), val) {
                        Ok(List(~[]))
                    } else {
                        Err(fmt!("`%s` was already defined in this environment.", ident.to_str()))
                    }
                }
            }
            _ => Err(~""), // TODO
        }
    }

    /// Evalue each expression in turn, returning the value of the last expression
    fn eval_do(&mut self, vals: &[~Value]) -> EvalResult {
        match vals {
            [..fst, lst] => {
                for fst.each |&val| {
                    match self.eval(val) {
                        Ok(List(ref l)) if l.is_empty() => (),
                        Ok(v) => return Err(fmt!("Expected unit expression, found: %s", v.to_str())),
                        Err(e) => return Err(e),
                    }
                }
                self.eval(lst)
            }
            _ => Err(~""), // TODO
        }
    }

    /// Return an expression without evaluating it
    fn eval_quote(&self, vals: &[~Value]) -> EvalResult {
        match vals {
            [ref val,..tl] if tl.is_empty() => Ok((**val).clone()),
            _ => Err(~""), // TODO
        }
    }

    /// Evaluate to an anonymous function
    fn eval_fn(&self, vals: &[~Value]) -> EvalResult {
        match vals {
            [~List(ref symbols), ref val] => {
                let mut idents = ~[];
                for symbols.each |&symbol| {
                    match *symbol {
                        Symbol(ref ident) => idents.push(ident.clone()),
                        _ => return Err(fmt!("Expected symbol identifier, found: \
                                             %s", symbol.to_str())),
                    }
                };
                Ok(Lambda(idents, val.clone()))
            }
            _ => Err(~""), // TODO
        }
    }
}

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

    #[test]
    fn test_env() {
        let outer = Rusp::empty();
        outer.define(~"a", Int(0));
        outer.define(~"b", Float(1.0));
        outer.define(~"c", Str(~"hi"));

        let inner = Rusp::new(outer);
        inner.define(~"a", Int(3));
        inner.define(~"b", Int(4));

        assert_eq!(outer.find(&~"a"), Some(Int(0)));
        assert_eq!(outer.find(&~"b"), Some(Float(1.0)));
        assert_eq!(outer.find(&~"c"), Some(Str(~"hi")));
        assert_eq!(inner.find(&~"a"), Some(Int(3)));
        assert_eq!(inner.find(&~"b"), Some(Int(4)));
        assert_eq!(inner.find(&~"c"), Some(Str(~"hi")));
    }

    #[test]
    fn test_atoms() {
        assert_eq!(Rusp::empty().eval(&Int(1)).get(), Int(1));
        assert_eq!(Rusp::empty().eval(&Float(1.0)).get(), Float(1.0));
        assert_eq!(Rusp::empty().eval(&Str(~"hi")).get(), Str(~"hi"));
    }

    #[test]
    fn test_list() {
        assert_eq!(Rusp::empty().eval(&List(~[])).get(), List(~[]));
        assert_eq!(
            Rusp::empty().eval(&List(~[
                ~Symbol("list"),
                ~Str(~"hi"),
                ~List(~[~Symbol(~"quote"), ~Symbol(~"x")]),
                ~Int(1)
            ])).get(),
            List(~[
                ~Str(~"hi"),
                ~Symbol(~"x"),
                ~Int(1)
            ])
        );
    }

    #[test]
    fn test_quote() {
        assert_eq!(Rusp::empty().eval(
            &List(~[~Symbol(~"quote"), ~Symbol(~"x")])).get(),
            Symbol(~"x")
        );
    }

    #[test]
    fn test_symbol() {
        let env = Rusp::empty();
        env.define(~"a", Int(0));
        env.define(~"b", Float(1.0));
        env.define(~"c", Str(~"hi"));

        assert_eq!(env.eval(&Symbol(~"a")).get(), Int(0));
        assert_eq!(env.eval(&Symbol(~"b")).get(), Float(1.0));
        assert_eq!(env.eval(&Symbol(~"c")).get(), Str(~"hi"));
        assert!(env.eval(&Symbol(~"d")).is_err());
    }

    #[test]
    fn test_if() {
        fn not(test: Value) -> EvalResult {
            Rusp::empty().eval(&List(~[~Symbol(~"if"), ~test, ~Bool(false), ~Bool(true)]))
        }

        assert_eq!(not(Bool(true)).get(), Bool(false));
        assert_eq!(not(Bool(false)).get(), Bool(true));
        assert!(not(Str(~"hi")).is_err());
    }

    #[test]
    fn test_def() {
        let env = Rusp::empty();
        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"a"), ~Int(0)])).is_ok());
        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"b"), ~Float(1.0)])).is_ok());
        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"c"), ~Str(~"hi")])).is_ok());

        assert_eq!(env.find(&~"a"), Some(Int(0)));
        assert_eq!(env.find(&~"b"), Some(Float(1.0)));
        assert_eq!(env.find(&~"c"), Some(Str(~"hi")));

        assert!(env.eval(&List(~[~Symbol(~"def"), Symbol(~"c"), ~List(~[])])).is_err());
    }

    #[test]
    fn test_do() {
        assert_eq!(Rusp::empty().eval(&List(~[~Symbol(~"do"), ~Str(~"hi")])).get(), Str(~"hi"));
        assert_eq!(
            Rusp::empty().eval(&List(~[
                ~Symbol(~"do"),
                ~List(~[]),
                ~Do(~[~List(~[])]),
                ~Str(~"hi")
            ])).get(),
            Str(~"hi")
        );

        assert!(Rusp::empty().eval(&Do(~[~Str(~"hi"), ~List(~[])])).is_err());
    }

    #[test]
    fn test_apply() {

    }
}
<|endoftext|>